diff --git a/lib/database/adapters/CtpTriggerCountersAdapter.js b/lib/database/adapters/CtpTriggerCountersAdapter.js index 6bf576f953..c775de6caf 100644 --- a/lib/database/adapters/CtpTriggerCountersAdapter.js +++ b/lib/database/adapters/CtpTriggerCountersAdapter.js @@ -29,7 +29,7 @@ class CtpTriggerCountersAdapter { * @param {SequelizeCtpTriggerCounters} databaseObject Object to convert. * @returns {CtpTriggerCounters} Converted entity object. */ - toEntity({ id, timestamp, runNumber, className, lmb, lma, l0b, l0a, l1b, l1a, createdAt, updatedAt }) { + toEntity({ id, timestamp, runNumber, className, lmb, lma, l0b, l0a, l1b, l1a, createdAt }) { return { id, timestamp: timestamp ? new Date(timestamp).getTime() : createdAt, @@ -41,8 +41,6 @@ class CtpTriggerCountersAdapter { l0a, l1b, l1a, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, }; } diff --git a/lib/database/migrations/v1/20250211085415-rename-trigger-counters-to-ctp-trigger-counters.js b/lib/database/migrations/v1/20250211085415-rename-trigger-counters-to-ctp-trigger-counters.js index 37da4d0c6a..2931e3049f 100644 --- a/lib/database/migrations/v1/20250211085415-rename-trigger-counters-to-ctp-trigger-counters.js +++ b/lib/database/migrations/v1/20250211085415-rename-trigger-counters-to-ctp-trigger-counters.js @@ -1,3 +1,16 @@ +/** + * @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. + */ + 'use strict'; /** @type {import('sequelize-cli').Migration} */ diff --git a/lib/database/models/ctpTriggerCounters.js b/lib/database/models/ctpTriggerCounters.js index 4e8234a752..e2c8e5d903 100644 --- a/lib/database/models/ctpTriggerCounters.js +++ b/lib/database/models/ctpTriggerCounters.js @@ -1,3 +1,16 @@ +/** + * @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 Sequelize = require('sequelize'); module.exports = (sequelize) => { diff --git a/lib/domain/entities/CtpTriggerCounters.js b/lib/domain/entities/CtpTriggerCounters.js index e66af5ad79..71808a0ddf 100644 --- a/lib/domain/entities/CtpTriggerCounters.js +++ b/lib/domain/entities/CtpTriggerCounters.js @@ -24,6 +24,4 @@ * @property {number} l0a * @property {number} l1b * @property {number} l1a - * @property {number} createdAt - * @property {number} updatedAt */ diff --git a/lib/server/api-docs/API.md b/lib/server/api-docs/API.md index 6a9bccd0a2..0b9bdd80fe 100644 --- a/lib/server/api-docs/API.md +++ b/lib/server/api-docs/API.md @@ -325,3 +325,28 @@ Responses: ```bash curl -g "$BOOKKEEPING_URL/api/runTypes/1?token=$TOKEN" ``` + +## CTP trigger counters + +Counters of the central trigger processor, per run and trigger class. + +### `GET /ctp-trigger-counters/{runNumber}` - List the CTP trigger counters of a run + +Operation id: `listCtpTriggerCountersOfRun` + +Returns the trigger counters of every trigger class recorded for the run. Not paginated. +A run without counters gives `200` with an empty list. A run that does not exist gives `404`. + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `runNumber` | path | integer | yes | Run number. | + +Responses: + +- `200`: The counters, one entry per trigger class. +- `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 run has this run number. + +```bash +curl -g "$BOOKKEEPING_URL/api/ctp-trigger-counters/106?token=$TOKEN" +``` diff --git a/lib/server/api-docs/index.html b/lib/server/api-docs/index.html index 591258d62b..6f3163ee7f 100644 --- a/lib/server/api-docs/index.html +++ b/lib/server/api-docs/index.html @@ -535,6 +535,41 @@

Run types

curl -g "$BOOKKEEPING_URL/api/runTypes/1?token=$TOKEN"
 
+

CTP trigger counters

+

Counters of the central trigger processor, per run and trigger class.

+
+

GET /ctp-trigger-counters/{runNumber} - List the CTP trigger counters of a run

+ +

Operation id: listCtpTriggerCountersOfRun

+

Returns the trigger counters of every trigger class recorded for the run. Not paginated. +A run without counters gives 200 with an empty list. A run that does not exist gives 404.

+ + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
runNumberpathintegeryesRun number.
+

Responses:

+
    +
  • 200: The counters, one entry per trigger class.
  • +
  • 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 run has this run number.
  • +
+
curl -g "$BOOKKEEPING_URL/api/ctp-trigger-counters/106?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 700d80fd25..2069aec8fa 100644 --- a/lib/server/api-docs/openapi.yaml +++ b/lib/server/api-docs/openapi.yaml @@ -45,6 +45,8 @@ tags: description: Detectors of the ALICE experiment. - name: Run types description: Types of runs (e.g. calibration kinds). + - name: CTP trigger counters + description: Counters of the central trigger processor, per run and trigger class. paths: /tags: @@ -359,6 +361,39 @@ paths: errors: - { status: 404, title: Not found, detail: Run type with this id (999999999) could not be found } + /ctp-trigger-counters/{runNumber}: + get: + tags: [CTP trigger counters] + operationId: listCtpTriggerCountersOfRun + summary: List the CTP trigger counters of a run + description: | + Returns the trigger counters of every trigger class recorded for the run. Not paginated. + A run without counters gives `200` with an empty list. A run that does not exist gives `404`. + parameters: + - name: runNumber + in: path + required: true + description: Run number. + schema: { type: integer, minimum: 1, examples: [106] } + responses: + '200': + description: The counters, one entry per trigger class. + content: + application/json: + schema: { $ref: '#/components/schemas/CtpTriggerCountersListResponse' } + example: + data: + - { id: 1, runNumber: 106, className: minbias, timestamp: 1704067200000, lmb: 10, lma: 9, l0b: 8, l0a: 7, l1b: 6, l1a: 5 } + '400': { $ref: '#/components/responses/ValidationError' } + '404': + description: No run has this run number. + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + errors: + - { status: 404, title: Not found, detail: Run with this run number (999999999) could not be found } + components: securitySchemes: jwtQuery: @@ -540,3 +575,24 @@ components: data: type: array items: { $ref: '#/components/schemas/RunType' } + CtpTriggerCounters: + type: object + description: Counters of one CTP trigger class of a run (LM, L0 and L1 levels, before and after vetoes). + properties: + id: { type: integer } + runNumber: { type: integer } + className: { type: string, description: Name of the trigger class. } + timestamp: { type: integer, description: Epoch milliseconds of the measurement. } + lmb: { type: integer, description: LM level counter before vetoes. } + lma: { type: integer, description: LM level counter after vetoes. } + l0b: { type: integer, description: L0 level counter before vetoes. } + l0a: { type: integer, description: L0 level counter after vetoes. } + l1b: { type: integer, description: L1 level counter before vetoes. } + l1a: { type: integer, description: L1 level counter after vetoes. } + CtpTriggerCountersListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/CtpTriggerCounters' } diff --git a/lib/server/controllers/ctpTriggerCounters.controller.js b/lib/server/controllers/ctpTriggerCounters.controller.js index 9f290dbdd9..97a59c29a7 100644 --- a/lib/server/controllers/ctpTriggerCounters.controller.js +++ b/lib/server/controllers/ctpTriggerCounters.controller.js @@ -1,16 +1,34 @@ +/** + * @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 { dtoValidator } = require('../utilities'); const { DtoFactory } = require('../../domain/dtos/DtoFactory'); -const Joi = require('joi'); +const EntityIdDto = require('../../domain/dtos/EntityIdDto.js'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError'); const { ctpTriggerCountersService } = require('../services/ctpTriggerCounters/CtpTriggerCountersService'); -// eslint-disable-next-line jsdoc/require-param /** * Return all the trigger counters for a given run + * + * @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. + * @returns {Promise} resolves once the response has been sent */ const getAllCtpTriggerCountersForRunHandler = async (request, response) => { const requestDto = await dtoValidator(DtoFactory.paramsOnly({ - runNumber: Joi.number().required(), + runNumber: EntityIdDto.required(), }), request, response); if (requestDto) { diff --git a/lib/server/controllers/gRPC/GRPCCtpTriggerCountersController.js b/lib/server/controllers/gRPC/GRPCCtpTriggerCountersController.js index 0dab70caee..b026846da7 100644 --- a/lib/server/controllers/gRPC/GRPCCtpTriggerCountersController.js +++ b/lib/server/controllers/gRPC/GRPCCtpTriggerCountersController.js @@ -24,7 +24,21 @@ class GRPCCtpTriggerCountersController { this.ctpTriggerCountersService = ctpTriggerCountersService; } - // eslint-disable-next-line jsdoc/require-jsdoc + /** + * Create or update the counters of a trigger class of a run + * + * @param {object} request the gRPC request + * @param {number} request.runNumber the run number of the run for which counters are created/updated + * @param {string} request.className the name of the trigger class for which counters are created/updated + * @param {string|number} [request.timestamp] the timestamp of the measure (UNIX timestamp in milliseconds) + * @param {number} request.lmb LM level counter before vetoes + * @param {number} request.lma LM level counter after vetoes + * @param {number} request.l0b L0 level counter before vetoes + * @param {number} request.l0a L0 level counter after vetoes + * @param {number} request.l1b L1 level counter before vetoes + * @param {number} request.l1a L1 level counter after vetoes + * @return {Promise} resolves with an empty response once the counters have been created or updated + */ async CreateOrUpdateForRun({ runNumber, className, timestamp, lmb, lma, l0b, l0a, l1b, l1a }) { await this.ctpTriggerCountersService.createOrUpdatePerRun( { runNumber, className }, diff --git a/lib/server/routers/ctpTriggerCounters.router.js b/lib/server/routers/ctpTriggerCounters.router.js index 73908d3a9f..1ebf2e627e 100644 --- a/lib/server/routers/ctpTriggerCounters.router.js +++ b/lib/server/routers/ctpTriggerCounters.router.js @@ -1,3 +1,16 @@ +/** + * @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 { getAllCtpTriggerCountersForRunHandler } = require('../controllers/ctpTriggerCounters.controller'); exports.ctpTriggerCountersRouter = { diff --git a/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.js b/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.js index db88e8719f..a592dece0a 100644 --- a/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.js +++ b/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.js @@ -1,6 +1,20 @@ -const { CtpTriggerCountersRepository } = require('../../../database/repositories'); +/** + * @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 { ctpTriggerCountersAdapter } = require('../../../database/adapters'); const { getRunOrFail } = require('../run/getRunOrFail.js'); +const { getAllCtpTriggerCounters } = require('./getAllCtpTriggerCounters.js'); +const { createOrUpdateCtpTriggerCounters } = require('./createOrUpdateCtpTriggerCounters.js'); /** * Service related to trigger counters @@ -10,10 +24,22 @@ class CtpTriggerCountersService { * Return the list of trigger counters linked to a given run * * @param {number} runNumber the run number of the run for which counters must be fetched - * @return {Promise} the trigger counters for the run + * @return {Promise} the trigger counters for the run (empty if the run has none) + * @throws {NotFoundError} if the run does not exist */ async getPerRun(runNumber) { - return (await CtpTriggerCountersRepository.findAll({ where: { runNumber } })).map(ctpTriggerCountersAdapter.toEntity); + const counters = (await getAllCtpTriggerCounters((queryBuilder) => queryBuilder.where('runNumber').is(runNumber))) + .map(ctpTriggerCountersAdapter.toEntity); + + if (counters.length === 0) { + /* + * Counters can only exist for an existing run (foreign key), so the run only needs to be checked to tell a run without counters + * from a missing run + */ + await getRunOrFail({ runNumber }); + } + + return counters; } /** @@ -21,24 +47,14 @@ class CtpTriggerCountersService { * * @param {object} criteria the criteria identifying the counters to update * @param {number} criteria.runNumber the run number of run for which trigger counters are created/updated - * @param {string} criteria.className the run number of run for which trigger counters are created/updated + * @param {string} criteria.className the name of the trigger class for which counters are created/updated * @param {Pick} counters the actual counters data * @return {Promise} resolves once the counters have been created */ async createOrUpdatePerRun({ runNumber, className }, counters) { // Check that run exists const run = await getRunOrFail({ runNumber }); - await CtpTriggerCountersRepository.upsert({ - runNumber: run.runNumber, - className, - timestamp: counters.timestamp, - lmb: counters.lmb, - lma: counters.lma, - l0b: counters.l0b, - l0a: counters.l0a, - l1b: counters.l1b, - l1a: counters.l1a, - }); + await createOrUpdateCtpTriggerCounters({ runNumber: run.runNumber, className }, counters); } } diff --git a/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.js b/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.js new file mode 100644 index 0000000000..75209d70b1 --- /dev/null +++ b/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.js @@ -0,0 +1,37 @@ +/** + * @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 { CtpTriggerCountersRepository } = require('../../../database/repositories/index.js'); + +/** + * Create the counters of a trigger class of a run, or update them if they already exist + * + * @param {object} criteria the criteria identifying the counters to create or update + * @param {number} criteria.runNumber the run number of the run for which counters are created or updated + * @param {string} criteria.className the name of the trigger class for which counters are created or updated + * @param {Pick} counters the actual counters data + * @return {Promise} resolves once the counters have been created or updated + */ +exports.createOrUpdateCtpTriggerCounters = async ({ runNumber, className }, counters) => { + await CtpTriggerCountersRepository.upsert({ + runNumber, + className, + timestamp: counters.timestamp, + lmb: counters.lmb, + lma: counters.lma, + l0b: counters.l0b, + l0a: counters.l0a, + l1b: counters.l1b, + l1a: counters.l1a, + }); +}; diff --git a/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.js b/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.js new file mode 100644 index 0000000000..8cdd9f9302 --- /dev/null +++ b/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.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 { CtpTriggerCountersRepository } = require('../../../database/repositories/index.js'); +const { utilities: { QueryBuilder } } = require('../../../database'); + +/** + * Return the list of all the CTP trigger counters + * + * @param {Function|null} [qbConfiguration] function called with the counters query builder as parameter to add specific configuration to the + * query + * @return {Promise} Promise resolving with the list of counters + */ +exports.getAllCtpTriggerCounters = (qbConfiguration = null) => { + const queryBuilder = new QueryBuilder(); + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + return CtpTriggerCountersRepository.findAll(queryBuilder); +}; diff --git a/test/api/ctpTriggerCounters.test.js b/test/api/ctpTriggerCounters.test.js index 2d127c6be2..aedf0f42a8 100644 --- a/test/api/ctpTriggerCounters.test.js +++ b/test/api/ctpTriggerCounters.test.js @@ -1,3 +1,16 @@ +/** + * @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 { resetDatabaseContent } = require('../utilities/resetDatabaseContent'); const request = require('supertest'); const { server } = require('../../lib/application'); @@ -12,6 +25,9 @@ module.exports = () => { const response = await request(server).get('/api/ctp-trigger-counters/1'); expect(response.status).to.equal(200); + response.body.data.forEach((counters) => expect(Object.keys(counters)).to.have.members( + ['id', 'timestamp', 'runNumber', 'className', 'lmb', 'lma', 'l0b', 'l0a', 'l1b', 'l1a'], + )); expect(response.body.data.map(({ id, runNumber, className }) => ({ id, runNumber, className }))).to.deep.eql([ { id: 1, runNumber: 1, className: 'FIRST-CLASS-NAME' }, { id: 2, runNumber: 1, className: 'SECOND-CLASS-NAME' }, @@ -24,5 +40,29 @@ module.exports = () => { expect(response.body.data.map(({ id }) => id)).to.eql([]); } }); + + it('Should return 404 if the run does not exist', async () => { + const response = await request(server).get('/api/ctp-trigger-counters/999999999'); + + expect(response.status).to.equal(404); + expect(response.body.errors).to.eql([ + { status: 404, title: 'Not found', detail: 'Run with this run number (999999999) could not be found' }, + ]); + }); + + it('Should return 400 if the run number is not a positive integer', async () => { + for (const [runNumber, message] of [ + ['abc', '"params.runNumber" must be a number'], + ['-1', '"params.runNumber" must be a positive number'], + ['0', '"params.runNumber" must be a positive number'], + ['0.5', '"params.runNumber" must be an integer'], + ]) { + const response = await request(server).get(`/api/ctp-trigger-counters/${runNumber}`); + + expect(response.status, `run number ${runNumber}`).to.equal(400); + const error = response.body.errors.find(({ source: { pointer } }) => pointer === '/data/attributes/params/runNumber'); + expect(error.detail, `run number ${runNumber}`).to.equal(message); + } + }); }); }; diff --git a/test/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.test.js b/test/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.test.js index fed65c6813..1359058779 100644 --- a/test/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.test.js +++ b/test/lib/server/services/ctpTriggerCounters/CtpTriggerCountersService.test.js @@ -1,11 +1,25 @@ +/** + * @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 { ctpTriggerCountersService } = require('../../../../../lib/server/services/ctpTriggerCounters/CtpTriggerCountersService'); const { expect } = require('chai'); const { resetDatabaseContent } = require('../../../../utilities/resetDatabaseContent.js'); const assert = require('assert'); const { BadParameterError } = require('../../../../../lib/server/errors/BadParameterError.js'); +const { NotFoundError } = require('../../../../../lib/server/errors/NotFoundError.js'); /** - * Simplify a trigger counter to extract properties interesting in testing (remove createdAt and updatedAt) + * Simplify a trigger counter to extract properties interesting in testing * * @param {CtpTriggerCounters} counters the counters to simplify * @return {object} simplified trigger counters @@ -56,6 +70,13 @@ module.exports = () => { expect(await ctpTriggerCountersService.getPerRun(2)).to.eql([]); }); + it('Should throw a not found error when fetching the counters of a non-existing run', async () => { + await assert.rejects( + () => ctpTriggerCountersService.getPerRun(999), + new NotFoundError('Run with this run number (999) could not be found'), + ); + }); + it('Should successfully insert new counters for a given run', async () => { const now = new Date(); const counters = { diff --git a/test/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.test.js b/test/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.test.js new file mode 100644 index 0000000000..99a11ff524 --- /dev/null +++ b/test/lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.test.js @@ -0,0 +1,52 @@ +/** + * @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 { createOrUpdateCtpTriggerCounters } = require('../../../../../lib/server/services/ctpTriggerCounters/createOrUpdateCtpTriggerCounters.js'); +const { getAllCtpTriggerCounters } = require('../../../../../lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.js'); +const { expect } = require('chai'); + +/** + * Return the counters of a given trigger class of a given run + * + * @param {number} runNumber the run number + * @param {string} className the name of the trigger class + * @return {Promise} the counters found + */ +const getCounters = (runNumber, className) => getAllCtpTriggerCounters((queryBuilder) => { + queryBuilder.where('runNumber').is(runNumber); + queryBuilder.where('className').is(className); +}); + +module.exports = () => { + const criteria = { runNumber: 2, className: 'CREATE-OR-UPDATE-CLASS' }; + + it('should successfully create the counters of a trigger class of a run', async () => { + expect(await getCounters(criteria.runNumber, criteria.className)).to.eql([]); + + await createOrUpdateCtpTriggerCounters(criteria, { timestamp: 1717425912000, lmb: 1, lma: 2, l0b: 3, l0a: 4, l1b: 5, l1a: 6 }); + + const [counters, ...others] = await getCounters(criteria.runNumber, criteria.className); + expect(others).to.lengthOf(0); + expect([counters.lmb, counters.lma, counters.l0b, counters.l0a, counters.l1b, counters.l1a]).to.eql([1, 2, 3, 4, 5, 6]); + expect(new Date(counters.timestamp).getTime()).to.equal(1717425912000); + }); + + it('should successfully update the counters instead of creating new ones when they already exist', async () => { + await createOrUpdateCtpTriggerCounters(criteria, { timestamp: 1717425913000, lmb: 11, lma: 12, l0b: 13, l0a: 14, l1b: 15, l1a: 16 }); + + const [counters, ...others] = await getCounters(criteria.runNumber, criteria.className); + expect(others).to.lengthOf(0); + expect([counters.lmb, counters.lma, counters.l0b, counters.l0a, counters.l1b, counters.l1a]).to.eql([11, 12, 13, 14, 15, 16]); + expect(new Date(counters.timestamp).getTime()).to.equal(1717425913000); + }); +}; diff --git a/test/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.test.js b/test/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.test.js new file mode 100644 index 0000000000..a4afe880c8 --- /dev/null +++ b/test/lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.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 { getAllCtpTriggerCounters } = require('../../../../../lib/server/services/ctpTriggerCounters/getAllCtpTriggerCounters.js'); +const { expect } = require('chai'); + +module.exports = () => { + it('should successfully return all the trigger counters', async () => { + const counters = await getAllCtpTriggerCounters(); + + expect(counters.length).to.be.greaterThan(1); + expect(counters.map(({ className }) => className)).to.include.members(['FIRST-CLASS-NAME', 'SECOND-CLASS-NAME']); + }); + + it('should apply the given query builder configuration', async () => { + const countersOfRun1 = await getAllCtpTriggerCounters((queryBuilder) => queryBuilder.where('runNumber').is(1)); + expect(countersOfRun1.map(({ className }) => className)).to.have.members(['FIRST-CLASS-NAME', 'SECOND-CLASS-NAME']); + expect(countersOfRun1.every(({ runNumber }) => runNumber === 1)).to.be.true; + + expect(await getAllCtpTriggerCounters((queryBuilder) => queryBuilder.where('runNumber').is(2))).to.eql([]); + }); +}; diff --git a/test/lib/server/services/ctpTriggerCounters/index.js b/test/lib/server/services/ctpTriggerCounters/index.js index 0c11f8c347..e014f767fd 100644 --- a/test/lib/server/services/ctpTriggerCounters/index.js +++ b/test/lib/server/services/ctpTriggerCounters/index.js @@ -1,8 +1,26 @@ +/** + * @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 { resetDatabaseContent } = require('../../../../utilities/resetDatabaseContent'); const CtpTriggerCountersServiceTest = require('./CtpTriggerCountersService.test'); +const getAllCtpTriggerCountersTest = require('./getAllCtpTriggerCounters.test.js'); +const createOrUpdateCtpTriggerCountersTest = require('./createOrUpdateCtpTriggerCounters.test.js'); module.exports = () => { before(resetDatabaseContent); + after(resetDatabaseContent); describe('CtpTriggerCountersService', CtpTriggerCountersServiceTest); + describe('getAllCtpTriggerCounters', getAllCtpTriggerCountersTest); + describe('createOrUpdateCtpTriggerCounters', createOrUpdateCtpTriggerCountersTest); };