From 1e8d30ee1d1740efd85b5e78ecd40e515434fc0f Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:01:59 +0200 Subject: [PATCH 01/15] Fix spelling mistake --- lib/utilities/stringUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utilities/stringUtils.js b/lib/utilities/stringUtils.js index cc302cbed4..9f22effee2 100644 --- a/lib/utilities/stringUtils.js +++ b/lib/utilities/stringUtils.js @@ -66,9 +66,9 @@ const snakeToPascal = (snake) => ucFirst(snakeToCamel(snake)); * Split the received string to an array of trimmed strings. * Boolean trick: https://michaeluloth.com/javascript-filter-boolean/ * @param {string} stringCollection String containing other strings withing split by seperator. - * @param {string} stringSeperator Used to seperate the stringCollection. + * @param {string} stringSeparator Used to seperate the stringCollection. */ -const splitStringToStringsTrimmed = (stringCollection, stringSeperator = ',') => stringCollection.split(stringSeperator) +const splitStringToStringsTrimmed = (stringCollection, stringSeparator = ',') => stringCollection.split(stringSeparator) .map((string) => string.trim()) .filter(Boolean); From ded1c122aeb3c57f8e1caffab2b1972e06a9c4d0 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:04:37 +0200 Subject: [PATCH 02/15] Update beam type dto to split and return array of values --- lib/domain/dtos/common/BeamTypeDto.js | 20 +++++++++++-------- lib/usecases/lhcFill/GetAllLhcFillsUseCase.js | 3 +-- .../lhcFill/GetAllLhcFillsUseCase.test.js | 6 +++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/domain/dtos/common/BeamTypeDto.js b/lib/domain/dtos/common/BeamTypeDto.js index dc2d401113..0f60d8df0a 100644 --- a/lib/domain/dtos/common/BeamTypeDto.js +++ b/lib/domain/dtos/common/BeamTypeDto.js @@ -12,12 +12,16 @@ */ const Joi = require('joi'); -const { validateBeamTypes, BEAM_TYPE_INVALID } = require('../../../utilities/beamTypeUtils'); +const { CustomJoi } = require('../CustomJoi.js'); +const { BEAM_TYPE_INVALID } = require('../../../utilities/beamTypeUtils'); -exports.BeamTypesDto = Joi.string() - .trim() - .custom(validateBeamTypes) - .messages({ - [BEAM_TYPE_INVALID]: '{{#message}}', - 'string.base': 'Beam type must be a string', - }); +exports.BeamTypesDto = CustomJoi.stringArray() + .items(Joi.string() + .trim() + .min(2) + .max(15) + .pattern(/^[A-Za-z0-9]+ ?- ?[A-Za-z0-9]+$/) + .messages({ + [BEAM_TYPE_INVALID]: '{{#message}}', + 'string.base': 'Beam type must be a string', + })); diff --git a/lib/usecases/lhcFill/GetAllLhcFillsUseCase.js b/lib/usecases/lhcFill/GetAllLhcFillsUseCase.js index f69ed2de34..8d9146dbd4 100644 --- a/lib/usecases/lhcFill/GetAllLhcFillsUseCase.js +++ b/lib/usecases/lhcFill/GetAllLhcFillsUseCase.js @@ -95,8 +95,7 @@ class GetAllLhcFillsUseCase { } if (beamTypes) { - const beamTypesArray = beamTypes.split(','); - queryBuilder.where('beamType').oneOf(beamTypesArray); + queryBuilder.where('beamType').oneOf(beamTypes); } if (schemeName) { diff --git a/test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js b/test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js index 8fbb5f2781..8060e95e71 100644 --- a/test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js +++ b/test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js @@ -278,7 +278,7 @@ module.exports = () => { }) it('should only contain specified beam type, {p-p}', async () => { - getAllLhcFillsDto.query = { filter: { beamTypes: 'p-p' } }; + getAllLhcFillsDto.query = { filter: { beamTypes: ['p-p'] } }; const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto) expect(lhcFills).to.be.an('array').and.lengthOf(2) @@ -290,7 +290,7 @@ module.exports = () => { it('should only contain specified beam types, {p-p, PROTON-PROTON, Pb-Pb}', async () => { const beamTypes = ['p-p', 'PROTON-PROTON', 'Pb-Pb'] - getAllLhcFillsDto.query = { filter: { beamTypes: beamTypes.join(',') } }; + getAllLhcFillsDto.query = { filter: { beamTypes: beamTypes } }; const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto) expect(lhcFills).to.be.an('array').and.lengthOf(4) @@ -302,7 +302,7 @@ module.exports = () => { it('should ignore unknown beam types, {p-p, Hello-world, Pb-Pb}', async () => { const beamTypes = ['p-p', 'Hello-world', 'Pb-Pb'] - getAllLhcFillsDto.query = { filter: { beamTypes: beamTypes.join(',') } }; + getAllLhcFillsDto.query = { filter: { beamTypes: beamTypes } }; const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto) expect(lhcFills).to.be.an('array').and.lengthOf(3) From 15fdc9c432344f27a1fed4e7eba1999c78a269b2 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:10:52 +0200 Subject: [PATCH 03/15] Remove unused utility and update tests --- lib/domain/dtos/common/BeamTypeDto.js | 3 +- lib/utilities/beamTypeUtils.js | 47 --------------------------- test/api/runs.test.js | 10 +++--- 3 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 lib/utilities/beamTypeUtils.js diff --git a/lib/domain/dtos/common/BeamTypeDto.js b/lib/domain/dtos/common/BeamTypeDto.js index 0f60d8df0a..8fd9e8cf57 100644 --- a/lib/domain/dtos/common/BeamTypeDto.js +++ b/lib/domain/dtos/common/BeamTypeDto.js @@ -13,7 +13,8 @@ const Joi = require('joi'); const { CustomJoi } = require('../CustomJoi.js'); -const { BEAM_TYPE_INVALID } = require('../../../utilities/beamTypeUtils'); + +const BEAM_TYPE_INVALID = 'beamType.invalid'; exports.BeamTypesDto = CustomJoi.stringArray() .items(Joi.string() diff --git a/lib/utilities/beamTypeUtils.js b/lib/utilities/beamTypeUtils.js deleted file mode 100644 index 55d8382325..0000000000 --- a/lib/utilities/beamTypeUtils.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE O2. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-o2.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -export const BEAM_TYPE_INVALID = 'beamType.invalid'; - -/** - * Validates beam types to have correct format - * Expects a string containing comma separated values. - * - * @param {string} value Beam types string to validate - * @param {*} helpers The helpers object - * @returns {string} The value if validation passes - */ -export const validateBeamTypes = (value, helpers) => { - const beamTypes = value.split(','); - - for (const type of beamTypes) { - // Max length accepted is 15 characters including spaces (e.g. "PROTON - PROTON") - if (type.length > 15) { - return helpers.error(BEAM_TYPE_INVALID, { - message: `Beam type exceeds max length of 15 characters: ${type}`, - }); - } - - /* - * Accepts combinations of letters and numbers separated by a hyphen, with optional spaces - * around the hyphen (e.g. "PROTON-PROTON", "PROTON - PROTON", "P1-P2") - */ - if (!/^[A-Za-z0-9]+ ?- ?[A-Za-z0-9]+$/.test(type)) { - return helpers.error(BEAM_TYPE_INVALID, { - message: `Invalid beam type format: ${type}`, - }); - } - } - - return value; -}; diff --git a/test/api/runs.test.js b/test/api/runs.test.js index c45a898634..a532128cff 100644 --- a/test/api/runs.test.js +++ b/test/api/runs.test.js @@ -161,7 +161,7 @@ module.exports = () => { }); it('should successfully filter with single beamType', async () => { - const beamType = 'p-p'; + const beamType = ['p-p']; const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamType}`); expect(response.status).to.equal(200); @@ -174,7 +174,7 @@ module.exports = () => { it('should successfully filter with multiple beamTypes', async () => { const beamTypes = ['p-p', 'Pb-Pb']; - const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes.join(',')}`); + const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes}`); expect(response.status).to.equal(200); const { data: runs } = response.body; @@ -185,15 +185,15 @@ module.exports = () => { }); it('should return 400 if beamTypes filter has the incorrect format', async () => { - const beamTypeString = 'DOES NOT EXIST'; - const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypeString}`); + const beamTypes = ['DOES NOT EXIST']; + const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes}`); expect(response.status).to.equal(400); const { errors: [error] } = response.body; expect(error.title).to.equal('Invalid Attribute'); - expect(error.detail).to.equal(`Invalid beam type format: ${beamTypeString}`); + expect(error.detail).to.equal(`Invalid beam type format: ${beamTypes}`); }); it('should return 400 if beamModes filter has the incorrect format', async () => { From b9334a34b46ef7979386e4a3e596febb4f530e5d Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:19:07 +0200 Subject: [PATCH 04/15] Fix API tests --- test/api/lhcFills.test.js | 2 +- test/api/runs.test.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/api/lhcFills.test.js b/test/api/lhcFills.test.js index dd84946b07..ff3fd3427f 100644 --- a/test/api/lhcFills.test.js +++ b/test/api/lhcFills.test.js @@ -771,7 +771,7 @@ module.exports = () => { const { errors: [error] } = res.body; expect(error.title).to.equal('Invalid Attribute'); - expect(error.detail).to.equal('"query.filter.beamTypes" is not allowed to be empty'); + expect(error.detail).to.equal('"query.filter.beamTypes[0]" is not allowed to be empty'); done(); }); }); diff --git a/test/api/runs.test.js b/test/api/runs.test.js index a532128cff..14a9d3c4e6 100644 --- a/test/api/runs.test.js +++ b/test/api/runs.test.js @@ -161,7 +161,7 @@ module.exports = () => { }); it('should successfully filter with single beamType', async () => { - const beamType = ['p-p']; + const beamType = 'p-p'; const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamType}`); expect(response.status).to.equal(200); @@ -173,7 +173,7 @@ module.exports = () => { }); it('should successfully filter with multiple beamTypes', async () => { - const beamTypes = ['p-p', 'Pb-Pb']; + const beamTypes = 'p-p,Pb-Pb'; const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes}`); expect(response.status).to.equal(200); @@ -185,7 +185,7 @@ module.exports = () => { }); it('should return 400 if beamTypes filter has the incorrect format', async () => { - const beamTypes = ['DOES NOT EXIST']; + const beamTypes = 'DOES NOT EXIST'; const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes}`); expect(response.status).to.equal(400); @@ -193,7 +193,7 @@ module.exports = () => { const { errors: [error] } = response.body; expect(error.title).to.equal('Invalid Attribute'); - expect(error.detail).to.equal(`Invalid beam type format: ${beamTypes}`); + expect(error.detail).to.equal(`"query.filter.beamTypes[0]" with value "${beamTypes}" fails to match the required pattern: /^[A-Za-z0-9]+ ?- ?[A-Za-z0-9]+$/`); }); it('should return 400 if beamModes filter has the incorrect format', async () => { From 67dd8a310dee1988bbb7c8c96bcfba08859e6361 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:24:02 +0200 Subject: [PATCH 05/15] Push also changes related to Runs as are using the BeamTypesDto --- lib/usecases/run/GetAllRunsUseCase.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/usecases/run/GetAllRunsUseCase.js b/lib/usecases/run/GetAllRunsUseCase.js index 813b5b66fe..24d7131023 100644 --- a/lib/usecases/run/GetAllRunsUseCase.js +++ b/lib/usecases/run/GetAllRunsUseCase.js @@ -121,10 +121,9 @@ class GetAllRunsUseCase { } if (beamTypes) { - const beamTypesList = splitStringToStringsTrimmed(beamTypes, SEARCH_ITEMS_SEPARATOR); filteringQueryBuilder.include({ association: 'lhcFill', - where: { beamType: { [Op.in]: beamTypesList } }, + where: { beamType: { [Op.in]: beamTypes } }, required: true, }); } From 33cf2876c760eda7daed27e79710c37494941856 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 13:32:38 +0200 Subject: [PATCH 06/15] FIx GetAllRuns tests --- test/lib/usecases/run/GetAllRunsUseCase.test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/lib/usecases/run/GetAllRunsUseCase.test.js b/test/lib/usecases/run/GetAllRunsUseCase.test.js index 7d4db513e5..bbaea9edaf 100644 --- a/test/lib/usecases/run/GetAllRunsUseCase.test.js +++ b/test/lib/usecases/run/GetAllRunsUseCase.test.js @@ -210,23 +210,22 @@ module.exports = () => { }); it('should successfully filter on beamTypes', async () => { - const singleBeamType = 'p-p'; - const multipleBeamTypes = 'p-p,Pb-Pb'; - const nonExistentBeamType = 'DOES-NOT-EXIST'; + const singleBeamType = ['p-p']; + const multipleBeamTypes = ['p-p', 'Pb-Pb']; + const nonExistentBeamType = ['DOES-NOT-EXIST']; getAllRunsDto.query = { filter: { beamTypes: singleBeamType }, page: { limit: 200 } }; { const { runs } = await new GetAllRunsUseCase().execute(getAllRunsDto); expect(runs).to.have.lengthOf.greaterThan(0); - expect(runs.every(({ lhcFill }) => lhcFill?.beamType === singleBeamType)).to.be.true; + expect(runs.every(({ lhcFill }) => singleBeamType.includes(lhcFill?.beamType))).to.be.true; } getAllRunsDto.query = { filter: { beamTypes: multipleBeamTypes }, page: { limit: 200 } }; { - const acceptedBeamTypes = multipleBeamTypes.split(','); const { runs } = await new GetAllRunsUseCase().execute(getAllRunsDto); expect(runs).to.have.lengthOf.greaterThan(0); - expect(runs.every(({ lhcFill }) => acceptedBeamTypes.includes(lhcFill?.beamType))).to.be.true; + expect(runs.every(({ lhcFill }) => multipleBeamTypes.includes(lhcFill?.beamType))).to.be.true; } getAllRunsDto.query = { filter: { beamTypes: nonExistentBeamType } }; From 17586546252ffb1e83093167f8f8344babc7e98e Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 14:10:35 +0200 Subject: [PATCH 07/15] Add endpoint for retrieving all unique pdpBeamTypes --- lib/server/controllers/runs.controller.js | 26 +++++++++++++++ lib/server/routers/runs.router.js | 5 +++ .../services/beam/getAllPdpBeamTypes.js | 32 +++++++++++++++++++ .../pdpBeamTypes/getAllPdpBeamTypes.test.js | 30 +++++++++++++++++ .../lib/server/services/pdpBeamTypes/index.js | 18 +++++++++++ 5 files changed, 111 insertions(+) create mode 100644 lib/server/services/beam/getAllPdpBeamTypes.js create mode 100644 test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js create mode 100644 test/lib/server/services/pdpBeamTypes/index.js diff --git a/lib/server/controllers/runs.controller.js b/lib/server/controllers/runs.controller.js index 916fdb7443..380899dff4 100644 --- a/lib/server/controllers/runs.controller.js +++ b/lib/server/controllers/runs.controller.js @@ -38,6 +38,7 @@ const { ApiConfig } = require('../../config/index.js'); const { DtoFactory } = require('../../domain/dtos/DtoFactory.js'); const { runService } = require('../services/run/RunService.js'); const { getAllBeamModes } = require('../services/beam/getAllBeamModes.js'); +const { getAllPdpBeamTypes } = require('../services/beam/getAllPdpBeamTypes.js'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError.js'); const { runToHttpView } = require('./runsToHttpView.js'); @@ -321,6 +322,30 @@ const listBeamModes = async (_request, response, _next) => { } }; +/** + * Retrieve a list of unique PDP beam types + * + * @param {Object} _request The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets + * an HTTP request. + * @param {NextFunction} _next The *next* object represents the next middleware function which is used to pass control to + * the next middleware function. + * @returns {undefined} + */ +const listPdpBeamTypes = async (_request, response, _next) => { + try { + const pdpBeamTypes = await getAllPdpBeamTypes(); + if (pdpBeamTypes?.length > 0) { + response.status(200).json({ data: pdpBeamTypes }); + } else { + response.status(204).json({ data: [] }); + } + } catch { + response.status(502).json({ errors: ['Unable to retrieve list of PDP beam types'] }); + } +}; + // eslint-disable-next-line jsdoc/require-param /** * Retrieve distinct combination of levels of alice L3 and dipole current rounded to kilo amperes @@ -348,6 +373,7 @@ module.exports = { getFlpsByRunNumberHandler, listReasonTypes, listBeamModes, + listPdpBeamTypes, listRuns, startRun, updateRunByRunNumber, diff --git a/lib/server/routers/runs.router.js b/lib/server/routers/runs.router.js index 59d453d9bc..b0db9f58bd 100644 --- a/lib/server/routers/runs.router.js +++ b/lib/server/routers/runs.router.js @@ -30,6 +30,11 @@ module.exports = { path: 'beamModes', controller: RunsController.listBeamModes, }, + { + method: 'get', + path: 'pdpBeamTypes', + controller: RunsController.listPdpBeamTypes, + }, { method: 'get', controller: [infoLoggerListenerMiddleware(FilterLogger), RunsController.listRuns], diff --git a/lib/server/services/beam/getAllPdpBeamTypes.js b/lib/server/services/beam/getAllPdpBeamTypes.js new file mode 100644 index 0000000000..4258ca3a72 --- /dev/null +++ b/lib/server/services/beam/getAllPdpBeamTypes.js @@ -0,0 +1,32 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +const { repositories: { RunRepository }, sequelize } = require('../../../database'); +const { Op } = require('sequelize'); + +/** + * Return the a list of unique PDP beam types which is built from the runs data + * + * @returns {Promise} Promise resolving with the list of unique PDP beam types + */ +exports.getAllPdpBeamTypes = async () => { + const pdpBeamTypes = await RunRepository.findAll({ + where: { + pdp_beam_type: { + [Op.ne]: null, + }, + }, + attributes: [[sequelize.fn('DISTINCT', sequelize.col('pdp_beam_type')), 'name']], + }); + return pdpBeamTypes; +}; diff --git a/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js new file mode 100644 index 0000000000..55c3435a2e --- /dev/null +++ b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js @@ -0,0 +1,30 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +const { expect } = require('chai'); +const { getAllPdpBeamTypes } = require('../../../../../lib/server/services/beam/getAllPdpBeamTypes.js'); + +module.exports = () => { + it('should successfully return the full list of not null PDP beam types from runs table', async () => { + const pdpBeamTypes = await getAllPdpBeamTypes(); + expect(pdpBeamTypes.map(({ dataValues: { name } }) => ({ name }))).to.deep.eq([ + { name: 'cosmic' }, + { name: 'technical' }, + { name: 'pp' }, + { name: 'PbPb' }, + { name: 'pO' }, + { name: 'OO' }, + { name: 'NeNe' }, + ]); + }); +}; diff --git a/test/lib/server/services/pdpBeamTypes/index.js b/test/lib/server/services/pdpBeamTypes/index.js new file mode 100644 index 0000000000..c2b0111eaf --- /dev/null +++ b/test/lib/server/services/pdpBeamTypes/index.js @@ -0,0 +1,18 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +const getAllPdpBeamTypes = require('./getAllPdpBeamTypes.test.js'); + +module.exports = () => { + describe('getAllPdpBeamTypes', getAllPdpBeamTypes); +}; From a1273f2f503ced5cf128a958ef518a5cb1558ff9 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 14:11:09 +0200 Subject: [PATCH 08/15] Add runs endpoint option to filter by pdpBeamType --- lib/domain/dtos/filters/RunFilterDto.js | 1 + lib/usecases/run/GetAllRunsUseCase.js | 5 ++ test/api/runs.test.js | 66 ++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/lib/domain/dtos/filters/RunFilterDto.js b/lib/domain/dtos/filters/RunFilterDto.js index 726ab4220f..39cc5a4418 100644 --- a/lib/domain/dtos/filters/RunFilterDto.js +++ b/lib/domain/dtos/filters/RunFilterDto.js @@ -41,6 +41,7 @@ exports.RunFilterDto = Joi.object({ 'Beam modes "{{#value}}" must contain only uppercase letters and single spaces between words.', })), beamTypes: BeamTypesDto, + pdpBeamTypes: CustomJoi.stringArray().items(Joi.string().trim().min(2).max(20)), runNumbers: Joi.string().trim().custom(validateRange).messages({ [RANGE_INVALID]: '{{#message}}', 'string.base': 'Run numbers must be comma-separated numbers or ranges (e.g. 12,15-18)', diff --git a/lib/usecases/run/GetAllRunsUseCase.js b/lib/usecases/run/GetAllRunsUseCase.js index 24d7131023..3014a8e548 100644 --- a/lib/usecases/run/GetAllRunsUseCase.js +++ b/lib/usecases/run/GetAllRunsUseCase.js @@ -85,6 +85,7 @@ class GetAllRunsUseCase { detectorsQcNotBadFraction, beamModes, beamTypes, + pdpBeamTypes, } = filter; if (runNumbers) { @@ -120,6 +121,10 @@ class GetAllRunsUseCase { filteringQueryBuilder.where('lhcBeamMode').oneOf(...beamModes); } + if (pdpBeamTypes) { + filteringQueryBuilder.where('pdpBeamType').oneOf(...pdpBeamTypes); + } + if (beamTypes) { filteringQueryBuilder.include({ association: 'lhcFill', diff --git a/test/api/runs.test.js b/test/api/runs.test.js index 14a9d3c4e6..8e42828f5a 100644 --- a/test/api/runs.test.js +++ b/test/api/runs.test.js @@ -196,6 +196,52 @@ module.exports = () => { expect(error.detail).to.equal(`"query.filter.beamTypes[0]" with value "${beamTypes}" fails to match the required pattern: /^[A-Za-z0-9]+ ?- ?[A-Za-z0-9]+$/`); }); + it('should successfully filter runs with pdpBeamType', async () => { + const pdpBeamType = 'pp'; + const response = await request(server).get(`/api/runs?filter[pdpBeamTypes]=${pdpBeamType}`); + + expect(response.status).to.equal(200); + const { data: runs } = response.body; + + expect(runs).to.be.an('array'); + expect(runs).to.have.lengthOf(6); + expect(runs.every(({ pdpBeamType: type }) => type === pdpBeamType)).to.be.true; + }); + + it('should successfully filter runs with multiple pdpBeamTypes', async () => { + const pdpBeamTypes = 'pp,PbPb'; + const response = await request(server).get(`/api/runs?filter[pdpBeamTypes]=${pdpBeamTypes}`); + + expect(response.status).to.equal(200); + const { data: runs } = response.body; + + expect(runs).to.be.an('array'); + expect(runs).to.have.lengthOf(10); + expect(runs.every(({ pdpBeamType: type }) => pdpBeamTypes.includes(type))).to.be.true; + }); + + it('should return 400 if pdpBeamTypes filter has the incorrect format', async () => { + const pdpBeamTypes = 'S'; // Too short + const response = await request(server).get(`/api/runs?filter[pdpBeamTypes]=${pdpBeamTypes}`); + + expect(response.status).to.equal(400); + + let { errors: [error] } = response.body; + + expect(error.title).to.equal('Invalid Attribute'); + expect(error.detail).to.equal(`"query.filter.pdpBeamTypes[0]" length must be at least 2 characters long`); + + const pdpBeamTypesLong = 'This is definitely not a pdp beam type'; // Too short + const responseLong = await request(server).get(`/api/runs?filter[pdpBeamTypes]=${pdpBeamTypesLong}`); + + expect(responseLong.status).to.equal(400); + + ({ errors: [error] } = responseLong.body); + + expect(error.title).to.equal('Invalid Attribute'); + expect(error.detail).to.equal(`"query.filter.pdpBeamTypes[0]" length must be less than or equal to 20 characters long`); + }); + it('should return 400 if beamModes filter has the incorrect format', async () => { const beamModeString = '*THERE\'S NON LETTERS IN HERE'; const response = await request(server).get(`/api/runs?filter[beamModes]=${beamModeString}`); @@ -727,7 +773,6 @@ module.exports = () => { }); }); - describe('GET /api/runs/beamModes', () => { it('should successfully return status 200 and list of beam modes', async () => { const { body } = await request(server) @@ -740,6 +785,25 @@ module.exports = () => { expect(body.data[0].name).to.equal('STABLE BEAMS'); }); }); + + describe('GET /api/runs/pdpBeamTypes', () => { + it('should successfully return status 200 and list of pdp beam types', async () => { + const { body } = await request(server) + .get('/api/runs/pdpBeamTypes') + .expect(200); + + expect(body.data).to.be.an('array'); + expect(body.data).to.have.lengthOf(5); + expect(body.data).to.deep.equal([ + { name: 'pp' }, + { name: 'PbPb' }, + { name: 'technical' }, + { name: 'cosmic' }, + { name: 'OO' }, + ]); + }); + }); + describe('GET /api/runs/reasonTypes', () => { it('should successfully return status 200 and list of reason types', async () => { const { body } = await request(server) From 60df82ebb38db786712e75ebd066d7d21ed5f430 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 14:20:42 +0200 Subject: [PATCH 09/15] Generalize BeamTypeFilterModel to accept a provider --- .../LhcFillsFilter/BeamTypeFilterModel.js | 20 ++++--------- .../beamTypes/pdpBeamTypesProvider.js | 30 +++++++++++++++++++ .../Overview/LhcFillsOverviewModel.js | 3 +- .../views/Runs/Overview/RunsOverviewModel.js | 5 +++- 4 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 lib/public/services/beamTypes/pdpBeamTypesProvider.js diff --git a/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js b/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js index fc0964da04..0341616057 100644 --- a/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js +++ b/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js @@ -11,26 +11,18 @@ * or submit itself to any jurisdiction. */ -import { beamTypesProvider } from '../../../services/beamTypes/beamTypesProvider.js'; -import { SelectionModel } from '../../common/selection/SelectionModel.js'; +import { ObservableBasedSelectionDropdownModel } from '../../detector/ObservableBasedSelectionDropdownModel.js'; /** * Beam type filter model */ -export class BeamTypeFilterModel extends SelectionModel { +export class BeamTypeFilterModel extends ObservableBasedSelectionDropdownModel { /** * Constructor + * + * @param {ObservableData>} beamTypes$ observable remote data of objects representing beam types */ - constructor() { - super({}); - - beamTypesProvider.items$.observe(() => { - beamTypesProvider.items$.getCurrent().apply({ - Success: (types) => { - const beamTypes = types.map((type) => ({ value: type.beam_type })); - this.setAvailableOptions(beamTypes); - }, - }); - }); + constructor(beamTypes$) { + super(beamTypes$, ({ name, beam_type }) => ({ value: name ?? beam_type })); } } diff --git a/lib/public/services/beamTypes/pdpBeamTypesProvider.js b/lib/public/services/beamTypes/pdpBeamTypesProvider.js new file mode 100644 index 0000000000..fd99ad92a3 --- /dev/null +++ b/lib/public/services/beamTypes/pdpBeamTypesProvider.js @@ -0,0 +1,30 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { getRemoteData } from '../../utilities/fetch/getRemoteData.js'; +import { RemoteDataProvider } from '../RemoteDataProvider.js'; + +/** + * Service class to fetch beams types from the backend + */ +export class PdpBeamTypesProvider extends RemoteDataProvider { + /** + * @inheritDoc + */ + async getRemoteData() { + const { data } = await getRemoteData('/api/runs/pdpBeamTypes'); + return data; + } +} + +export const pdpBeamTypesProvider = new PdpBeamTypesProvider(); diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index 3e73c2fa0f..4ab9ed78d4 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -18,6 +18,7 @@ import { TextComparisonFilterModel } from '../../../components/Filters/common/fi import { TimeRangeFilterModel } from '../../../components/Filters/RunsFilter/TimeRangeFilter.js'; import { ToggleFilterModel } from '../../../components/Filters/common/filters/ToggleFilterModel.js'; import { FilterableOverviewPageModel } from '../../../models/FilterableOverviewPageModel.js'; +import { beamTypesProvider } from '../../../services/beamTypes/beamTypesProvider.js'; /** * Model for the LHC fills overview page @@ -43,7 +44,7 @@ export class LhcFillsOverviewModel extends FilterableOverviewPageModel { hasStableBeams: new ToggleFilterModel(stableBeamsOnly, true), stableBeamsStart: new TimeRangeFilterModel(), stableBeamsEnd: new TimeRangeFilterModel(), - beamTypes: new BeamTypeFilterModel(), + beamTypes: new BeamTypeFilterModel(beamTypesProvider.items$), schemeName: new RawTextFilterModel(), }, ); diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index af658e36ad..42dc413f8a 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -32,6 +32,8 @@ import { DataExportModel } from '../../../models/DataExportModel.js'; import { runsActiveColumns as dataExportConfiguration } from '../ActiveColumns/runsActiveColumns.js'; import { BeamModeFilterModel } from '../../../components/Filters/RunsFilter/BeamModeFilterModel.js'; import { beamModesProvider } from '../../../services/beamModes/beamModesProvider.js'; +import { pdpBeamTypesProvider } from '../../../services/beamTypes/pdpBeamTypesProvider.js'; +import { beamTypesProvider } from '../../../services/beamTypes/beamTypesProvider.js'; import { RadioButtonFilterModel } from '../../../components/Filters/common/RadioButtonFilterModel.js'; import { SelectionModel } from '../../../components/common/selection/SelectionModel.js'; import { TRIGGER_VALUES } from '../../../domain/enums/TriggerValue.js'; @@ -96,7 +98,8 @@ export class RunsOverviewModel extends FilterableOverviewPageModel { dcs: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }), - beamTypes: new BeamTypeFilterModel(), + beamTypes: new BeamTypeFilterModel(beamTypesProvider.items$), + pdpBeamTypes: new BeamTypeFilterModel(pdpBeamTypesProvider.items$), }, ); From 34ad98673dd024131d85bbbd440183551ef05b77 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 14:20:50 +0200 Subject: [PATCH 10/15] Add widget for filtering on runs by pdp beam type --- .../views/Runs/ActiveColumns/runsActiveColumns.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index f121d791ad..46dd31bfed 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -44,7 +44,6 @@ import { numericalComparisonFilter } from '../../../components/Filters/common/fi import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js'; import radioButtonFilter from '../../../components/Filters/common/filters/radioButtonFilter.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; -import { beamTypeFilter } from '../../../components/Filters/LhcFillsFilter/beamTypeFilter.js'; /** * List of active columns for a generic runs table @@ -588,11 +587,18 @@ export const runsActiveColumns = { pdpBeamType: { name: 'PDP Beam Type', visible: false, + filter: (runsOverviewModel) => selectionDropdown( + runsOverviewModel.filteringModel.get('pdpBeamTypes'), + { selectorPrefix: 'pdp-beam-types' }, + ), }, beamType: { name: 'Beam Type', visible: false, - filter: (runsOverviewModel) => beamTypeFilter(runsOverviewModel.filteringModel.get('beamTypes')), + filter: (runsOverviewModel) => selectionDropdown( + runsOverviewModel.filteringModel.get('beamTypes'), + { selectorPrefix: 'beam-types' }, + ), }, readoutCfgUri: { name: 'Readout Config URI', From 97eacb4ea460ea9bc0ebd74a6e67ac610e558617 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Fri, 31 Jul 2026 14:38:39 +0200 Subject: [PATCH 11/15] Fix tests on overview lhc fills --- .../Filters/LhcFillsFilter/beamTypeFilter.js | 22 ------------------- .../ActiveColumns/lhcFillsActiveColumns.js | 7 ++++-- test/public/lhcFills/overview.test.js | 5 +++-- 3 files changed, 8 insertions(+), 26 deletions(-) delete mode 100644 lib/public/components/Filters/LhcFillsFilter/beamTypeFilter.js diff --git a/lib/public/components/Filters/LhcFillsFilter/beamTypeFilter.js b/lib/public/components/Filters/LhcFillsFilter/beamTypeFilter.js deleted file mode 100644 index 83f1487922..0000000000 --- a/lib/public/components/Filters/LhcFillsFilter/beamTypeFilter.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE O2. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-o2.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { checkboxes } from '../common/filters/checkboxFilter.js'; - -/** - * Renders a list of checkboxes that lets the user look for beam types - * - * @param {BeamTypeFilterModel} beamTypeFilterModel beamTypeFilterModel - * @return {Component} the filter - */ -export const beamTypeFilter = (beamTypeFilterModel) => checkboxes(beamTypeFilterModel, { selector: 'beam-types' }); diff --git a/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js b/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js index be4311d7e4..afc49d80f2 100644 --- a/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js +++ b/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js @@ -21,11 +21,11 @@ import { formatRunsList } from '../../Runs/format/formatRunsList.js'; import { formatLhcFillsTimeLoss } from '../format/formatLhcFillsTimeLoss.js'; import { buttonLinkWithDropdown } from '../../../components/common/selection/infoLoggerButtonGroup/buttonLinkWithDropdown.js'; import { infologgerLinksComponents } from '../../../components/common/externalLinks/infologgerLinksComponents.js'; +import { selectionDropdown } from '../../../components/common/selection/dropdown/selectionDropdown.js'; import { formatBeamType } from '../../../utilities/formatting/formatBeamType.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; import { durationFilter } from '../../../components/Filters/LhcFillsFilter/durationFilter.js'; -import { beamTypeFilter } from '../../../components/Filters/LhcFillsFilter/beamTypeFilter.js'; import { timeRangeFilter } from '../../../components/Filters/common/filters/timeRangeFilter.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; @@ -187,7 +187,10 @@ export const lhcFillsActiveColumns = { visible: true, size: 'w-8', format: (value) => formatBeamType(value), - filter: (lhcFillModel) => beamTypeFilter(lhcFillModel.filteringModel.get('beamTypes')), + filter: (lhcFillModel) => selectionDropdown( + lhcFillModel.filteringModel.get('beamTypes'), + { selectorPrefix: 'beam-types' }, + ), }, collidingBunches: { name: 'Colliding bunches', diff --git a/test/public/lhcFills/overview.test.js b/test/public/lhcFills/overview.test.js index e5acd2c10e..dc083605c2 100644 --- a/test/public/lhcFills/overview.test.js +++ b/test/public/lhcFills/overview.test.js @@ -349,13 +349,14 @@ module.exports = () => { }); it('should successfully apply beam types filter', async () => { - const filterBeamTypeP_Pb = '#beam-types-checkbox-p-Pb'; - const filterBeamTypePb_Pb = '#beam-types-checkbox-Pb-Pb'; + const filterBeamTypeP_Pb = '#beam-types-dropdown-option-p-Pb'; + const filterBeamTypePb_Pb = '#beam-types-dropdown-option-Pb-Pb'; await goToPage(page, 'lhc-fill-overview'); await waitForTableLength(page, 5); await openFilteringPanel(page); + await pressElement(page, '.beamType-filter .dropdown-trigger', true); await pressElement(page, filterBeamTypeP_Pb); await waitForTableLength(page, 1); From 8cfafff559c9caf76710a7a5e880ad1817e76ef4 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Sat, 1 Aug 2026 12:50:11 +0200 Subject: [PATCH 12/15] Provide more details on beam type format --- lib/domain/dtos/common/BeamTypeDto.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/domain/dtos/common/BeamTypeDto.js b/lib/domain/dtos/common/BeamTypeDto.js index 8fd9e8cf57..b2994bd501 100644 --- a/lib/domain/dtos/common/BeamTypeDto.js +++ b/lib/domain/dtos/common/BeamTypeDto.js @@ -14,8 +14,23 @@ const Joi = require('joi'); const { CustomJoi } = require('../CustomJoi.js'); -const BEAM_TYPE_INVALID = 'beamType.invalid'; - +/** + * @typedef {string[]} BeamTypesDto + * @description An array of beam types, each represented as a string. + * Each beam type must be a string with a minimum length of 2 characters and a maximum length of 15 characters. + * The string must match patterns such as "PROTON - PROTON", "NE10 - NE10", where the two parts are separated by a hyphen and optional spaces. + * + * RUN3 has the following beam types: + * "PROTON - PROTON" + * "NE10 - NE10" + * "O8 - O8" + * "PB82 - PB82" + * "PROTON - O8" + * "PROTON - PROTON" + * + * @example + * const beamTypes = ["PROTON - PROTON", "NE10 - NE10"]; + */ exports.BeamTypesDto = CustomJoi.stringArray() .items(Joi.string() .trim() @@ -23,6 +38,8 @@ exports.BeamTypesDto = CustomJoi.stringArray() .max(15) .pattern(/^[A-Za-z0-9]+ ?- ?[A-Za-z0-9]+$/) .messages({ - [BEAM_TYPE_INVALID]: '{{#message}}', 'string.base': 'Beam type must be a string', + 'string.min': 'Beam type must be at least 2 characters long', + 'string.max': 'Beam type must be at most 15 characters long', + 'string.pattern.base': 'Beam type must look like "PROTON - PROTON", "NE10 - NE10", etc.', })); From fe1c5616aa4e394ac5184fe2b2c7480c9fe45624 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Tue, 4 Aug 2026 16:24:04 +0200 Subject: [PATCH 13/15] Add tooltip for beam type filters in runs-overview --- lib/public/views/Runs/ActiveColumns/runsActiveColumns.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 46dd31bfed..a779c1286d 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -591,6 +591,7 @@ export const runsActiveColumns = { runsOverviewModel.filteringModel.get('pdpBeamTypes'), { selectorPrefix: 'pdp-beam-types' }, ), + filterTooltip: 'As set by user in ECS GUI during an environment deployment.', }, beamType: { name: 'Beam Type', @@ -599,6 +600,7 @@ export const runsActiveColumns = { runsOverviewModel.filteringModel.get('beamTypes'), { selectorPrefix: 'beam-types' }, ), + filterTooltip: 'As sent by LHC to Bookkeeping-LHC Plugin.', }, readoutCfgUri: { name: 'Readout Config URI', From ddc50d26daa6fa756f520dc4b04c88f2bf8c1e90 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Wed, 5 Aug 2026 10:23:17 +0200 Subject: [PATCH 14/15] Mapped pdp beam type to beam_type rather than name --- .../Filters/LhcFillsFilter/BeamTypeFilterModel.js | 2 +- lib/server/services/beam/getAllPdpBeamTypes.js | 2 +- .../services/pdpBeamTypes/getAllPdpBeamTypes.test.js | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js b/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js index 0341616057..a0894367c4 100644 --- a/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js +++ b/lib/public/components/Filters/LhcFillsFilter/BeamTypeFilterModel.js @@ -23,6 +23,6 @@ export class BeamTypeFilterModel extends ObservableBasedSelectionDropdownModel { * @param {ObservableData>} beamTypes$ observable remote data of objects representing beam types */ constructor(beamTypes$) { - super(beamTypes$, ({ name, beam_type }) => ({ value: name ?? beam_type })); + super(beamTypes$, ({ beam_type }) => ({ value: beam_type })); } } diff --git a/lib/server/services/beam/getAllPdpBeamTypes.js b/lib/server/services/beam/getAllPdpBeamTypes.js index 4258ca3a72..77de29f352 100644 --- a/lib/server/services/beam/getAllPdpBeamTypes.js +++ b/lib/server/services/beam/getAllPdpBeamTypes.js @@ -26,7 +26,7 @@ exports.getAllPdpBeamTypes = async () => { [Op.ne]: null, }, }, - attributes: [[sequelize.fn('DISTINCT', sequelize.col('pdp_beam_type')), 'name']], + attributes: [[sequelize.fn('DISTINCT', sequelize.col('pdp_beam_type')), 'beam_type']], }); return pdpBeamTypes; }; diff --git a/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js index 54ca7ba992..ddff777ccd 100644 --- a/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js +++ b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js @@ -18,11 +18,11 @@ module.exports = () => { it('should successfully return the full list of not null PDP beam types from runs table', async () => { const pdpBeamTypes = await getAllPdpBeamTypes(); expect(pdpBeamTypes.map(({ dataValues: { name } }) => ({ name }))).to.deep.eq([ - { name: 'pp' }, - { name: 'PbPb' }, - { name: 'technical' }, - { name: 'cosmic' }, - { name: 'OO' }, + { beam_type: 'pp' }, + { beam_type: 'PbPb' }, + { beam_type: 'technical' }, + { beam_type: 'cosmic' }, + { beam_type: 'OO' }, ]); }); }; From ceac6d305926df7674fcf9e5339a2adada10efa6 Mon Sep 17 00:00:00 2001 From: George Raduta Date: Wed, 5 Aug 2026 10:30:10 +0200 Subject: [PATCH 15/15] Update tests following change of key name --- test/api/runs.test.js | 10 +++++----- .../services/pdpBeamTypes/getAllPdpBeamTypes.test.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/api/runs.test.js b/test/api/runs.test.js index 6a67b0931d..4ada026ce5 100644 --- a/test/api/runs.test.js +++ b/test/api/runs.test.js @@ -805,11 +805,11 @@ module.exports = () => { expect(body.data).to.be.an('array'); expect(body.data).to.have.lengthOf(5); expect(body.data).to.deep.equal([ - { name: 'pp' }, - { name: 'PbPb' }, - { name: 'technical' }, - { name: 'cosmic' }, - { name: 'OO' }, + { beam_type: 'pp' }, + { beam_type: 'PbPb' }, + { beam_type: 'technical' }, + { beam_type: 'cosmic' }, + { beam_type: 'OO' }, ]); }); }); diff --git a/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js index ddff777ccd..05732b7072 100644 --- a/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js +++ b/test/lib/server/services/pdpBeamTypes/getAllPdpBeamTypes.test.js @@ -17,7 +17,7 @@ const { getAllPdpBeamTypes } = require('../../../../../lib/server/services/beam module.exports = () => { it('should successfully return the full list of not null PDP beam types from runs table', async () => { const pdpBeamTypes = await getAllPdpBeamTypes(); - expect(pdpBeamTypes.map(({ dataValues: { name } }) => ({ name }))).to.deep.eq([ + expect(pdpBeamTypes.map(({ dataValues: { beam_type } }) => ({ beam_type }))).to.deep.eq([ { beam_type: 'pp' }, { beam_type: 'PbPb' }, { beam_type: 'technical' },