diff --git a/lib/server/api-docs/API.md b/lib/server/api-docs/API.md index 7dd06411a7..70a34d614f 100644 --- a/lib/server/api-docs/API.md +++ b/lib/server/api-docs/API.md @@ -379,3 +379,60 @@ Responses: ```bash curl -g "$BOOKKEEPING_URL/api/shift-data?shiftType=ECS&token=$TOKEN" ``` + +## Simulation passes + +Monte Carlo simulation productions. + +### `GET /simulationPasses` - List simulation passes + +Operation id: `listSimulationPasses` + +Returns a page of simulation passes with their statistics (number of runs, data passes and logs). +Default order is by name, descending. + +List filters are repeated query parameters: `filter[ids][]=1&filter[ids][]=2`. A single value without `[]` is rejected. Filters are combined +with AND. + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `filter[ids][]` | query | integer | no | Keep the simulation passes with one of these ids. Repeat the parameter for several values. | +| `filter[names][]` | query | string | no | Keep the simulation passes with one of these exact names. Repeat the parameter for several values. | +| `filter[lhcPeriodIds][]` | query | integer | no | Keep the simulation passes used by data passes of one of these LHC periods. Repeat the parameter for several values. | +| `filter[dataPassIds][]` | query | integer | no | Keep the simulation passes anchored to one of these data passes. Repeat the parameter for several values. | +| `sort[id]` | query | string | no | Sort by id. | +| `sort[name]` | query | string | no | Sort by name (the default is `desc`). | +| `sort[pwg]` | query | string | no | Sort by physics working group. | +| `sort[jiraId]` | query | string | no | Sort by Jira ticket. | +| `sort[requestedEventsCount]` | query | string | no | Sort by number of requested events. | +| `sort[generatedEventsCount]` | query | string | no | Sort by number of generated events. | +| `sort[outputSize]` | query | string | no | Sort by output size. | +| `page[limit]` | query | integer | no | Maximum number of items to return. LLMs and agents must use 100 or less. | +| `page[offset]` | query | integer | no | Number of items to skip. | + +Responses: + +- `200`: A page of simulation passes. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. + +```bash +curl -g "$BOOKKEEPING_URL/api/simulationPasses?page[limit]=100&token=$TOKEN" +``` + +### `GET /simulationPasses/{id}` - Get a simulation pass by id + +Operation id: `getSimulationPass` + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `id` | path | integer | yes | Simulation pass identifier. | + +Responses: + +- `200`: The simulation pass. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. +- `404`: No simulation pass has this id. + +```bash +curl -g "$BOOKKEEPING_URL/api/simulationPasses/1?token=$TOKEN" +``` diff --git a/lib/server/api-docs/index.html b/lib/server/api-docs/index.html index 8caf3ba071..cb869dc1c8 100644 --- a/lib/server/api-docs/index.html +++ b/lib/server/api-docs/index.html @@ -608,6 +608,157 @@

Shifts

curl -g "$BOOKKEEPING_URL/api/shift-data?shiftType=ECS&token=$TOKEN"
 
+

Simulation passes

+

Monte Carlo simulation productions.

+
+

GET /simulationPasses - List simulation passes

+ +

Operation id: listSimulationPasses

+

Returns a page of simulation passes with their statistics (number of runs, data passes and logs). +Default order is by name, descending.

+

List filters are repeated query parameters: filter[ids][]=1&filter[ids][]=2. A single value without [] is rejected. Filters are combined +with AND.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
filter[ids][]queryintegernoKeep the simulation passes with one of these ids. Repeat the parameter for several values.
filter[names][]querystringnoKeep the simulation passes with one of these exact names. Repeat the parameter for several values.
filter[lhcPeriodIds][]queryintegernoKeep the simulation passes used by data passes of one of these LHC periods. Repeat the parameter for several values.
filter[dataPassIds][]queryintegernoKeep the simulation passes anchored to one of these data passes. Repeat the parameter for several values.
sort[id]querystringnoSort by id.
sort[name]querystringnoSort by name (the default is desc).
sort[pwg]querystringnoSort by physics working group.
sort[jiraId]querystringnoSort by Jira ticket.
sort[requestedEventsCount]querystringnoSort by number of requested events.
sort[generatedEventsCount]querystringnoSort by number of generated events.
sort[outputSize]querystringnoSort by output size.
page[limit]queryintegernoMaximum number of items to return. LLMs and agents must use 100 or less.
page[offset]queryintegernoNumber of items to skip.
+

