diff --git a/api/src/certification/session-management/application/cancellation-controller.js b/api/src/certification/session-management/application/cancellation-controller.js index 4c733e8ea25..266381d17ce 100644 --- a/api/src/certification/session-management/application/cancellation-controller.js +++ b/api/src/certification/session-management/application/cancellation-controller.js @@ -1,11 +1,10 @@ -import * as events from '../../../../src/shared/domain/events/index.js'; import { usecases } from '../domain/usecases/index.js'; -const cancel = async function (request, h, dependencies = { events }) { +const cancel = async function (request, h) { const juryId = request.auth.credentials.userId; const certificationCourseId = request.params.certificationCourseId; - const certificationCancelledEvent = await usecases.cancelCertificationCourse({ certificationCourseId, juryId }); - await dependencies.events.eventDispatcher.dispatch(certificationCancelledEvent); + + await usecases.cancel({ certificationCourseId, juryId }); return h.response().code(204); }; diff --git a/api/src/certification/session-management/domain/usecases/cancel-certification-course.js b/api/src/certification/session-management/domain/usecases/cancel.js similarity index 68% rename from api/src/certification/session-management/domain/usecases/cancel-certification-course.js rename to api/src/certification/session-management/domain/usecases/cancel.js index c6dbaa7e18c..acd85de5f21 100644 --- a/api/src/certification/session-management/domain/usecases/cancel-certification-course.js +++ b/api/src/certification/session-management/domain/usecases/cancel.js @@ -1,6 +1,7 @@ /** * @typedef {import('./index.js'.CertificationCourseRepository} CertificationCourseRepository * @typedef {import('./index.js'.SessionRepository} SessionRepository + * @typedef {import('./index.js'.CertificationRescoringRepository} CertificationRescoringRepository */ import CertificationCancelled from '../../../../../src/shared/domain/events/CertificationCancelled.js'; @@ -11,13 +12,14 @@ import { NotFinalizedSessionError } from '../../../../shared/domain/errors.js'; * @param {number} params.certificationCourseId * @param {CertificationCourseRepository} params.certificationCourseRepository * @param {SessionRepository} params.sessionRepository - * @returns {Promise} + * @param {CertificationRescoringRepository} params.certificationRescoringRepository */ -export const cancelCertificationCourse = async function ({ +export const cancel = async function ({ certificationCourseId, juryId, certificationCourseRepository, sessionRepository, + certificationRescoringRepository, }) { const certificationCourse = await certificationCourseRepository.get({ id: certificationCourseId }); const session = await sessionRepository.get({ id: certificationCourse.getSessionId() }); @@ -28,5 +30,10 @@ export const cancelCertificationCourse = async function ({ certificationCourse.cancel(); await certificationCourseRepository.update({ certificationCourse }); - return new CertificationCancelled({ certificationCourseId: certificationCourse.getId(), juryId }); + const certificationCancelledEvent = new CertificationCancelled({ + certificationCourseId: certificationCourse.getId(), + juryId, + }); + + return certificationRescoringRepository.execute({ certificationCancelledEvent }); }; diff --git a/api/src/certification/session-management/infrastructure/repositories/certification-rescoring-repository.js b/api/src/certification/session-management/infrastructure/repositories/certification-rescoring-repository.js new file mode 100644 index 00000000000..dc8df7ca661 --- /dev/null +++ b/api/src/certification/session-management/infrastructure/repositories/certification-rescoring-repository.js @@ -0,0 +1,15 @@ +/** + * @typedef {import('../../../../../src/shared/domain/events/CertificationCancelled.js'} CertificationCancelled + * @typedef {import('./index.js'.LibServices} LibServices + */ + +/** + * @param {Object} params + * @param {CertificationCancelled} params.certificationCancelledEvent + * @param {LibServices} params.libServices + */ +export const execute = async ({ certificationCancelledEvent, libServices }) => { + return libServices.handleCertificationRescoring({ + event: certificationCancelledEvent, + }); +}; diff --git a/api/src/certification/session-management/infrastructure/repositories/index.js b/api/src/certification/session-management/infrastructure/repositories/index.js index f3ec77be661..d4e9159095c 100644 --- a/api/src/certification/session-management/infrastructure/repositories/index.js +++ b/api/src/certification/session-management/infrastructure/repositories/index.js @@ -1,3 +1,4 @@ +import { handlersAsServices as libServices } from '../../../../../src/shared/domain/events/index.js'; import * as certificationIssueReportRepository from '../../../../certification/shared/infrastructure/repositories/certification-issue-report-repository.js'; import * as issueReportCategoryRepository from '../../../../certification/shared/infrastructure/repositories/issue-report-category-repository.js'; import * as answerRepository from '../../../../shared/infrastructure/repositories/answer-repository.js'; @@ -21,6 +22,7 @@ import * as certificationCandidateRepository from './certification-candidate-rep import * as certificationCompanionAlertRepository from './certification-companion-alert-repository.js'; import * as certificationOfficerRepository from './certification-officer-repository.js'; import * as certificationRepository from './certification-repository.js'; +import * as certificationRescoringRepository from './certification-rescoring-repository.js'; import * as competenceMarkRepository from './competence-mark-repository.js'; import * as courseAssessmentResultRepository from './course-assessment-result-repository.js'; import * as cpfExportRepository from './cpf-export-repository.js'; @@ -75,6 +77,7 @@ import * as v3CertificationCourseDetailsForAdministrationRepository from './v3-c * @typedef {juryCertificationSummaryRepository} JuryCertificationSummaryRepository * @typedef {certificationCandidateRepository} CertificationCandidateRepository * @typedef {typeof certificationCompanionAlertRepository} CertificationCompanionAlertRepository + * @typedef {certificationRescoringRepository} CertificationRescoringRepository */ const repositoriesWithoutInjectedDependencies = { assessmentRepository, @@ -109,13 +112,17 @@ const repositoriesWithoutInjectedDependencies = { certificationCpfCountryRepository, certificationCandidateRepository, certificationCompanionAlertRepository, + certificationRescoringRepository, }; /** * Using {@link https://jsdoc.app/tags-type "Closure Compiler's syntax"} to document injected dependencies - * + * @typedef {libServices} LibServices */ -const dependencies = {}; +const dependencies = { + libServices, +}; + const sessionRepositories = injectDependencies(repositoriesWithoutInjectedDependencies, dependencies); export { answerRepository, diff --git a/api/src/shared/domain/events/index.js b/api/src/shared/domain/events/index.js index 8cd39b2b185..dc6e54bb578 100644 --- a/api/src/shared/domain/events/index.js +++ b/api/src/shared/domain/events/index.js @@ -40,11 +40,14 @@ import * as competenceRepository from '../../infrastructure/repositories/compete import * as knowledgeElementRepository from '../../infrastructure/repositories/knowledge-element-repository.js'; import * as organizationRepository from '../../infrastructure/repositories/organization-repository.js'; import * as skillRepository from '../../infrastructure/repositories/skill-repository.js'; -import { injectDefaults } from '../../infrastructure/utils/dependency-injection.js'; +import { injectDefaults, injectDependencies } from '../../infrastructure/utils/dependency-injection.js'; import { logger } from '../../infrastructure/utils/logger.js'; const { performance } = perf_hooks; +/** + * @typedef {certificationAssessmentRepository} CertificationAssessmentRepository + */ const dependencies = { answerRepository, assessmentRepository, @@ -119,4 +122,10 @@ const _forTestOnly = { }, }; -export { _forTestOnly, eventBus, eventDispatcher }; +/** + * Using {@link https://jsdoc.app/tags-type "Closure Compiler's syntax"} to document injected dependencies + * @typedef {handleCertificationRescoring} HandleCertificationRescoring + */ +const handlersAsServices = injectDependencies(handlersToBeInjected, dependencies); + +export { _forTestOnly, eventBus, eventDispatcher, handlersAsServices }; diff --git a/api/tests/certification/session-management/acceptance/application/cancellation-route_test.js b/api/tests/certification/session-management/acceptance/application/cancellation-route_test.js index eb7783719ae..c21c9d48215 100644 --- a/api/tests/certification/session-management/acceptance/application/cancellation-route_test.js +++ b/api/tests/certification/session-management/acceptance/application/cancellation-route_test.js @@ -1,8 +1,8 @@ import { PIX_ADMIN } from '../../../../../src/authorization/domain/constants.js'; import { AlgorithmEngineVersion } from '../../../../../src/certification/shared/domain/models/AlgorithmEngineVersion.js'; import { SESSIONS_VERSIONS } from '../../../../../src/certification/shared/domain/models/SessionVersion.js'; -import { Assessment } from '../../../../../src/shared/domain/models/Assessment.js'; -import { AssessmentResult } from '../../../../../src/shared/domain/models/AssessmentResult.js'; +import { Assessment } from '../../../../../src/shared/domain/models/index.js'; +import { AssessmentResult } from '../../../../../src/shared/domain/models/index.js'; import { AnswerStatus } from '../../../../../src/shared/domain/models/index.js'; import { createServer, diff --git a/api/tests/certification/session-management/integration/infrastructure/repositories/certification-rescoring-repository_test.js b/api/tests/certification/session-management/integration/infrastructure/repositories/certification-rescoring-repository_test.js new file mode 100644 index 00000000000..27b84bdd6f7 --- /dev/null +++ b/api/tests/certification/session-management/integration/infrastructure/repositories/certification-rescoring-repository_test.js @@ -0,0 +1,26 @@ +import CertificationCancelled from '../../../../../../lib/domain/events/CertificationCancelled.js'; +import { sessionRepositories } from '../../../../../../src/certification/session-management/infrastructure/repositories/index.js'; +import { NotFoundError } from '../../../../../../src/shared/domain/errors.js'; +import { catchErr, expect } from '../../../../../test-helper.js'; + +describe('Integration | Repository | certification-rescoring-repository', function () { + describe('#execute', function () { + it('should trigger a rescoring', async function () { + // given + const certificationCancelledEvent = new CertificationCancelled({ certificationCourseId: 444, juryId: 555 }); + sessionRepositories; + + // when + const error = await catchErr(sessionRepositories.certificationRescoringRepository.execute)({ + certificationCancelledEvent, + }); + + // then + expect(error).to.deepEqualInstance( + new NotFoundError( + `L'assessment de certification avec un certificationCourseId de ${certificationCancelledEvent.certificationCourseId} n'existe pas ou son accès est restreint`, + ), + ); + }); + }); +}); diff --git a/api/tests/certification/session-management/unit/application/cancellation-controller_test.js b/api/tests/certification/session-management/unit/application/cancellation-controller_test.js index 1bd1a4aa403..b2e232c8a51 100644 --- a/api/tests/certification/session-management/unit/application/cancellation-controller_test.js +++ b/api/tests/certification/session-management/unit/application/cancellation-controller_test.js @@ -5,9 +5,9 @@ import { expect, hFake, sinon } from '../../../../test-helper.js'; describe('Certification | Session-management | Unit | Application | Controller | cancellation', function () { describe('#cancel', function () { - it('should call cancel-certification-course usecase', async function () { + it('should call cancel usecase', async function () { // given - sinon.stub(usecases, 'cancelCertificationCourse'); + sinon.stub(usecases, 'cancel'); const request = { auth: { credentials: { @@ -18,43 +18,17 @@ describe('Certification | Session-management | Unit | Application | Controller | certificationCourseId: 123, }, }; - usecases.cancelCertificationCourse.resolves(); + usecases.cancel.resolves(); // when await cancellationController.cancel(request, hFake); // then - expect(usecases.cancelCertificationCourse).to.have.been.calledWithExactly({ + expect(usecases.cancel).to.have.been.calledWithExactly({ certificationCourseId: 123, juryId: 345, }); }); - - it('should fire a CertificationCancelled event', async function () { - // given - const certificationCourseId = 123; - const juryId = 456; - const events = { eventDispatcher: { dispatch: sinon.stub() } }; - const expectedEvent = new CertificationCancelled({ certificationCourseId, juryId }); - sinon.stub(usecases, 'cancelCertificationCourse'); - const request = { - auth: { - credentials: { - userId: 345, - }, - }, - params: { - certificationCourseId, - }, - }; - usecases.cancelCertificationCourse.resolves(expectedEvent); - - // when - await cancellationController.cancel(request, hFake, { events }); - - // then - expect(events.eventDispatcher.dispatch).to.have.been.calledWithExactly(expectedEvent); - }); }); describe('#uncancelCertificationCourse', function () { diff --git a/api/tests/certification/session-management/unit/domain/usecases/cancel-certification-course_test.js b/api/tests/certification/session-management/unit/domain/usecases/cancel_test.js similarity index 81% rename from api/tests/certification/session-management/unit/domain/usecases/cancel-certification-course_test.js rename to api/tests/certification/session-management/unit/domain/usecases/cancel_test.js index a6788a79799..b990b263328 100644 --- a/api/tests/certification/session-management/unit/domain/usecases/cancel-certification-course_test.js +++ b/api/tests/certification/session-management/unit/domain/usecases/cancel_test.js @@ -1,9 +1,9 @@ -import { cancelCertificationCourse } from '../../../../../../src/certification/session-management/domain/usecases/cancel-certification-course.js'; +import { cancel } from '../../../../../../src/certification/session-management/domain/usecases/cancel.js'; import { NotFinalizedSessionError } from '../../../../../../src/shared/domain/errors.js'; import CertificationCancelled from '../../../../../../src/shared/domain/events/CertificationCancelled.js'; import { catchErr, domainBuilder, expect, sinon } from '../../../../../test-helper.js'; -describe('Certification | Session-management | Unit | Domain | UseCases | cancel-certification-course', function () { +describe('Certification | Session-management | Unit | Domain | UseCases | cancel', function () { describe('when session is finalized', function () { it('should cancel the certification course', async function () { // given @@ -20,22 +20,32 @@ describe('Certification | Session-management | Unit | Domain | UseCases | cancel const sessionRepository = { get: sinon.stub(), }; + const certificationRescoringRepository = { + execute: sinon.stub(), + }; certificationCourseRepository.get.withArgs({ id: 123 }).resolves(certificationCourse); certificationCourseRepository.update.resolves(); + certificationRescoringRepository.execute.resolves(); sessionRepository.get.withArgs({ id: certificationCourse.getSessionId() }).resolves(session); // when - const cancelledEvent = await cancelCertificationCourse({ + await cancel({ certificationCourseId: 123, + juryId, certificationCourseRepository, sessionRepository, - juryId, + certificationRescoringRepository, }); // then expect(certificationCourse.cancel).to.have.been.calledOnce; expect(certificationCourseRepository.update).to.have.been.calledWithExactly({ certificationCourse }); - expect(cancelledEvent).to.deepEqualInstance(new CertificationCancelled({ certificationCourseId: 123, juryId })); + expect(certificationRescoringRepository.execute).to.have.been.calledWithExactly({ + certificationCancelledEvent: new CertificationCancelled({ + certificationCourseId: certificationCourse.getId(), + juryId, + }), + }); }); }); @@ -58,7 +68,7 @@ describe('Certification | Session-management | Unit | Domain | UseCases | cancel sessionRepository.get.withArgs({ id: certificationCourse.getSessionId() }).resolves(session); // when - const error = await catchErr(cancelCertificationCourse)({ + const error = await catchErr(cancel)({ certificationCourseId: 123, certificationCourseRepository, sessionRepository,