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..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 @@ -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'; @@ -23,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, @@ -40,11 +42,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/delete-session.js b/api/src/certification/enrolment/domain/usecases/delete-session.js index 35d8914a190..95b7d19bd2f 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'; /** @@ -10,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 }))) { @@ -17,7 +20,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/domain/usecases/enrol-students-to-session.js b/api/src/certification/enrolment/domain/usecases/enrol-students-to-session.js index f28df4bcfea..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 @@ -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'; @@ -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, @@ -38,6 +39,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-attendance-sheet.js b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js index 2ec9adfe83a..cc1a8a003fc 100644 --- a/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js +++ b/api/src/certification/enrolment/domain/usecases/get-attendance-sheet.js @@ -3,10 +3,13 @@ * @typedef {import('./index.js').AttendanceSheetPdfUtils} AttendanceSheetPdfUtils */ +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, @@ -16,6 +19,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/domain/usecases/get-candidate-import-sheet-data.js b/api/src/certification/enrolment/domain/usecases/get-candidate-import-sheet-data.js index 4f1060417b3..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 @@ -2,14 +2,21 @@ * @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 * @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 }); + + 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..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 @@ -5,13 +5,14 @@ */ 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 * @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, @@ -28,11 +29,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/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/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..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 @@ -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'; @@ -7,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(); @@ -59,7 +57,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/src/certification/enrolment/infrastructure/repositories/session-repository.js b/api/src/certification/enrolment/infrastructure/repositories/session-repository.js index 6adcdc43318..9fe1a0d0a4a 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'; @@ -8,8 +7,7 @@ import { SessionEnrolment } from '../../domain/models/SessionEnrolment.js'; * @function * @param {object} params * @param {number} params.id - * @returns {Promise} - * @throws {NotFoundError} + * @returns {Promise} the session, or null when no session was found */ export async function get({ id }) { const knexConn = DomainTransaction.getConnection(); @@ -69,8 +67,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 = @@ -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..6efd4237a76 100644 --- a/api/src/certification/session-management/domain/usecases/unfinalize-session.js +++ b/api/src/certification/session-management/domain/usecases/unfinalize-session.js @@ -4,12 +4,15 @@ */ import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; +import { NotFoundError } from '../../../../shared/domain/errors.js'; 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 })) { @@ -17,7 +20,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/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); }); }); 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/integration/infrastructure/repositories/session-repository_test.js b/api/tests/certification/enrolment/integration/infrastructure/repositories/session-repository_test.js index cffe7bf8e4a..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 () { @@ -93,7 +91,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 +114,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; }); }); @@ -286,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/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/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/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-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, }); 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 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 });