Responses:

+
    +
  • 200: A page of simulation passes.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
+
curl -g "$BOOKKEEPING_URL/api/simulationPasses?page[limit]=100&token=$TOKEN"
+
+
+
+

GET /simulationPasses/{id} - Get a simulation pass by id

+ +

Operation id: getSimulationPass

+ + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
idpathintegeryesSimulation pass identifier.
+

Responses:

+
    +
  • 200: The simulation pass.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
  • 404: No simulation pass has this id.
  • +
+
curl -g "$BOOKKEEPING_URL/api/simulationPasses/1?token=$TOKEN"
+
+
Generated from openapi.yaml by npm run docs:api · ALICE Bookkeeping · internal use only
diff --git a/lib/server/api-docs/openapi.yaml b/lib/server/api-docs/openapi.yaml index 605499ea69..4a7fc919b2 100644 --- a/lib/server/api-docs/openapi.yaml +++ b/lib/server/api-docs/openapi.yaml @@ -49,6 +49,8 @@ tags: description: Counters of the central trigger processor, per run and trigger class. - name: Shifts description: Data used to fill end-of-shift reports. + - name: Simulation passes + description: Monte Carlo simulation productions. paths: /tags: @@ -427,6 +429,99 @@ paths: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } + /simulationPasses: + get: + tags: [Simulation passes] + operationId: listSimulationPasses + summary: List simulation passes + description: | + Returns a page of simulation passes with their statistics (number of runs, data passes and logs). + Default order is by name, descending. + + List filters are repeated query parameters: `filter[ids][]=1&filter[ids][]=2`. A single value without `[]` is rejected. Filters are combined + with AND. + parameters: + - name: filter[ids][] + in: query + description: Keep the simulation passes with one of these ids. Repeat the parameter for several values. + schema: { type: integer } + - name: filter[names][] + in: query + description: Keep the simulation passes with one of these exact names. Repeat the parameter for several values. + schema: { type: string, examples: [LHC23k6b] } + - name: filter[lhcPeriodIds][] + in: query + description: Keep the simulation passes used by data passes of one of these LHC periods. Repeat the parameter for several values. + schema: { type: integer } + - name: filter[dataPassIds][] + in: query + description: Keep the simulation passes anchored to one of these data passes. Repeat the parameter for several values. + schema: { type: integer } + - name: sort[id] + in: query + description: Sort by id. + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[name] + in: query + description: Sort by name (the default is `desc`). + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[pwg] + in: query + description: Sort by physics working group. + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[jiraId] + in: query + description: Sort by Jira ticket. + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[requestedEventsCount] + in: query + description: Sort by number of requested events. + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[generatedEventsCount] + in: query + description: Sort by number of generated events. + schema: { $ref: '#/components/schemas/SortOrder' } + - name: sort[outputSize] + in: query + description: Sort by output size. + schema: { $ref: '#/components/schemas/SortOrder' } + - $ref: '#/components/parameters/PageLimit' + - $ref: '#/components/parameters/PageOffset' + responses: + '200': + description: A page of simulation passes. + content: + application/json: + schema: { $ref: '#/components/schemas/SimulationPassListResponse' } + '400': { $ref: '#/components/responses/ValidationError' } + + /simulationPasses/{id}: + get: + tags: [Simulation passes] + operationId: getSimulationPass + summary: Get a simulation pass by id + parameters: + - name: id + in: path + required: true + description: Simulation pass identifier. + schema: { type: integer, minimum: 1 } + responses: + '200': + description: The simulation pass. + content: + application/json: + schema: { $ref: '#/components/schemas/SimulationPassResponse' } + '400': { $ref: '#/components/responses/ValidationError' } + '404': + description: No simulation pass has this id. + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + errors: + - { status: 404, title: Not found, detail: Simulation pass with this id (99999) could not be found } + components: securitySchemes: jwtQuery: @@ -444,6 +539,17 @@ components: schema: { type: integer, minimum: 1 } + PageLimit: + name: page[limit] + in: query + description: Maximum number of items to return. LLMs and agents must use 100 or less. + schema: { type: integer, minimum: 1, examples: [100] } + PageOffset: + name: page[offset] + in: query + description: Number of items to skip. + schema: { type: integer, minimum: 0, default: 0 } + responses: ValidationError: description: | @@ -669,3 +775,47 @@ components: required: [data] properties: data: { $ref: '#/components/schemas/ShiftData' } + PageMeta: + type: object + description: Pagination information of a list response. + properties: + page: + type: object + properties: + pageCount: { type: integer, description: 'Number of pages for the requested `page[limit]`.' } + totalCount: { type: integer, description: 'Number of items matching the request, all pages included.' } + SortOrder: + type: string + enum: [asc, desc, ASC, DESC] + SimulationPass: + type: object + required: [id, name] + properties: + id: { type: integer } + name: { type: string, examples: [LHC23k6b] } + jiraId: { type: [string, 'null'], description: Jira ticket of the simulation request., examples: [SIMTICKET-2] } + description: { type: [string, 'null'] } + pwg: { type: [string, 'null'], description: Physics working group., examples: [PWGX1] } + requestedEventsCount: { type: [integer, 'null'] } + generatedEventsCount: { type: [integer, 'null'] } + outputSize: { type: [integer, 'null'], description: Size of the output in bytes. } + runsCount: { type: integer, description: Number of runs of the simulation pass. } + dataPassesCount: { type: integer, description: Number of data passes anchored to the simulation pass. } + logsCount: { type: integer, description: Number of logs linked to the simulation pass. } + pdpBeamTypes: + type: array + description: PDP beam types of the runs. + items: { type: string, examples: [PbPb] } + SimulationPassResponse: + type: object + required: [data] + properties: + data: { $ref: '#/components/schemas/SimulationPass' } + SimulationPassListResponse: + type: object + required: [data, meta] + properties: + data: + type: array + items: { $ref: '#/components/schemas/SimulationPass' } + meta: { $ref: '#/components/schemas/PageMeta' } diff --git a/lib/server/controllers/simulationPasses.controller.js b/lib/server/controllers/simulationPasses.controller.js index 48d210414f..f70c5462ba 100644 --- a/lib/server/controllers/simulationPasses.controller.js +++ b/lib/server/controllers/simulationPasses.controller.js @@ -19,14 +19,20 @@ const { dtoValidator } = require('../utilities/dtoValidator.js'); const { countedItemsToHttpView } = require('../utilities/countedItemsToHttpView.js'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError'); const PaginationDto = require('../../domain/dtos/PaginationDto.js'); +const EntityIdDto = require('../../domain/dtos/EntityIdDto.js'); -// eslint-disable-next-line jsdoc/require-param /** * Get one Simulation Pass by id + * + * @param {Object} req The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} res The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const getSimulationPassByIdHandler = async (req, res) => { const validatedDTO = await dtoValidator( - DtoFactory.paramsOnly({ id: Joi.number() }), + DtoFactory.paramsOnly({ id: EntityIdDto.required() }), req, res, ); @@ -40,16 +46,21 @@ const getSimulationPassByIdHandler = async (req, res) => { } }; -// eslint-disable-next-line jsdoc/require-param /** * List All Simulation Passes with statistics + * + * @param {Object} req The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} res The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const listSimulationPassesHandler = async (req, res) => { const validatedDTO = await dtoValidator( DtoFactory.queryOnly({ filter: { - lhcPeriodIds: Joi.array().items(Joi.string()), - dataPassIds: Joi.array().items(Joi.string()), + lhcPeriodIds: Joi.array().items(Joi.number()), + dataPassIds: Joi.array().items(Joi.number()), ids: Joi.array().items(Joi.number()), names: Joi.array().items(Joi.string()), }, diff --git a/lib/server/services/simulationPasses/SimulationPassService.js b/lib/server/services/simulationPasses/SimulationPassService.js index 23f9976303..6327064ad3 100644 --- a/lib/server/services/simulationPasses/SimulationPassService.js +++ b/lib/server/services/simulationPasses/SimulationPassService.js @@ -11,12 +11,13 @@ * or submit itself to any jurisdiction. */ -const { repositories: { SimulationPassRepository } } = require('../../../database'); const { dataSource } = require('../../../database/DataSource'); const { simulationPassAdapter } = require('../../../database/adapters'); -const { Op } = require('sequelize'); +const { getAllSimulationPasses } = require('./getAllSimulationPasses.js'); const { getOneSimulationPass } = require('./getOneSimulationPass.js'); const { getOneSimulationPassOrFail } = require('./getOneSimulationPassOrFail.js'); +const { getSimulationPassIdsByDataPasses } = require('./getSimulationPassIdsByDataPasses.js'); +const { getSimulationPassIdsByLhcPeriods } = require('./getSimulationPassIdsByLhcPeriods.js'); /** * @typedef SimulationPassIdentifier @@ -70,42 +71,31 @@ class SimulationPassService { offset, sort, } = {}) { - const queryBuilder = this.prepareQueryBuilder(); + const { ids, names, lhcPeriodIds, dataPassIds } = filter ?? {}; + const lhcPeriodsSimulationPassIds = lhcPeriodIds ? await getSimulationPassIdsByLhcPeriods(lhcPeriodIds) : null; + const dataPassesSimulationPassIds = dataPassIds ? await getSimulationPassIdsByDataPasses(dataPassIds) : null; - if (sort) { - for (const property in sort) { - queryBuilder.orderBy(property, sort[property]); + const { count, rows } = await getAllSimulationPasses((queryBuilder) => { + this.prepareQueryBuilder(queryBuilder); + + if (sort) { + for (const property in sort) { + queryBuilder.orderBy(property, sort[property]); + } } - } - if (limit) { - queryBuilder.limit(limit); - } - if (offset) { - queryBuilder.offset(offset); - } + if (limit) { + queryBuilder.limit(limit); + } + if (offset) { + queryBuilder.offset(offset); + } - if (filter) { - const { ids, names, lhcPeriodIds, dataPassIds } = filter; - if (lhcPeriodIds) { - const associatedIds = (await SimulationPassRepository.findAll({ - raw: true, - include: { - association: 'dataPasses', - where: { lhcPeriodId: { [Op.in]: lhcPeriodIds } }, - }, - })).map(({ id }) => id); - queryBuilder.where('id').oneOf(...associatedIds); + if (lhcPeriodsSimulationPassIds) { + queryBuilder.where('id').oneOf(...lhcPeriodsSimulationPassIds); } - if (dataPassIds) { - const associatedIds = (await SimulationPassRepository.findAll({ - raw: true, - include: { - association: 'dataPasses', - where: { id: { [Op.in]: dataPassIds } }, - }, - })).map(({ id }) => id); - queryBuilder.where('id').oneOf(...associatedIds); + if (dataPassesSimulationPassIds) { + queryBuilder.where('id').oneOf(...dataPassesSimulationPassIds); } if (ids) { queryBuilder.where('id').oneOf(...ids); @@ -113,9 +103,7 @@ class SimulationPassService { if (names) { queryBuilder.where('name').oneOf(...names); } - } - - const { count, rows } = await SimulationPassRepository.findAndCountAll(queryBuilder); + }); return { count: count.length, // With GROUP-BY clause, findAndCountAll method returns list of counts per each grouping diff --git a/lib/server/services/simulationPasses/getAllSimulationPasses.js b/lib/server/services/simulationPasses/getAllSimulationPasses.js new file mode 100644 index 0000000000..2ed5f770da --- /dev/null +++ b/lib/server/services/simulationPasses/getAllSimulationPasses.js @@ -0,0 +1,33 @@ +/** + * @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 { dataSource } = require('../../../database/DataSource.js'); +const { SimulationPassRepository } = require('../../../database/repositories/index.js'); + +/** + * Find and return the simulation passes (with the number of matching rows) + * + * @param {(function(QueryBuilder):void)|null} [qbConfiguration=null] function called with the simulation passes find query builder as parameter + * to add specific configuration to the query + * @return {Promise<{count: number|object[], rows: SequelizeSimulationPass[]}>} resolves with the simulation passes found and their count. When + * the query is grouped (as the one prepared by the simulation pass service), the count is the list of the counts of each group + */ +exports.getAllSimulationPasses = (qbConfiguration = null) => { + const queryBuilder = dataSource.createQueryBuilder(); + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + + return SimulationPassRepository.findAndCountAll(queryBuilder); +}; diff --git a/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.js b/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.js new file mode 100644 index 0000000000..e756bbc5c0 --- /dev/null +++ b/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.js @@ -0,0 +1,33 @@ +/** + * @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 { Op } = require('sequelize'); +const { SimulationPassRepository } = require('../../../database/repositories/index.js'); + +/** + * Return the ids of the simulation passes linked to at least one of the given data passes + * + * @param {number[]} dataPassIds the ids of the data passes + * @return {Promise} resolves with the ids of the simulation passes + */ +exports.getSimulationPassIdsByDataPasses = async (dataPassIds) => { + const simulationPasses = await SimulationPassRepository.findAll({ + raw: true, + include: { + association: 'dataPasses', + where: { id: { [Op.in]: dataPassIds } }, + }, + }); + + return [...new Set(simulationPasses.map(({ id }) => id))]; +}; diff --git a/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.js b/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.js new file mode 100644 index 0000000000..b64697aa53 --- /dev/null +++ b/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.js @@ -0,0 +1,33 @@ +/** + * @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 { Op } = require('sequelize'); +const { SimulationPassRepository } = require('../../../database/repositories/index.js'); + +/** + * Return the ids of the simulation passes linked to at least one data pass of the given LHC periods + * + * @param {number[]} lhcPeriodIds the ids of the LHC periods + * @return {Promise} resolves with the ids of the simulation passes + */ +exports.getSimulationPassIdsByLhcPeriods = async (lhcPeriodIds) => { + const simulationPasses = await SimulationPassRepository.findAll({ + raw: true, + include: { + association: 'dataPasses', + where: { lhcPeriodId: { [Op.in]: lhcPeriodIds } }, + }, + }); + + return [...new Set(simulationPasses.map(({ id }) => id))]; +}; diff --git a/test/api/simulationPasses.test.js b/test/api/simulationPasses.test.js index 6d68ddeced..de1c7d6e1d 100644 --- a/test/api/simulationPasses.test.js +++ b/test/api/simulationPasses.test.js @@ -81,6 +81,21 @@ module.exports = () => { }); }); + it('should return 400 if the simulation pass id is not a positive integer', async () => { + for (const [id, message] of [ + ['abc', '"params.id" must be a number'], + ['-1', '"params.id" must be a positive number'], + ['0', '"params.id" must be a positive number'], + ['0.5', '"params.id" must be an integer'], + ]) { + const response = await request(server).get(`/api/simulationPasses/${id}`); + + expect(response.status, `id ${id}`).to.equal(400); + const error = response.body.errors.find(({ source: { pointer } }) => pointer === '/data/attributes/params/id'); + expect(error.detail, `id ${id}`).to.equal(message); + } + }); + it('should send error that simulation pass with given id cannot be found', (done) => { request(server) .get('/api/simulationPasses/99999') @@ -240,6 +255,18 @@ module.exports = () => { done(); }); }); + it('should return 400 if a filter on ids contains something else than a number', async () => { + for (const filterName of ['ids', 'lhcPeriodIds', 'dataPassIds']) { + const response = await request(server).get(`/api/simulationPasses?filter[${filterName}][]=abc`); + + expect(response.status, filterName).to.equal(400); + expect( + response.body.errors.some(({ source: { pointer }, detail }) => + pointer.includes(filterName) && detail.includes('must be a number')), + filterName, + ).to.be.true; + } + }); it('should successfully sort on id and name', (done) => { request(server) .get('/api/simulationPasses?sort[id]=DESC&sort[name]=ASC') diff --git a/test/lib/server/services/simulationPasses/getAllSimulationPasses.test.js b/test/lib/server/services/simulationPasses/getAllSimulationPasses.test.js new file mode 100644 index 0000000000..2caa9871d3 --- /dev/null +++ b/test/lib/server/services/simulationPasses/getAllSimulationPasses.test.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 { expect } = require('chai'); +const { getAllSimulationPasses } = require('../../../../../lib/server/services/simulationPasses/getAllSimulationPasses.js'); + +module.exports = () => { + it('should successfully return all the simulation passes with their count', async () => { + const { count, rows } = await getAllSimulationPasses(); + + expect(rows.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b', 'LHC23k6a']); + expect(count).to.equal(3); + }); + + it('should apply the given query builder configuration', async () => { + const { rows } = await getAllSimulationPasses((queryBuilder) => { + queryBuilder.where('name').is('LHC23k6b'); + }); + + expect(rows.map(({ name }) => name)).to.eql(['LHC23k6b']); + }); +}; diff --git a/test/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.test.js b/test/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.test.js new file mode 100644 index 0000000000..0801b48561 --- /dev/null +++ b/test/lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.test.js @@ -0,0 +1,27 @@ +/** + * @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 { + getSimulationPassIdsByDataPasses, +} = require('../../../../../lib/server/services/simulationPasses/getSimulationPassIdsByDataPasses.js'); + +module.exports = () => { + it('should successfully return the ids of the simulation passes linked to the given data passes', async () => { + expect(await getSimulationPassIdsByDataPasses([1])).to.eql([1]); + }); + + it('should return an empty list when no simulation pass is linked to the given data passes', async () => { + expect(await getSimulationPassIdsByDataPasses([9999])).to.eql([]); + }); +}; diff --git a/test/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.test.js b/test/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.test.js new file mode 100644 index 0000000000..25ecce4a4b --- /dev/null +++ b/test/lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.test.js @@ -0,0 +1,33 @@ +/** + * @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 { + getSimulationPassIdsByLhcPeriods, +} = require('../../../../../lib/server/services/simulationPasses/getSimulationPassIdsByLhcPeriods.js'); + +module.exports = () => { + it('should successfully return the ids of the simulation passes linked to the data passes of the given LHC periods', async () => { + expect(await getSimulationPassIdsByLhcPeriods([1])).to.have.members([2, 3]); + }); + + it('should return each simulation pass once even if several of its data passes belong to the periods', async () => { + const ids = await getSimulationPassIdsByLhcPeriods([1, 2, 3]); + + expect(ids).to.have.lengthOf(new Set(ids).size); + }); + + it('should return an empty list when no simulation pass is linked to the given LHC periods', async () => { + expect(await getSimulationPassIdsByLhcPeriods([9999])).to.eql([]); + }); +}; diff --git a/test/lib/server/services/simulationPasses/index.js b/test/lib/server/services/simulationPasses/index.js index 2a6a4fbb9b..e380cfbf96 100644 --- a/test/lib/server/services/simulationPasses/index.js +++ b/test/lib/server/services/simulationPasses/index.js @@ -13,8 +13,14 @@ const SimulationPassesService = require('./SimulationPassesService.test.js'); const getAllSimulationPassesByNameOrFail = require('./getAllSimulationPassesByNameOrFail.test.js'); +const getAllSimulationPasses = require('./getAllSimulationPasses.test.js'); +const getSimulationPassIdsByLhcPeriods = require('./getSimulationPassIdsByLhcPeriods.test.js'); +const getSimulationPassIdsByDataPasses = require('./getSimulationPassIdsByDataPasses.test.js'); module.exports = () => { describe('SimulationPassesService', SimulationPassesService); describe('getAllSimulationPassesByNameOrFail', getAllSimulationPassesByNameOrFail); + describe('getAllSimulationPasses', getAllSimulationPasses); + describe('getSimulationPassIdsByLhcPeriods', getSimulationPassIdsByLhcPeriods); + describe('getSimulationPassIdsByDataPasses', getSimulationPassIdsByDataPasses); };