From 69db5952dde31b6accbb2ce62abcc49c5565316e Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Fri, 14 Aug 2026 10:30:26 +0200 Subject: [PATCH 1/5] refactor(api): remove CandidateNotFoundError from candidate-repository.update method It is unnecessarry to check whether the candidate exists or not as this is already done where candidate-repository.update is called (be it usecases or services) --- .../repositories/candidate-repository.js | 8 +-- .../repositories/candidate-repository_test.js | 72 +++++++------------ 2 files changed, 28 insertions(+), 52 deletions(-) diff --git a/api/src/certification/enrolment/infrastructure/repositories/candidate-repository.js b/api/src/certification/enrolment/infrastructure/repositories/candidate-repository.js index 7dd2ba0db50..e82b6c7ad31 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/candidate-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/candidate-repository.js @@ -4,7 +4,6 @@ // @ts-check import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; -import { CertificationCandidateNotFoundError } from '../../../shared/domain/errors.js'; import { Candidate } from '../../domain/models/Candidate.js'; /** @@ -57,8 +56,7 @@ export async function findByUserId({ userId }) { * @function * @param {Candidate} candidate * - * @returns {Promise} - * @throws {CertificationCandidateNotFoundError} Certification candidate not found + * @returns {Promise} the updated candidate, or undefined when no candidate was found */ export async function update(candidate) { const candidateDataToSave = adaptModelToDb(candidate); @@ -71,9 +69,7 @@ export async function update(candidate) { .update(candidateDataToSave) .returning('*'); - if (!updatedCertificationCandidate) { - throw new CertificationCandidateNotFoundError(); - } + return updatedCertificationCandidate; } /** diff --git a/api/tests/certification/enrolment/integration/infrastructure/repositories/candidate-repository_test.js b/api/tests/certification/enrolment/integration/infrastructure/repositories/candidate-repository_test.js index 620f97f1cbb..712db1d5d15 100644 --- a/api/tests/certification/enrolment/integration/infrastructure/repositories/candidate-repository_test.js +++ b/api/tests/certification/enrolment/integration/infrastructure/repositories/candidate-repository_test.js @@ -1,11 +1,9 @@ import { Candidate } from '../../../../../../src/certification/enrolment/domain/models/Candidate.js'; import * as candidateRepository from '../../../../../../src/certification/enrolment/infrastructure/repositories/candidate-repository.js'; -import { CertificationCandidateNotFoundError } from '../../../../../../src/certification/shared/domain/errors.js'; import { Frameworks } from '../../../../../../src/certification/shared/domain/models/Frameworks.js'; import { expect } from '../../../../../test-helper.js'; import { databaseBuilder } from '../../../../../tooling/databases.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; -import { catchErr } from '../../../../../tooling/test-utils/error.js'; describe('Integration | Certification | Enrolment | Repository | Candidate', function () { describe('#get', function () { @@ -147,59 +145,41 @@ describe('Integration | Certification | Enrolment | Repository | Candidate', fun }); describe('#update', function () { - context('when the candidate exists', function () { - it('should update the candidate', async function () { - // when - const certificationCandidate = domainBuilder.certification.enrolment - .candidateBuilder() - .withIdentity({ firstName: 'toto' }) - .insertToDB({ databaseBuilder }); - - await databaseBuilder.commit(); + it('updates the candidate', async function () { + // when + const certificationCandidate = domainBuilder.certification.enrolment + .candidateBuilder() + .withIdentity({ firstName: 'toto' }) + .insertToDB({ databaseBuilder }); - // when - await candidateRepository.update({ ...certificationCandidate, firstName: 'tutu' }); + await databaseBuilder.commit(); - const candidate = await candidateRepository.get({ - certificationCandidateId: certificationCandidate.id, - }); + // when + await candidateRepository.update({ ...certificationCandidate, firstName: 'tutu' }); - // then - expect(candidate).to.be.instanceOf(Candidate); - expect(candidate.firstName).to.equal('tutu'); + const candidate = await candidateRepository.get({ + certificationCandidateId: certificationCandidate.id, }); - it('should update its subscription', async function () { - // given - const certificationCandidate = domainBuilder.certification.enrolment - .candidateBuilder() - .withSubscription(Frameworks.DROIT) - .insertToDB({ databaseBuilder }); - await databaseBuilder.commit(); - - // when - await candidateRepository.update({ ...certificationCandidate, subscription: Frameworks.EDU_1ER_DEGRE }); - - // then - const updated = await candidateRepository.get({ certificationCandidateId: certificationCandidate.id }); - expect(updated.subscription).to.equal(Frameworks.EDU_1ER_DEGRE); - }); + // then + expect(candidate).to.be.instanceOf(Candidate); + expect(candidate.firstName).to.equal('tutu'); }); - context('when the candidate does not exist', function () { - it('should throw', async function () { - // when - const certificationCandidateToUpdate = domainBuilder.certification.enrolment.buildCertificationSessionCandidate( - { firstName: 'candidate unknown' }, - ); - - certificationCandidateToUpdate.firstName = 'tutu'; + it('updates its subscription', async function () { + // given + const certificationCandidate = domainBuilder.certification.enrolment + .candidateBuilder() + .withSubscription(Frameworks.DROIT) + .insertToDB({ databaseBuilder }); + await databaseBuilder.commit(); - const error = await catchErr(candidateRepository.update)(certificationCandidateToUpdate); + // when + await candidateRepository.update({ ...certificationCandidate, subscription: Frameworks.EDU_1ER_DEGRE }); - // then - expect(error).to.be.instanceOf(CertificationCandidateNotFoundError); - }); + // then + const updated = await candidateRepository.get({ certificationCandidateId: certificationCandidate.id }); + expect(updated.subscription).to.equal(Frameworks.EDU_1ER_DEGRE); }); }); From f782025e4e203f2f6e3dda51d589681645a49617 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Fri, 14 Aug 2026 11:27:12 +0200 Subject: [PATCH 2/5] refactor(api): remove thrown SessionNotFoundError from session-for-attendance-sheet-repository getWithCertificationCandidates --- .../domain/usecases/get-attendance-sheet.js | 6 ++++ ...session-for-attendance-sheet-repository.js | 3 +- ...on-for-attendance-sheet-repository_test.js | 14 ++++---- .../usecases/get-attendance-sheet_test.js | 34 +++++++++++++++++-- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js index 2ec9adfe83a..780fa022933 100644 --- a/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js +++ b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js @@ -3,6 +3,8 @@ * @typedef {import('./index.js').AttendanceSheetPdfUtils} AttendanceSheetPdfUtils */ +import { NotFoundError } from '../../../../shared/domain/errors.js'; + /** * @param {object} params * @param {SessionForAttendanceSheetRepository} params.sessionForAttendanceSheetRepository @@ -16,6 +18,10 @@ const getAttendanceSheet = async function ({ }) { const session = await sessionForAttendanceSheetRepository.getWithCertificationCandidates({ id: sessionId }); + if (!session) { + throw new NotFoundError("La session n'existe pas ou aucun candidat n'est inscrit à celle-ci"); + } + const { attendanceSheet, fileName } = await attendanceSheetPdfUtils.getAttendanceSheetPdfBuffer({ session, i18n, diff --git a/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js index b1db6dc1288..b39de188842 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js @@ -1,5 +1,4 @@ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; -import { NotFoundError } from '../../../../shared/domain/errors.js'; import { CertificationCandidateForAttendanceSheet } from '../../domain/read-models/CertificationCandidateForAttendanceSheet.js'; import { SessionForAttendanceSheet } from '../../domain/read-models/SessionForAttendanceSheet.js'; @@ -59,7 +58,7 @@ export async function getWithCertificationCandidates({ id }) { .first(); if (!results || results.certificationCandidates === null) { - throw new NotFoundError("La session n'existe pas ou aucun candidat n'est inscrit à celle-ci"); + return null; } return _toDomain(results); diff --git a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-for-attendance-sheet-repository_test.js b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-for-attendance-sheet-repository_test.js index d0faef62e36..8a93f7f92a0 100644 --- a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-for-attendance-sheet-repository_test.js +++ b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-for-attendance-sheet-repository_test.js @@ -1,11 +1,9 @@ import { CertificationCandidateForAttendanceSheet } from '../../../../../../src/certification/enrolment/domain/read-models/CertificationCandidateForAttendanceSheet.js'; import { SessionForAttendanceSheet } from '../../../../../../src/certification/enrolment/domain/read-models/SessionForAttendanceSheet.js'; import * as sessionForAttendanceSheetRepository from '../../../../../../src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js'; -import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { expect } from '../../../../../test-helper.js'; import { databaseBuilder } from '../../../../../tooling/databases.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; -import { catchErr } from '../../../../../tooling/test-utils/error.js'; describe('Integration | Repository | Session-for-attendance-sheet', function () { describe('#getWithCertificationCandidates', function () { @@ -190,31 +188,31 @@ describe('Integration | Repository | Session-for-attendance-sheet', function () }); context('when no session was found', function () { - it('should return a Not found error', async function () { + it('returns null', async function () { // when - const error = await catchErr(sessionForAttendanceSheetRepository.getWithCertificationCandidates)({ + const session = await sessionForAttendanceSheetRepository.getWithCertificationCandidates({ id: 12434354, }); // then - expect(error).to.be.instanceOf(NotFoundError); + expect(session).to.be.null; }); }); context('when no certification candidates was found', function () { - it('should return a Not found error', async function () { + it('returns null', async function () { // given const sessionId = 1234; databaseBuilder.factory.buildSession({ id: sessionId }); await databaseBuilder.commit(); // when - const error = await catchErr(sessionForAttendanceSheetRepository.getWithCertificationCandidates)({ + const session = await sessionForAttendanceSheetRepository.getWithCertificationCandidates({ id: sessionId, }); // then< - expect(error).to.be.instanceOf(NotFoundError); + expect(session).to.be.null; }); }); }); diff --git a/api/tests/certification/enrolment/unit/domain/usecases/get-attendance-sheet_test.js b/api/tests/certification/enrolment/unit/domain/usecases/get-attendance-sheet_test.js index 98a468dcab8..653e496e118 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/get-attendance-sheet_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/get-attendance-sheet_test.js @@ -1,19 +1,48 @@ import sinon from 'sinon'; import { getAttendanceSheet } from '../../../../../../src/certification/enrolment/domain/usecases/get-attendance-sheet.js'; +import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { expect } from '../../../../../test-helper.js'; +import { catchErr } from '../../../../../tooling/test-utils/error.js'; describe('Unit | UseCase | get-attendance-sheet', function () { describe('getAttendanceSheet', function () { + describe('when no session is found', function () { + it('throws a SessionNotFound error', async function () { + // given + const userId = 'dummyUserId'; + const i18n = 'dummyi18n'; + const sessionForAttendanceSheetRepository = { getWithCertificationCandidates: sinon.stub() }; + + sessionForAttendanceSheetRepository.getWithCertificationCandidates.withArgs({ id: 1 }).resolves(null); + + const attendanceSheetPdfUtilsStub = { + getAttendanceSheetPdfBuffer: sinon.stub(), + }; + + // when + const error = await catchErr(getAttendanceSheet)({ + userId, + sessionId: 1, + i18n, + sessionForAttendanceSheetRepository, + attendanceSheetPdfUtils: attendanceSheetPdfUtilsStub, + }); + + // then + expect(error).to.deepEqualInstance( + new NotFoundError("La session n'existe pas ou aucun candidat n'est inscrit à celle-ci"), + ); + }); + }); + it('should return the attendance sheet in pdf format', async function () { // given const userId = 'dummyUserId'; const i18n = 'dummyi18n'; - const sessionRepository = { doesUserHaveCertificationCenterMembershipForSession: sinon.stub() }; const sessionForAttendanceSheetRepository = { getWithCertificationCandidates: sinon.stub() }; const session = _buildSessionWithCandidate('SUP', true); - sessionRepository.doesUserHaveCertificationCenterMembershipForSession.resolves(true); sessionForAttendanceSheetRepository.getWithCertificationCandidates.withArgs({ id: 1 }).resolves(session); const pdfBuffer = Buffer.from('some pdf file'); @@ -31,7 +60,6 @@ describe('Unit | UseCase | get-attendance-sheet', function () { userId, sessionId: 1, i18n, - sessionRepository, sessionForAttendanceSheetRepository, attendanceSheetPdfUtils: attendanceSheetPdfUtilsStub, }); From 9f0f0d26b14f25f4c1fc0b8ad9acf78bceb23fe5 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Fri, 14 Aug 2026 14:30:55 +0200 Subject: [PATCH 3/5] refator(api): remove NotFoundError from sessionRepository.get() - Different calls from sessionAuthorizationAdapter.find already return null if no session is found - As these calls are made before sessionRepository.get in all the usecases modified in this commit we check on missing session beforehand to throw a NotFoundError - Note: some sessionRepository.get calls are made directly from controllers, we return 404 error codes instead of mapped domain errors. This choice prevents any additional information to be returned from the route call --- .../application/session-controller.js | 12 ++++- .../usecases/add-candidate-to-session.js | 6 +++ .../usecases/enrol-students-to-session.js | 7 ++- .../get-candidate-import-sheet-data.js | 6 +++ ...candidates-from-candidates-import-sheet.js | 8 ++- .../repositories/session-repository.js | 4 +- .../repositories/session-repository_test.js | 7 +-- .../application/session-controller_test.js | 53 +++++++++++++++++++ .../usecases/add-candidate-to-session_test.js | 22 ++++++++ .../enrol-students-to-session_test.js | 17 +++++- .../get-candidate-import-sheet-data_test.js | 22 ++++++++ ...dates-from-candidates-import-sheet_test.js | 22 +++++++- 12 files changed, 176 insertions(+), 10 deletions(-) diff --git a/api/src/certification/enrolment/application/session-controller.js b/api/src/certification/enrolment/application/session-controller.js index 4251c3b49e4..8c7e782b821 100644 --- a/api/src/certification/enrolment/application/session-controller.js +++ b/api/src/certification/enrolment/application/session-controller.js @@ -4,7 +4,7 @@ import * as sessionRepository from '../infrastructure/repositories/session-repos import { candidateSerializer } from '../infrastructure/serializers/candidate-serializer.js'; import { sessionSerializer } from '../infrastructure/serializers/session-serializer.js'; -async function createSession(request, _h, dependencies = { sessionSerializer, sessionRepository }) { +async function createSession(request, h, dependencies = { sessionSerializer, sessionRepository }) { const userId = request.auth.credentials.userId; const certificationCenterId = request.params.certificationCenterId; const { address, room, date, time, examiner, description } = request.payload.data.attributes; @@ -19,6 +19,7 @@ async function createSession(request, _h, dependencies = { sessionSerializer, se examiner, description, }); + const session = await dependencies.sessionRepository.get({ id: newSessionId }); return dependencies.sessionSerializer.serialize(session); @@ -31,6 +32,10 @@ async function update(request, h, dependencies = { sessionSerializer, sessionRep await usecases.updateSession({ address, room, date, time, examiner, description, sessionId }); const updatedSession = await dependencies.sessionRepository.get({ id: sessionId }); + if (!updatedSession) { + return h.response().code(404); + } + return dependencies.sessionSerializer.serialize(updatedSession); } @@ -45,6 +50,11 @@ async function remove(request, h) { async function get(request, h, dependencies = { sessionSerializer, sessionRepository }) { const sessionId = request.params.sessionId; const session = await dependencies.sessionRepository.get({ id: sessionId }); + + if (!session) { + return h.response().code(404); + } + return dependencies.sessionSerializer.serialize(session); } diff --git a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js index a3812994728..7c941a0ec81 100644 --- a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js +++ b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js @@ -10,6 +10,7 @@ import { CertificationCandidateByPersonalInfoTooManyMatchesError, CertificationCandidatesError, + NotFoundError, } from '../../../../shared/domain/errors.js'; import { mailCheck as mailCheckImplementation } from '../../../../shared/mail/infrastructure/services/mail-check.js'; import { CERTIFICATION_CANDIDATES_ERRORS } from '../../../shared/domain/constants/certification-candidates-errors.js'; @@ -40,11 +41,16 @@ export async function addCandidateToSession({ candidate.sessionId = sessionId; const sessionAuthorization = await sessionAuthorizationAdapter.find({ sessionId }); + if (!sessionAuthorization) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } + if (!sessionAuthorization.canEnrollCandidateIndividually) { throw new CannotEnrollCandidateIndividuallyError(); } const session = await sessionRepository.get({ id: sessionId }); + try { candidate.validate({ isSco: session.isSco }); } catch (error) { diff --git a/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js b/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js index f28df4bcfea..c437bcbde8b 100644 --- a/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js +++ b/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js @@ -6,7 +6,7 @@ * @typedef {import('./index.js').SessionRepository} SessionRepository * @typedef {import('./index.js').EventAdapter} EventAdapter */ -import { ForbiddenAccess } from '../../../../shared/domain/errors.js'; +import { ForbiddenAccess, NotFoundError } from '../../../../shared/domain/errors.js'; import { PromiseUtils } from '../../../../shared/infrastructure/utils/promise-utils.js'; import { SUBSCRIPTION_TYPES } from '../../../shared/domain/constants.js'; import { CannotEnrollScoCandidateError, UnknownCountryForStudentEnrolmentError } from '../errors.js'; @@ -38,6 +38,11 @@ export async function enrolStudentsToSession({ return; } const sessionAuthorization = await sessionAuthorizationAdapter.find({ sessionId }); + + if (!sessionAuthorization) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } + if (!sessionAuthorization.canEnrollScoCandidate) { throw new CannotEnrollScoCandidateError(); } diff --git a/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js b/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js index 4f1060417b3..b19826757b7 100644 --- a/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js +++ b/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js @@ -2,6 +2,7 @@ * @typedef {import('./index.js').SessionRepository} SessionRepository * @typedef {import('./index.js').CenterRepository} CenterRepository */ +import { NotFoundError } from '../../../../shared/domain/errors.js'; import { Candidate } from '../models/Candidate.js'; /** * @param {object} params @@ -10,6 +11,11 @@ import { Candidate } from '../models/Candidate.js'; */ export async function getCandidateImportSheetData({ sessionId, sessionRepository, centerRepository }) { const session = await sessionRepository.get({ id: sessionId }); + + if (!session) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } + const enrolledCandidates = session.certificationCandidates.sort(Candidate.sortByLastNameAndFirstName); const center = await centerRepository.getById({ id: session.certificationCenterId }); return { diff --git a/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js b/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js index 96a48e16544..674c9bcad16 100644 --- a/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js +++ b/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js @@ -5,7 +5,7 @@ */ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; -import { CandidateAlreadyLinkedToUserError } from '../../../../shared/domain/errors.js'; +import { CandidateAlreadyLinkedToUserError, NotFoundError } from '../../../../shared/domain/errors.js'; /** * @param {object} params @@ -28,11 +28,17 @@ export async function importCertificationCandidatesFromCandidatesImportSheet({ certificationCpfService, }) { const sessionAuthorization = await sessionAuthorizationAdapter.find({ sessionId }); + + if (!sessionAuthorization) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } + if (!sessionAuthorization.canEnrollCandidateViaODS) { throw new CandidateAlreadyLinkedToUserError('At least one candidate is already linked to a user'); } const session = await sessionRepository.get({ id: sessionId }); + const candidates = await certificationCandidatesOdsService.extractCertificationCandidatesFromCandidatesImportSheet({ i18n, session, diff --git a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js index 6adcdc43318..84854ce0155 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js @@ -9,7 +9,6 @@ import { SessionEnrolment } from '../../domain/models/SessionEnrolment.js'; * @param {object} params * @param {number} params.id * @returns {Promise} - * @throws {NotFoundError} */ export async function get({ id }) { const knexConn = DomainTransaction.getConnection(); @@ -69,8 +68,9 @@ export async function get({ id }) { .join('certification-centers', 'certification-centers.id', 'sessions.certificationCenterId') .where('sessions.id', id) .first(); + if (!foundSession) { - throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + return null; } const certificationCandidates = diff --git a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js index cffe7bf8e4a..967bd313e4c 100644 --- a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js +++ b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js @@ -93,7 +93,7 @@ describe('Integration | Repository | certification | enrolment | SessionEnrolmen expect(actualSession).to.deepEqualInstance(expectedSession); }); - it('should return a Not found error when no session was found', async function () { + it('returns null when no session was found', async function () { domainBuilder.certification.enrolment .sessionEnrolmentBuilder() .createdBy({ @@ -116,11 +116,12 @@ describe('Integration | Repository | certification | enrolment | SessionEnrolmen }) .insertToDB({ databaseBuilder }); await databaseBuilder.commit(); + // when - const error = await catchErr(sessionRepository.get)({ id: 777 }); + const session = await sessionRepository.get({ id: 777 }); // then - expect(error).to.be.instanceOf(NotFoundError); + expect(session).to.be.null; }); }); diff --git a/api/tests/certification/enrolment/unit/application/session-controller_test.js b/api/tests/certification/enrolment/unit/application/session-controller_test.js index 5d99f7c855d..c2e4509746b 100644 --- a/api/tests/certification/enrolment/unit/application/session-controller_test.js +++ b/api/tests/certification/enrolment/unit/application/session-controller_test.js @@ -96,6 +96,39 @@ describe('Certification | Enrolment | Unit | Application | Controller | session- // then expect(response).to.equal('json'); }); + + context('when the updated session cannot be retrieved', function () { + it('should return a 404 response', async function () { + // given + const request = { + auth: { credentials: { userId: 1 } }, + params: { sessionId: 345 }, + payload: { + data: { + attributes: { + address: '1 rue des lauriers', + room: '2B', + date: '2021-01-01', + time: '14:00', + examiner: 'Louise', + description: 'coucou', + }, + }, + }, + }; + sinon.stub(usecases, 'updateSession').resolves(); + const sessionSerializer = { serialize: sinon.stub() }; + const sessionRepository = { get: sinon.stub() }; + sessionRepository.get.withArgs({ id: 345 }).resolves(null); + + // when + const response = await sessionController.update(request, hFake, { sessionSerializer, sessionRepository }); + + // then + expect(response.statusCode).to.equal(404); + expect(sessionSerializer.serialize).to.not.have.been.called; + }); + }); }); describe('#delete', function () { @@ -142,6 +175,26 @@ describe('Certification | Enrolment | Unit | Application | Controller | session- // then expect(response).to.equal('json'); }); + + context('when the session does not exist', function () { + it('should return a 404 response', async function () { + // given + const request = { + auth: { credentials: { userId: 1 } }, + params: { sessionId: 345 }, + }; + const sessionSerializer = { serialize: sinon.stub() }; + const sessionRepository = { get: sinon.stub() }; + sessionRepository.get.withArgs({ id: 345 }).resolves(null); + + // when + const response = await sessionController.get(request, hFake, { sessionSerializer, sessionRepository }); + + // then + expect(response.statusCode).to.equal(404); + expect(sessionSerializer.serialize).to.not.have.been.called; + }); + }); }); describe('#createCandidateParticipation', function () { diff --git a/api/tests/certification/enrolment/unit/domain/usecases/add-candidate-to-session_test.js b/api/tests/certification/enrolment/unit/domain/usecases/add-candidate-to-session_test.js index 696bf6f36d3..47b40c9d89c 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/add-candidate-to-session_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/add-candidate-to-session_test.js @@ -12,6 +12,7 @@ import { CERTIFICATION_CENTER_TYPES } from '../../../../../../src/shared/constan import { CertificationCandidateByPersonalInfoTooManyMatchesError, CertificationCandidatesError, + NotFoundError, } from '../../../../../../src/shared/domain/errors.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; import { catchErr, preventStubsToBeCalledUnexpectedly } from '../../../../../tooling/test-utils/error.js'; @@ -102,6 +103,27 @@ describe('Certification | Enrolment | Unit | UseCase | add-candidate-to-session' }; }); + context('when session is not found', function () { + it('throws a NotFoundError', async function () { + // given + candidateToEnroll = domainBuilder.certification.enrolment + .candidateBuilder() + .withParameters({ sessionId }) + .build(); + sessionAuthorizationAdapter.find.withArgs({ sessionId }).resolves(null); + + // when + const error = await catchErr(addCandidateToSession)({ + sessionId, + candidate: candidateToEnroll, + ...dependencies, + }); + + // then + expect(error).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + }); + }); + context('when session cannot enrol any candidate', function () { it('should throw a CertificationCandidateOnFinalizedSessionError', async function () { // given diff --git a/api/tests/certification/enrolment/unit/domain/usecases/enrol-students-to-session_test.js b/api/tests/certification/enrolment/unit/domain/usecases/enrol-students-to-session_test.js index f99528abe51..103b3a1b6cc 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/enrol-students-to-session_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/enrol-students-to-session_test.js @@ -8,7 +8,7 @@ import { import { Candidate } from '../../../../../../src/certification/enrolment/domain/models/Candidate.js'; import { enrolStudentsToSession } from '../../../../../../src/certification/enrolment/domain/usecases/enrol-students-to-session.js'; import { SUBSCRIPTION_TYPES } from '../../../../../../src/certification/shared/domain/constants.js'; -import { ForbiddenAccess } from '../../../../../../src/shared/domain/errors.js'; +import { ForbiddenAccess, NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; import { catchErr, preventStubsToBeCalledUnexpectedly } from '../../../../../tooling/test-utils/error.js'; @@ -101,6 +101,21 @@ describe('Certification | Enrolment | Unit | UseCase | enrol-students-to-session }); }); + context('when the session does not exist', function () { + it('throws a NotFoundError', async function () { + sessionAuthorizationAdapter.find.withArgs({ sessionId }).resolves(null); + + const err = await catchErr(enrolStudentsToSession)({ + ...dependencies, + sessionId, + studentIds: [michelStudentData.id, jeannetteStudentData.id], + }); + + // then + expect(err).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + }); + }); + context('when adding candidate to session is not allowed', function () { it('throws a CannotEnrollScoCandidateError', async function () { sessionAuthorizationAdapter.find diff --git a/api/tests/certification/enrolment/unit/domain/usecases/get-candidate-import-sheet-data_test.js b/api/tests/certification/enrolment/unit/domain/usecases/get-candidate-import-sheet-data_test.js index 3067c6bb500..7b58df7a808 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/get-candidate-import-sheet-data_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/get-candidate-import-sheet-data_test.js @@ -4,7 +4,9 @@ import sinon from 'sinon'; import { getCandidateImportSheetData } from '../../../../../../src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js'; import { Frameworks } from '../../../../../../src/certification/shared/domain/models/Frameworks.js'; import { CERTIFICATION_CENTER_TYPES } from '../../../../../../src/shared/constants.js'; +import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; +import { catchErr } from '../../../../../tooling/test-utils/error.js'; describe('Certification | Enrolment | Unit | UseCase | get-candidate-import-sheet-data', function () { let sessionRepository; @@ -19,6 +21,26 @@ describe('Certification | Enrolment | Unit | UseCase | get-candidate-import-shee }; }); + describe('when the session does not exist', function () { + it('throws a NotFoundError', async function () { + // given + const userId = 123; + const sessionId = 456; + sessionRepository.get.withArgs({ id: sessionId }).resolves(null); + + // when + const error = await catchErr(getCandidateImportSheetData)({ + userId, + sessionId, + sessionRepository, + centerRepository, + }); + + // then + expect(error).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + }); + }); + it('should get a session with candidates and the certification center habilitations', async function () { // given const userId = 123; diff --git a/api/tests/certification/enrolment/unit/domain/usecases/import-certification-candidates-from-candidates-import-sheet_test.js b/api/tests/certification/enrolment/unit/domain/usecases/import-certification-candidates-from-candidates-import-sheet_test.js index 9ea62d311a8..20e857f0af7 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/import-certification-candidates-from-candidates-import-sheet_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/import-certification-candidates-from-candidates-import-sheet_test.js @@ -5,7 +5,7 @@ import { importCertificationCandidatesFromCandidatesImportSheet } from '../../.. import { Frameworks } from '../../../../../../src/certification/shared/domain/models/Frameworks.js'; import { CERTIFICATION_CENTER_TYPES } from '../../../../../../src/shared/constants.js'; import { DomainTransaction } from '../../../../../../src/shared/domain/DomainTransaction.js'; -import { CandidateAlreadyLinkedToUserError } from '../../../../../../src/shared/domain/errors.js'; +import { CandidateAlreadyLinkedToUserError, NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { getI18n } from '../../../../../../src/shared/infrastructure/i18n/i18n.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; import { catchErr } from '../../../../../tooling/test-utils/error.js'; @@ -65,6 +65,26 @@ describe('Unit | UseCase | import-certification-candidates-from-attendance-sheet }); describe('#importCertificationCandidatesFromCandidatesImportSheet', function () { + context('when session does not exist', function () { + it('throws a NotFoundError', async function () { + // given + const sessionId = 'sessionId'; + const odsBuffer = 'buffer'; + sessionAuthorizationAdapter.find.withArgs({ sessionId }).resolves(null); + + // when + const error = await catchErr(importCertificationCandidatesFromCandidatesImportSheet)({ + i18n, + sessionId, + odsBuffer, + ...dependencies, + }); + + // then + expect(error).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + }); + }); + context('when session cannot enrolled candidates', function () { it('should throw a BadRequestError', async function () { // given From f96010d81171fa280f29fb071d45593c93612c4e Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Fri, 14 Aug 2026 14:53:47 +0200 Subject: [PATCH 4/5] refactor(api): remove thrown NotFoundError from sessionRepository.remove --- .../domain/usecases/delete-session.js | 7 +++- .../repositories/session-repository.js | 11 ++++--- .../domain/usecases/unfinalize-session.js | 8 ++++- .../repositories/session-repository_test.js | 11 +++---- .../domain/usecases/delete-session_test.js | 25 +++++++++++++++ .../usecases/unfinalize-session_test.js | 32 +++++++++++++++++++ 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/api/src/certification/enrolment/domain/usecases/delete-session.js b/api/src/certification/enrolment/domain/usecases/delete-session.js index 35d8914a190..29de8bc9ba2 100644 --- a/api/src/certification/enrolment/domain/usecases/delete-session.js +++ b/api/src/certification/enrolment/domain/usecases/delete-session.js @@ -1,4 +1,5 @@ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; +import { NotFoundError } from '../../../../shared/domain/errors.js'; import { SessionStartedDeletionError } from '../errors.js'; /** @@ -17,7 +18,11 @@ const deleteSession = async ({ sessionId, sessionRepository, sessionManagementRe } await DomainTransaction.execute(async () => { - await sessionRepository.remove({ id: sessionId }); + const deletedSession = await sessionRepository.remove({ id: sessionId }); + + if (!deletedSession) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } }); }; diff --git a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js index 84854ce0155..7ff2bfd0d6f 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js @@ -1,5 +1,4 @@ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; -import { NotFoundError } from '../../../../shared/domain/errors.js'; import { AlgorithmEngineVersion } from '../../../shared/domain/models/AlgorithmEngineVersion.js'; import { Candidate } from '../../domain/models/Candidate.js'; import { SessionEnrolment } from '../../domain/models/SessionEnrolment.js'; @@ -190,13 +189,17 @@ export async function updateInfo({ id, address, room, examiner, date, time, desc * @function * @param {object} params * @param {number} params.id - * @returns {Promise} - * @throws {NotFoundError} + * @returns {Promise} the number of deleted sessions, or null when no session was found */ export async function remove({ id }) { const knexConn = DomainTransaction.getConnection(); await knexConn('invigilator_accesses').where({ sessionId: id }).del(); await knexConn('certification-candidates').where({ sessionId: id }).del(); const nbSessionsDeleted = await knexConn('sessions').where({ id }).del(); - if (nbSessionsDeleted === 0) throw new NotFoundError(); + + if (nbSessionsDeleted === 0) { + return null; + } + + return nbSessionsDeleted; } diff --git a/api/src/certification/session-management/domain/usecases/unfinalize-session.js b/api/src/certification/session-management/domain/usecases/unfinalize-session.js index 89e408c16fd..20ad505b463 100644 --- a/api/src/certification/session-management/domain/usecases/unfinalize-session.js +++ b/api/src/certification/session-management/domain/usecases/unfinalize-session.js @@ -4,6 +4,7 @@ */ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; +import { NotFoundError } from '../../../../shared/domain/errors.js'; import { SessionAlreadyPublishedError } from '../errors.js'; /** @@ -17,7 +18,12 @@ const unfinalizeSession = async function ({ sessionId, sessionManagementReposito } return DomainTransaction.execute(async () => { - await finalizedSessionRepository.remove({ sessionId }); + const nbFinalizedSessionsRemoved = await finalizedSessionRepository.remove({ sessionId }); + + if (!nbFinalizedSessionsRemoved) { + throw new NotFoundError("La session n'existe pas ou son accès est restreint"); + } + await sessionManagementRepository.unfinalize({ id: sessionId }); }); }; diff --git a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js index 967bd313e4c..19ed329f536 100644 --- a/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js +++ b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js @@ -1,11 +1,9 @@ import * as sessionRepository from '../../../../../../src/certification/enrolment/infrastructure/repositories/session-repository.js'; import { BILLING_MODES } from '../../../../../../src/certification/shared/domain/constants.js'; import { SCOPES } from '../../../../../../src/certification/shared/domain/models/Scopes.js'; -import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { expect } from '../../../../../test-helper.js'; import { databaseBuilder, knex } from '../../../../../tooling/databases.js'; import { domainBuilder } from '../../../../../tooling/domain-builder/domain-builder.js'; -import { catchErr } from '../../../../../tooling/test-utils/error.js'; describe('Integration | Repository | certification | enrolment | SessionEnrolment', function () { describe('#get', function () { @@ -287,25 +285,26 @@ describe('Integration | Repository | certification | enrolment | SessionEnrolmen await databaseBuilder.commit(); // when - await sessionRepository.remove({ id: sessionId }); + const result = await sessionRepository.remove({ id: sessionId }); // then const foundSession = await knex('sessions').select('id').where({ id: sessionId }).first(); expect(foundSession).to.be.undefined; + expect(result).to.equal(1); }); }); }); context('when session does not exist', function () { - it('should throw a not found error', async function () { + it('return null', async function () { // given const sessionId = 123456; // when - const error = await catchErr(sessionRepository.remove)({ id: sessionId }); + const result = await sessionRepository.remove({ id: sessionId }); // then - expect(error).to.be.instanceOf(NotFoundError); + expect(result).to.be.null; }); }); }); diff --git a/api/tests/certification/enrolment/unit/domain/usecases/delete-session_test.js b/api/tests/certification/enrolment/unit/domain/usecases/delete-session_test.js index 0d2d022aa11..99029c6345f 100644 --- a/api/tests/certification/enrolment/unit/domain/usecases/delete-session_test.js +++ b/api/tests/certification/enrolment/unit/domain/usecases/delete-session_test.js @@ -3,6 +3,7 @@ import sinon from 'sinon'; import { SessionStartedDeletionError } from '../../../../../../src/certification/enrolment/domain/errors.js'; import { deleteSession } from '../../../../../../src/certification/enrolment/domain/usecases/delete-session.js'; import { DomainTransaction } from '../../../../../../src/shared/domain/DomainTransaction.js'; +import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { expect } from '../../../../../test-helper.js'; import { catchErr } from '../../../../../tooling/test-utils/error.js'; @@ -16,6 +17,7 @@ describe('Unit | UseCase | delete-session', function () { const sessionRepository = { remove: sinon.stub() }; const sessionManagementRepository = { hasNoStartedCertification: sinon.stub() }; sessionManagementRepository.hasNoStartedCertification.resolves(true); + sessionRepository.remove.withArgs({ id: 123 }).resolves(1); // when await deleteSession({ @@ -29,6 +31,29 @@ describe('Unit | UseCase | delete-session', function () { }); }); + context('when session does not exist', function () { + it('throw a NotFoundError', async function () { + // given + const sessionId = 123; + const sessionRepository = { remove: sinon.stub() }; + const sessionManagementRepository = { + hasNoStartedCertification: sinon.stub(), + }; + sessionManagementRepository.hasNoStartedCertification.resolves(true); + sessionRepository.remove.withArgs({ id: sessionId }).resolves(null); + + // when + const error = await catchErr(deleteSession)({ + sessionId, + sessionRepository, + sessionManagementRepository, + }); + + // then + expect(error).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + }); + }); + context('when there are certification courses', function () { it('should throw SessionStartedDeletionError error', async function () { // given diff --git a/api/tests/certification/session-management/unit/domain/usecases/unfinalize-session_test.js b/api/tests/certification/session-management/unit/domain/usecases/unfinalize-session_test.js index b417bf9f54e..2cfca52f628 100644 --- a/api/tests/certification/session-management/unit/domain/usecases/unfinalize-session_test.js +++ b/api/tests/certification/session-management/unit/domain/usecases/unfinalize-session_test.js @@ -3,6 +3,7 @@ import sinon from 'sinon'; import { SessionAlreadyPublishedError } from '../../../../../../src/certification/session-management/domain/errors.js'; import { unfinalizeSession } from '../../../../../../src/certification/session-management/domain/usecases/unfinalize-session.js'; import { DomainTransaction } from '../../../../../../src/shared/domain/DomainTransaction.js'; +import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; import { expect } from '../../../../../test-helper.js'; import { catchErr } from '../../../../../tooling/test-utils/error.js'; @@ -10,6 +11,36 @@ describe('Unit | UseCase | unfinalize-session', function () { let sessionManagementRepository; let finalizedSessionRepository; + describe('when session does not exist', function () { + it('throws a NotFoundError', async function () { + // given + sinon.stub(DomainTransaction, 'execute').callsFake((fn) => fn({})); + + const sessionId = 123; + sessionManagementRepository = { + unfinalize: sinon.stub(), + isPublished: sinon.stub(), + }; + finalizedSessionRepository = { + remove: sinon.stub(), + }; + + sessionManagementRepository.isPublished.withArgs({ id: sessionId }).resolves(false); + finalizedSessionRepository.remove.withArgs({ sessionId }).resolves(null); + + // when + const error = await catchErr(unfinalizeSession)({ + sessionId, + sessionManagementRepository, + finalizedSessionRepository, + }); + + // then + expect(error).to.deepEqualInstance(new NotFoundError("La session n'existe pas ou son accès est restreint")); + expect(sessionManagementRepository.unfinalize).to.not.have.been.called; + }); + }); + describe('when session is not published', function () { it('should call repositories with transaction', async function () { // given @@ -22,6 +53,7 @@ describe('Unit | UseCase | unfinalize-session', function () { finalizedSessionRepository = { remove: sinon.stub(), }; + finalizedSessionRepository.remove.withArgs({ sessionId: 99 }).resolves(1); // when await unfinalizeSession({ sessionId: 99, sessionManagementRepository, finalizedSessionRepository }); From 8688229c3f639fd2dc4774919acd18f255eba6b1 Mon Sep 17 00:00:00 2001 From: Alexandre COIN Date: Fri, 14 Aug 2026 15:41:40 +0200 Subject: [PATCH 5/5] refactor(api): update JSDoc Co-authored-by: Claude --- .../enrolment/domain/usecases/add-candidate-to-session.js | 1 + .../certification/enrolment/domain/usecases/delete-session.js | 2 ++ .../enrolment/domain/usecases/enrol-students-to-session.js | 1 + .../enrolment/domain/usecases/get-attendance-sheet.js | 1 + .../domain/usecases/get-candidate-import-sheet-data.js | 1 + ...rt-certification-candidates-from-candidates-import-sheet.js | 1 + .../repositories/session-for-attendance-sheet-repository.js | 3 +-- .../infrastructure/repositories/session-repository.js | 2 +- .../session-management/domain/usecases/unfinalize-session.js | 2 ++ 9 files changed, 11 insertions(+), 3 deletions(-) diff --git a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js index 7c941a0ec81..f0024b1e3e0 100644 --- a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js +++ b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js @@ -24,6 +24,7 @@ import { CannotEnrollCandidateIndividuallyError } from '../errors.js'; * @param {CertificationCpfCountryRepository} params.certificationCpfCountryRepository * @param {CertificationCpfCityRepository} params.certificationCpfCityRepository * @param {EventAdapter} params.eventAdapter + * @throws {NotFoundError} the session does not exist or its access is restricted */ export async function addCandidateToSession({ sessionId, diff --git a/api/src/certification/enrolment/domain/usecases/delete-session.js b/api/src/certification/enrolment/domain/usecases/delete-session.js index 29de8bc9ba2..95b7d19bd2f 100644 --- a/api/src/certification/enrolment/domain/usecases/delete-session.js +++ b/api/src/certification/enrolment/domain/usecases/delete-session.js @@ -11,6 +11,8 @@ import { SessionStartedDeletionError } from '../errors.js'; * @param {object} params * @param {SessionRepository} params.sessionRepository * @param {SessionManagementRepository} params.sessionManagementRepository + * @throws {SessionStartedDeletionError} the session has already started + * @throws {NotFoundError} the session does not exist or its access is restricted */ const deleteSession = async ({ sessionId, sessionRepository, sessionManagementRepository }) => { if (!(await sessionManagementRepository.hasNoStartedCertification({ id: sessionId }))) { diff --git a/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js b/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js index c437bcbde8b..a045774acae 100644 --- a/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js +++ b/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js @@ -21,6 +21,7 @@ const INSEE_PREFIX_CODE = '99'; * @param {CountryRepository} params.countryRepository * @param {EventAdapter} params.eventAdapter * @param {SessionAuthorizationAdapter} params.sessionAuthorizationAdapter + * @throws {NotFoundError} the session does not exist or its access is restricted */ export async function enrolStudentsToSession({ sessionId, diff --git a/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js index 780fa022933..cc1a8a003fc 100644 --- a/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js +++ b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js @@ -9,6 +9,7 @@ import { NotFoundError } from '../../../../shared/domain/errors.js'; * @param {object} params * @param {SessionForAttendanceSheetRepository} params.sessionForAttendanceSheetRepository * @param {AttendanceSheetPdfUtils} params.attendanceSheetPdfUtils + * @throws {NotFoundError} the session does not exist or no candidate is enrolled in it */ const getAttendanceSheet = async function ({ sessionId, diff --git a/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js b/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js index b19826757b7..725542a363a 100644 --- a/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js +++ b/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js @@ -8,6 +8,7 @@ import { Candidate } from '../models/Candidate.js'; * @param {object} params * @param {SessionRepository} params.sessionRepository * @param {CenterRepository} params.centerRepository + * @throws {NotFoundError} the session does not exist or its access is restricted */ export async function getCandidateImportSheetData({ sessionId, sessionRepository, centerRepository }) { const session = await sessionRepository.get({ id: sessionId }); diff --git a/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js b/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js index 674c9bcad16..474d8f456b1 100644 --- a/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js +++ b/api/src/certification/enrolment/domain/usecases/import-certification-candidates-from-candidates-import-sheet.js @@ -12,6 +12,7 @@ import { CandidateAlreadyLinkedToUserError, NotFoundError } from '../../../../sh * @param {CandidateRepository} params.candidateRepository * @param {SessionRepository} params.sessionRepository * @param {EventAdapter} params.eventAdapter + * @throws {NotFoundError} the session does not exist or its access is restricted */ export async function importCertificationCandidatesFromCandidatesImportSheet({ sessionId, diff --git a/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js index b39de188842..dff225facee 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/session-for-attendance-sheet-repository.js @@ -6,8 +6,7 @@ import { SessionForAttendanceSheet } from '../../domain/read-models/SessionForAt * @function * @param {object} params * @param {number} params.id - * @returns {Promise} - * @throws {NotFoundError} + * @returns {Promise} the session with its candidates, or null when the session does not exist or has no enrolled candidate */ export async function getWithCertificationCandidates({ id }) { const knexConn = DomainTransaction.getConnection(); diff --git a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js index 7ff2bfd0d6f..9fe1a0d0a4a 100644 --- a/api/src/certification/enrolment/infrastructure/repositories/session-repository.js +++ b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js @@ -7,7 +7,7 @@ import { SessionEnrolment } from '../../domain/models/SessionEnrolment.js'; * @function * @param {object} params * @param {number} params.id - * @returns {Promise} + * @returns {Promise} the session, or null when no session was found */ export async function get({ id }) { const knexConn = DomainTransaction.getConnection(); diff --git a/api/src/certification/session-management/domain/usecases/unfinalize-session.js b/api/src/certification/session-management/domain/usecases/unfinalize-session.js index 20ad505b463..6efd4237a76 100644 --- a/api/src/certification/session-management/domain/usecases/unfinalize-session.js +++ b/api/src/certification/session-management/domain/usecases/unfinalize-session.js @@ -11,6 +11,8 @@ import { SessionAlreadyPublishedError } from '../errors.js'; * @param {object} params * @param {SessionManagementRepository} params.sessionManagementRepository * @param {FinalizedSessionRepository} params.finalizedSessionRepository + * @throws {SessionAlreadyPublishedError} the session is already published + * @throws {NotFoundError} the finalized session does not exist or its access is restricted */ const unfinalizeSession = async function ({ sessionId, sessionManagementRepository, finalizedSessionRepository }) { if (await sessionManagementRepository.isPublished({ id: sessionId })) {