diff --git a/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js b/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js index ae380262527..28a17cf98ba 100644 --- a/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js +++ b/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js @@ -2,9 +2,12 @@ * @typedef {import('../index.js').AssessmentResultRepository} AssessmentResultRepository * @typedef {import('../index.js').CertificationCourseRepository} CertificationCourseRepository * @typedef {import('../index.js').CompetenceMarkRepository} CompetenceMarkRepository + * @typedef {import('../index.js').CertificationCandidateRepository} CertificationCandidateRepository * @typedef {import('../index.js').ScoringDegradationService} ScoringDegradationService * @typedef {import('../index.js').ScoringCertificationService} ScoringCertificationService * @typedef {import('../index.js').ScoringService} ScoringService + * @typedef {import('../index.js').PlacementProfileService} PlacementProfileService + * @typedef {import('../../../../session-management/domain/models/CertificationAssessment.js').CertificationAssessment} CertificationAssessment */ import _ from 'lodash'; @@ -24,6 +27,9 @@ import { AlgorithmEngineVersion } from '../../../../shared/domain/models/Algorit /** * @param {Object} params + * @param {{juryId: number}} params.[event] + * @param {'PIX-ALGO'|'Jury Pix'|'PIX-ALGO-FRAUD-REJECTION'} params.emitter + * @param {CertificationAssessment} params.certificationAssessment * @param {AssessmentResultRepository} params.assessmentResultRepository * @param {CertificationCourseRepository} params.certificationCourseRepository * @param {CompetenceMarkRepository} params.competenceMarkRepository @@ -31,6 +37,9 @@ import { AlgorithmEngineVersion } from '../../../../shared/domain/models/Algorit * @param {AreaRepository} params.areaRepository * @param {PlacementProfileService} params.placementProfileService * @param {ScoringService} params.scoringService + * @param {CertificationCandidateRepository} params.certificationCandidateRepository + * @param {Object} params.dependencies + * @param {calculateCertificationAssessmentScore} params.dependencies.calculateCertificationAssessmentScore */ export const handleV2CertificationScoring = async ({ event, @@ -43,14 +52,15 @@ export const handleV2CertificationScoring = async ({ areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, dependencies = { calculateCertificationAssessmentScore }, }) => { const certificationAssessmentScore = await dependencies.calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); const certificationCourse = await certificationCourseRepository.get({ id: certificationAssessment.certificationCourseId, @@ -78,18 +88,24 @@ export const handleV2CertificationScoring = async ({ /** * @param {Object} params - * @param {ScoringService} params.dependencies.scoringService + * @param {CertificationAssessment} params.certificationAssessment + * @param {ScoringService} params.scoringService + * @param {CertificationCandidateRepository} params.certificationCandidateRepository */ export const calculateCertificationAssessmentScore = async function ({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }) { + const candidate = await certificationCandidateRepository.findByAssessmentId({ + assessmentId: certificationAssessment.id, + }); + const testedCompetences = await _getTestedCompetences({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, placementProfileService, }); @@ -105,16 +121,13 @@ export const calculateCertificationAssessmentScore = async function ({ ); const allAreas = await areaRepository.list(); - return _getResult( - matchingAnswers, - matchingCertificationChallenges, - testedCompetences, - allAreas, - continueOnError, - scoringService, - ); + return _getResult(matchingAnswers, matchingCertificationChallenges, testedCompetences, allAreas, scoringService); }; +/** + * @param {Object} params + * @param {PlacementProfileService} params.placementProfileService + */ async function _getTestedCompetences({ userId, limitDate, version, placementProfileService }) { const placementProfile = await placementProfileService.getPlacementProfile({ userId, limitDate, version }); return _(placementProfile.userCompetences) @@ -143,7 +156,6 @@ function _getCompetenceMarksWithCertifiedLevelAndScore( listCompetences, reproducibilityRate, certificationChallenges, - continueOnError, answerCollection, allAreas, scoringService, @@ -152,11 +164,9 @@ function _getCompetenceMarksWithCertifiedLevelAndScore( const challengesForCompetence = _.filter(certificationChallenges, { competenceId: competence.id }); const answersForCompetence = _selectAnswersMatchingCertificationChallenges(answers, challengesForCompetence); - if (!continueOnError) { - CertificationContract.assertThatCompetenceHasAtLeastOneChallenge(challengesForCompetence, competence.index); - CertificationContract.assertThatEveryAnswerHasMatchingChallenge(answersForCompetence, challengesForCompetence); - CertificationContract.assertThatNoChallengeHasMoreThanOneAnswer(answersForCompetence, challengesForCompetence); - } + CertificationContract.assertThatCompetenceHasAtLeastOneChallenge(challengesForCompetence, competence.index); + CertificationContract.assertThatEveryAnswerHasMatchingChallenge(answersForCompetence, challengesForCompetence); + CertificationContract.assertThatNoChallengeHasMoreThanOneAnswer(answersForCompetence, challengesForCompetence); const certifiedLevel = CertifiedLevel.from({ numberOfChallenges: answerCollection.numberOfChallengesForCompetence(competence.id), @@ -196,10 +206,8 @@ function _getCompetenceMarksWithFailedLevel(listCompetences, allAreas, scoringSe /** * @param {ScoringService} scoringService */ -function _getResult(answers, certificationChallenges, testedCompetences, allAreas, continueOnError, scoringService) { - if (!continueOnError) { - CertificationContract.assertThatWeHaveEnoughAnswers(answers, certificationChallenges); - } +function _getResult(answers, certificationChallenges, testedCompetences, allAreas, scoringService) { + CertificationContract.assertThatWeHaveEnoughAnswers(answers, certificationChallenges); const answerCollection = AnswerCollectionForScoring.from({ answers, challenges: certificationChallenges }); @@ -227,16 +235,13 @@ function _getResult(answers, certificationChallenges, testedCompetences, allArea testedCompetences, reproducibilityRate.value, certificationChallenges, - continueOnError, answerCollection, allAreas, scoringService, ); const scoreAfterRating = _getSumScoreFromCertifiedCompetences(competenceMarks); - if (!continueOnError) { - CertificationContract.assertThatScoreIsCoherentWithReproducibilityRate(scoreAfterRating, reproducibilityRate.value); - } + CertificationContract.assertThatScoreIsCoherentWithReproducibilityRate(scoreAfterRating, reproducibilityRate.value); return new CertificationAssessmentScore({ competenceMarks, @@ -247,6 +252,7 @@ function _getResult(answers, certificationChallenges, testedCompetences, allArea /** * @param {Object} params + * @param {CertificationAssessment} params.certificationAssessment * @param {ScoringCertificationService} params.scoringCertificationService */ function _createV2AssessmentResult({ diff --git a/api/src/certification/session-management/domain/models/CertificationAssessment.js b/api/src/certification/session-management/domain/models/CertificationAssessment.js index a9910e53fb7..3d4baf550ba 100644 --- a/api/src/certification/session-management/domain/models/CertificationAssessment.js +++ b/api/src/certification/session-management/domain/models/CertificationAssessment.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('../../../../shared/domain/models/CertificationChallengeWithType.js').CertificationChallengeWithType} CertificationChallengeWithType + * @typedef {import('../../../../evaluation/domain/models/Answer.js').Answer} Answer + */ import JoiDate from '@joi/date'; import BaseJoi from 'joi'; const Joi = BaseJoi.extend(JoiDate); @@ -36,6 +40,12 @@ const certificationAssessmentSchema = Joi.object({ }); class CertificationAssessment { + /** + * @param {Object} params + * @param {Date} params.createdAt certification course creation date + * @param {Array} params.certificationChallenges + * @param {Array} params.certificationAnswersByDate + */ constructor({ id, userId, diff --git a/api/src/shared/domain/models/PlacementProfile.js b/api/src/shared/domain/models/PlacementProfile.js index 0f32a5b040c..60c23bff7fe 100644 --- a/api/src/shared/domain/models/PlacementProfile.js +++ b/api/src/shared/domain/models/PlacementProfile.js @@ -1,8 +1,17 @@ +/** + * @typedef {import('./UserCompetence.js').UserCompetence} UserCompetence + */ import _ from 'lodash'; import { MINIMUM_CERTIFIABLE_COMPETENCES_FOR_CERTIFIABILITY } from '../constants.js'; class PlacementProfile { + /** + * @param {Object} params + * @param {Date} params.profileDate + * @param {number} params.userId + * @param {Array} params.userCompetences + */ constructor({ profileDate, userId, userCompetences } = {}) { this.profileDate = profileDate; this.userId = userId; diff --git a/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v2_test.js b/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v2_test.js index ab3b1e16c2b..e3d7bd40d49 100644 --- a/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v2_test.js +++ b/api/tests/certification/evaluation/unit/domain/services/scoring/scoring-v2_test.js @@ -886,12 +886,15 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct context('#calculateCertificationAssessmentScore', function () { let certificationAssessment, certificationAssessmentData, expectedCertifiedCompetences; let competenceWithMarks_1_1, competenceWithMarks_2_2, competenceWithMarks_3_3, competenceWithMarks_4_4; - let areaRepository; + let areaRepository, certificationCandidateRepository; beforeEach(function () { areaRepository = { list: sinon.stub(), }; + certificationCandidateRepository = { + findByAssessmentId: sinon.stub().throws('bad arguments'), + }; areaRepository.list.resolves(['1', '2', '3', '4', '5', '6'].map((code) => ({ id: `area${code}`, code }))); certificationAssessmentData = { id: 1, @@ -964,6 +967,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct certificationAnswersByDate, certificationChallenges: challenges, }; + const candidate = domainBuilder.certification.evaluation.buildCandidate(); const placementProfileService = { getPlacementProfile: sinon.stub(), @@ -971,18 +975,23 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); + certificationCandidateRepository.findByAssessmentId + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate); // when const error = await catchErr(calculateCertificationAssessmentScore)({ certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1023,19 +1032,24 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); + certificationCandidateRepository.findByAssessmentId + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate); // when const error = await catchErr(calculateCertificationAssessmentScore)({ candidate, certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1138,19 +1152,24 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); + certificationCandidateRepository.findByAssessmentId + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate); // when const error = await catchErr(calculateCertificationAssessmentScore)({ candidate, certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1159,8 +1178,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct }); }); - context('Compute certification result for jury (continue on error)', function () { - const continueOnError = true; + context('Compute certification result for jury', function () { let placementProfileService; beforeEach(function () { certificationAssessment = domainBuilder.buildCertificationAssessment({ @@ -1168,6 +1186,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct certificationAnswersByDate: wrongAnswersForAllChallenges(), certificationChallenges: challenges, }); + const candidate = domainBuilder.certification.evaluation.buildCandidate(); placementProfileService = { getPlacementProfile: sinon.stub(), @@ -1175,20 +1194,25 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); + certificationCandidateRepository.findByAssessmentId + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate); }); it('should get user profile', async function () { // when await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1210,10 +1234,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: startedCertificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1226,10 +1250,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1270,10 +1294,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1307,10 +1331,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1348,10 +1372,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1366,10 +1390,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const { percentageCorrectAnswers } = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1421,10 +1445,16 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService = { getPlacementProfile: sinon.stub(), }; + const candidate = domainBuilder.certification.evaluation.buildCandidate(); + certificationCandidateRepository.findByAssessmentId + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate); placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); @@ -1432,10 +1462,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // When const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // Then @@ -1448,7 +1478,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct }); context('Calculate certification result when assessment is completed (stop on error)', function () { - const continueOnError = false; let placementProfileService; beforeEach(function () { certificationAssessment = domainBuilder.buildCertificationAssessment({ @@ -1456,13 +1485,22 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct certificationAnswersByDate: wrongAnswersForAllChallenges(), certificationChallenges: challenges, }); + const candidate = domainBuilder.certification.evaluation.buildCandidate(); placementProfileService = { getPlacementProfile: sinon.stub(), }; + certificationCandidateRepository = { + findByAssessmentId: sinon + .stub() + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate), + }; placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); @@ -1473,10 +1511,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1517,10 +1555,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1552,10 +1590,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1593,10 +1631,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1605,7 +1643,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct }); context('when only one challenge is asked for a competence', function () { - let placementProfileService; + let placementProfileService, certificationCandidateRepository; it('certifies a level below the estimated one if reproducibility rate is < 70%', async function () { // given const userCompetences = [ @@ -1641,10 +1679,19 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct placementProfileService = { getPlacementProfile: sinon.stub(), }; + const candidate = domainBuilder.certification.evaluation.buildCandidate(); + certificationCandidateRepository = { + findByAssessmentId: sinon + .stub() + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate), + }; placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); @@ -1678,10 +1725,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1690,7 +1737,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct }); context('when challenges contains one QROCM-dep challenge to validate two skills', function () { - let placementProfileService; + let placementProfileService, certificationCandidateRepository; beforeEach(function () { const userCompetences = [ _buildUserCompetence(competence_5, 50, 5), @@ -1733,13 +1780,23 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct domainBuilder.buildCertificationChallengeWithType, ); + const candidate = domainBuilder.certification.evaluation.buildCandidate(); + certificationCandidateRepository = { + findByAssessmentId: sinon + .stub() + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate), + }; + placementProfileService = { getPlacementProfile: sinon.stub(), }; placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); @@ -1778,10 +1835,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1830,10 +1887,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1843,7 +1900,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct }); context('non neutralization rate trustability', function () { - let placementProfileService; + let placementProfileService, certificationCandidateRepository; beforeEach(function () { certificationAssessment = domainBuilder.buildCertificationAssessment({ @@ -1852,13 +1909,22 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct certificationChallenges: challenges, }); certificationAssessment.certificationAnswersByDate = correctAnswersForAllChallenges(); + const candidate = domainBuilder.certification.evaluation.buildCandidate(); placementProfileService = { getPlacementProfile: sinon.stub(), }; + certificationCandidateRepository = { + findByAssessmentId: sinon + .stub() + .withArgs({ + assessmentId: certificationAssessment.id, + }) + .resolves(candidate), + }; placementProfileService.getPlacementProfile .withArgs({ userId: certificationAssessment.userId, - limitDate: certificationAssessment.createdAt, + limitDate: candidate.reconciledAt, version: AlgorithmEngineVersion.V2, }) .resolves({ userCompetences }); @@ -1876,10 +1942,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1901,10 +1967,10 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError: false, areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then diff --git a/api/tests/certification/session-management/acceptance/application/certification-course-route_test.js b/api/tests/certification/session-management/acceptance/application/certification-course-route_test.js index cf97a61e2a2..a6d28a94203 100644 --- a/api/tests/certification/session-management/acceptance/application/certification-course-route_test.js +++ b/api/tests/certification/session-management/acceptance/application/certification-course-route_test.js @@ -1,3 +1,5 @@ +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/index.js'; import { createServer, @@ -138,6 +140,7 @@ describe('Certification | Session Management | Acceptance | Application | Routes }); const { assessment, assessmentResult } = await createSuccessfulCertificationCourse({ + sessionId: session.id, userId, certificationCourse, }); @@ -267,15 +270,46 @@ describe('Certification | Session Management | Acceptance | Application | Routes const session = databaseBuilder.factory.buildSession({ publishedAt: new Date('2018-12-01T01:02:03Z'), + version: SESSIONS_VERSIONS.V3, }); const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ sessionId: session.id, userId, isRejectedForFraud: true, + version: AlgorithmEngineVersion.V3, + }); + + const configurationCreatorId = databaseBuilder.factory.buildUser().id; + databaseBuilder.factory.buildCompetenceScoringConfiguration({ + createdByUserId: configurationCreatorId, + configuration: [ + { + competence: '1.1', + values: [ + { + bounds: { + max: 0, + min: -5, + }, + competenceLevel: 0, + }, + { + bounds: { + max: 5, + min: 0, + }, + competenceLevel: 1, + }, + ], + }, + ], }); + databaseBuilder.factory.buildScoringConfiguration({ createdByUserId: configurationCreatorId }); + databaseBuilder.factory.buildFlashAlgorithmConfiguration(); const { assessment, assessmentResult } = await createSuccessfulCertificationCourse({ + sessionId: session.id, userId, certificationCourse, }); diff --git a/api/tests/certification/session-management/acceptance/application/finalize-route_test.js b/api/tests/certification/session-management/acceptance/application/finalize-route_test.js index e8dd9d314f8..2911ceab8a0 100644 --- a/api/tests/certification/session-management/acceptance/application/finalize-route_test.js +++ b/api/tests/certification/session-management/acceptance/application/finalize-route_test.js @@ -1,7 +1,9 @@ +import { AlgorithmEngineVersion } from '../../../../../src/certification/shared/domain/models/AlgorithmEngineVersion.js'; import { CertificationIssueReportCategory, CertificationIssueReportSubcategories, } from '../../../../../src/certification/shared/domain/models/CertificationIssueReportCategory.js'; +import { SESSIONS_VERSIONS } from '../../../../../src/certification/shared/domain/models/SessionVersion.js'; import { AnswerStatus, Assessment, CertificationResult } from '../../../../../src/shared/domain/models/index.js'; import { createServer, @@ -86,7 +88,15 @@ describe('Certification | Session Management | Acceptance | Application | Route // given const userId = databaseBuilder.factory.buildUser().id; const session = databaseBuilder.factory.buildSession(); - const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ sessionId: session.id }).id; + const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ + userId, + sessionId: session.id, + }).id; + databaseBuilder.factory.buildCertificationCandidate({ + sessionId: session.id, + userId, + reconciledAt: new Date('2020-01-01'), + }); databaseBuilder.factory.buildCertificationCenterMembership({ userId, certificationCenterId: session.certificationCenterId, @@ -182,39 +192,18 @@ describe('Certification | Session Management | Acceptance | Application | Route it('should set the finalized session as publishable when the issue reports have been resolved', async function () { // given - const learningContent = [ - { - id: 'recArea0', - code: '66', - competences: [ - { - id: 'recCompetence0', - index: '1', - tubes: [ - { - id: 'recTube0_0', - skills: [ - { - id: 'recSkill0_0', - nom: '@recSkill0_0', - challenges: [{ id: 'recChallenge0_0_0' }], - }, - ], - }, - ], - }, - ], - }, - ]; - const learningContentObjects = learningContentBuilder.fromAreas(learningContent); - mockLearningContent(learningContentObjects); - const userId = databaseBuilder.factory.buildUser().id; const session = databaseBuilder.factory.buildSession(); const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ + userId, sessionId: session.id, completedAt: new Date(), }).id; + databaseBuilder.factory.buildCertificationCandidate({ + sessionId: session.id, + userId, + reconciledAt: new Date('2020-01-01'), + }); databaseBuilder.factory.buildCertificationCenterMembership({ userId, certificationCenterId: session.certificationCenterId, @@ -291,34 +280,6 @@ describe('Certification | Session Management | Acceptance | Application | Route it('should re score assessment when there is auto-neutralizable challenge', async function () { // given - - const learningContent = [ - { - id: 'recArea0', - code: '66', - competences: [ - { - id: 'recCompetence0', - index: '1', - tubes: [ - { - id: 'recTube0_0', - skills: [ - { - id: 'recSkill0_0', - nom: '@recSkill0_0', - challenges: [{ id: 'recChallenge0_0_0' }], - }, - ], - }, - ], - }, - ], - }, - ]; - const learningContentObjects = learningContentBuilder.fromAreas(learningContent); - mockLearningContent(learningContentObjects); - const userId = databaseBuilder.factory.buildUser().id; const session = databaseBuilder.factory.buildSession(); const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ @@ -326,6 +287,11 @@ describe('Certification | Session Management | Acceptance | Application | Route userId, createdAt: new Date(), }).id; + databaseBuilder.factory.buildCertificationCandidate({ + sessionId: session.id, + userId, + reconciledAt: new Date('2020-01-01'), + }); databaseBuilder.factory.buildCertificationCenterMembership({ userId, certificationCenterId: session.certificationCenterId, @@ -447,38 +413,13 @@ describe('Certification | Session Management | Acceptance | Application | Route it('should set the finalized session as publishable', async function () { // given - const learningContent = [ - { - id: 'recArea0', - code: '66', - competences: [ - { - id: 'recCompetence0', - index: '1', - tubes: [ - { - id: 'recTube0_0', - skills: [ - { - id: 'recSkill0_0', - nom: '@recSkill0_0', - challenges: [{ id: 'recChallenge0_0_0' }], - }, - ], - }, - ], - }, - ], - }, - ]; - const learningContentObjects = learningContentBuilder.fromAreas(learningContent); - mockLearningContent(learningContentObjects); - const userId = databaseBuilder.factory.buildUser().id; - const session = databaseBuilder.factory.buildSession(); + const session = databaseBuilder.factory.buildSession({ version: SESSIONS_VERSIONS.V3 }); const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ + userId, sessionId: session.id, completedAt: new Date(), + version: AlgorithmEngineVersion.V3, }).id; databaseBuilder.factory.buildCertificationCenterMembership({ userId, @@ -494,7 +435,7 @@ describe('Certification | Session Management | Acceptance | Application | Route certificationCourseId, category: CertificationIssueReportCategory.IN_CHALLENGE, description: '', - subcategory: CertificationIssueReportSubcategories.WEBSITE_BLOCKED, + subcategory: CertificationIssueReportSubcategories.EXTRA_TIME_PERCENTAGE, questionNumber: 1, }); @@ -557,39 +498,19 @@ describe('Certification | Session Management | Acceptance | Application | Route it('should mark the assessment as ended due to finalization', async function () { // given const abortReason = 'candidate'; - const learningContent = [ - { - id: 'recArea0', - code: '66', - competences: [ - { - id: 'recCompetence0', - index: '1', - tubes: [ - { - id: 'recTube0_0', - skills: [ - { - id: 'recSkill0_0', - nom: '@recSkill0_0', - challenges: [{ id: 'recChallenge0_0_0' }], - }, - ], - }, - ], - }, - ], - }, - ]; - const learningContentObjects = learningContentBuilder.fromAreas(learningContent); - mockLearningContent(learningContentObjects); - const userId = databaseBuilder.factory.buildUser().id; - const session = databaseBuilder.factory.buildSession(); + const session = databaseBuilder.factory.buildSession({ version: SESSIONS_VERSIONS.V3 }); const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ + userId, sessionId: session.id, completedAt: null, + version: AlgorithmEngineVersion.V3, }).id; + databaseBuilder.factory.buildCertificationCandidate({ + sessionId: session.id, + userId, + reconciledAt: new Date('2020-01-01'), + }); databaseBuilder.factory.buildCertificationCenterMembership({ userId, certificationCenterId: session.certificationCenterId, @@ -607,7 +528,7 @@ describe('Certification | Session Management | Acceptance | Application | Route certificationCourseId, category: CertificationIssueReportCategory.IN_CHALLENGE, description: '', - subcategory: CertificationIssueReportSubcategories.WEBSITE_BLOCKED, + subcategory: CertificationIssueReportSubcategories.EXTRA_TIME_PERCENTAGE, questionNumber: 1, }); @@ -627,6 +548,34 @@ describe('Certification | Session Management | Acceptance | Application | Route result: AnswerStatus.KO.status, }); + const configurationCreatorId = databaseBuilder.factory.buildUser().id; + databaseBuilder.factory.buildCompetenceScoringConfiguration({ + createdByUserId: configurationCreatorId, + configuration: [ + { + competence: '1.1', + values: [ + { + bounds: { + max: 0, + min: -5, + }, + competenceLevel: 0, + }, + { + bounds: { + max: 5, + min: 0, + }, + competenceLevel: 1, + }, + ], + }, + ], + }); + databaseBuilder.factory.buildScoringConfiguration({ createdByUserId: configurationCreatorId }); + databaseBuilder.factory.buildFlashAlgorithmConfiguration(); + await databaseBuilder.commit(); options = { @@ -756,7 +705,7 @@ const _createSession = async ({ version = 2 } = {}) => { competences: [ { id: 'recCompetence0', - index: '1', + index: '1.1', tubes: [ { id: 'recTube0_0', diff --git a/api/tests/certification/shared/fixtures/certification-course.js b/api/tests/certification/shared/fixtures/certification-course.js index d8229a2536a..5f7a35f8bc2 100644 --- a/api/tests/certification/shared/fixtures/certification-course.js +++ b/api/tests/certification/shared/fixtures/certification-course.js @@ -127,15 +127,18 @@ const buildKnowledgeElementsFromAnswers = ({ answers, challenges, userId }) => { }); }; -export const createSuccessfulCertificationCourse = async ({ userId, certificationCourse }) => { +export const createSuccessfulCertificationCourse = async ({ sessionId, userId, certificationCourse }) => { const { challenges } = createLearningContent(); + databaseBuilder.factory.buildCertificationCandidate({ sessionId, userId, reconciledAt: new Date('2020-01-01') }); + const assessment = databaseBuilder.factory.buildAssessment({ userId, certificationCourseId: certificationCourse.id, type: Assessment.types.CERTIFICATION, state: 'completed', }); + const assessmentResult = databaseBuilder.factory.buildAssessmentResult.last({ certificationCourseId: certificationCourse.id, assessmentId: assessment.id,