From 4c6931e718efd35e25227702bb97e01740f83816 Mon Sep 17 00:00:00 2001 From: Steph0 Date: Thu, 14 Nov 2024 11:28:16 +0100 Subject: [PATCH 1/4] :recycle: api: documentation of scoring V2 --- .../domain/services/scoring/scoring-v2.js | 14 ++++++++++++++ .../domain/models/CertificationAssessment.js | 10 ++++++++++ api/src/shared/domain/models/PlacementProfile.js | 9 +++++++++ 3 files changed, 33 insertions(+) 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..0cb709465b0 100644 --- a/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js +++ b/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js @@ -5,6 +5,8 @@ * @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 +26,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 +36,8 @@ import { AlgorithmEngineVersion } from '../../../../shared/domain/models/Algorit * @param {AreaRepository} params.areaRepository * @param {PlacementProfileService} params.placementProfileService * @param {ScoringService} params.scoringService + * @param {Object} params.dependencies + * @param {calculateCertificationAssessmentScore} params.dependencies.calculateCertificationAssessmentScore */ export const handleV2CertificationScoring = async ({ event, @@ -78,6 +85,8 @@ export const handleV2CertificationScoring = async ({ /** * @param {Object} params + * @param {CertificationAssessment} params.certificationAssessment + * @param {boolean} params.continueOnError * @param {ScoringService} params.dependencies.scoringService */ export const calculateCertificationAssessmentScore = async function ({ @@ -115,6 +124,10 @@ export const calculateCertificationAssessmentScore = async function ({ ); }; +/** + * @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) @@ -247,6 +260,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; From 47f77d3dd31d578c235519a3a6980de97a0fe4d6 Mon Sep 17 00:00:00 2001 From: Steph0 Date: Thu, 14 Nov 2024 11:51:38 +0100 Subject: [PATCH 2/4] :recycle: api: continueOnError had absolutely no effect on code --- .../domain/services/scoring/scoring-v2.js | 32 ++++--------------- .../services/scoring/scoring-v2_test.js | 24 +------------- 2 files changed, 8 insertions(+), 48 deletions(-) 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 0cb709465b0..f98220761ed 100644 --- a/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js +++ b/api/src/certification/evaluation/domain/services/scoring/scoring-v2.js @@ -54,7 +54,6 @@ export const handleV2CertificationScoring = async ({ }) => { const certificationAssessmentScore = await dependencies.calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, @@ -86,12 +85,10 @@ export const handleV2CertificationScoring = async ({ /** * @param {Object} params * @param {CertificationAssessment} params.certificationAssessment - * @param {boolean} params.continueOnError * @param {ScoringService} params.dependencies.scoringService */ export const calculateCertificationAssessmentScore = async function ({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -114,14 +111,7 @@ 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); }; /** @@ -156,7 +146,6 @@ function _getCompetenceMarksWithCertifiedLevelAndScore( listCompetences, reproducibilityRate, certificationChallenges, - continueOnError, answerCollection, allAreas, scoringService, @@ -165,11 +154,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), @@ -209,10 +196,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 }); @@ -240,16 +225,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, 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..8114e6c1364 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 @@ -979,7 +979,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const error = await catchErr(calculateCertificationAssessmentScore)({ certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, @@ -1032,7 +1031,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct const error = await catchErr(calculateCertificationAssessmentScore)({ candidate, certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, @@ -1147,7 +1145,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct const error = await catchErr(calculateCertificationAssessmentScore)({ candidate, certificationAssessment, - continueOnError: false, areaRepository, placementProfileService, scoringService, @@ -1159,8 +1156,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({ @@ -1185,7 +1181,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1210,7 +1205,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: startedCertificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1226,7 +1220,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1270,7 +1263,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1307,7 +1299,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1348,7 +1339,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1366,7 +1356,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const { percentageCorrectAnswers } = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1432,7 +1421,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // When const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1448,7 +1436,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({ @@ -1473,7 +1460,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1517,7 +1503,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1552,7 +1537,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1593,7 +1577,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1678,7 +1661,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1778,7 +1760,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1830,7 +1811,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment, - continueOnError, areaRepository, placementProfileService, scoringService, @@ -1876,7 +1856,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError: false, areaRepository, placementProfileService, scoringService, @@ -1901,7 +1880,6 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct // when const certificationAssessmentScore = await calculateCertificationAssessmentScore({ certificationAssessment: certificationAssessmentWithNeutralizedChallenge, - continueOnError: false, areaRepository, placementProfileService, scoringService, From baa28173b0af73fe5bc522553fcb409cc5f9abbb Mon Sep 17 00:00:00 2001 From: Steph0 Date: Fri, 15 Nov 2024 18:25:59 +0100 Subject: [PATCH 3/4] :sparkles: api: V2 scoring must find the competences used during certification challenges selection --- .../domain/services/scoring/scoring-v2.js | 14 ++- .../services/scoring/scoring-v2_test.js | 114 ++++++++++++++++-- .../certification-course-route_test.js | 2 + .../application/finalize-route_test.js | 22 +++- .../shared/fixtures/certification-course.js | 5 +- 5 files changed, 140 insertions(+), 17 deletions(-) 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 f98220761ed..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,6 +2,7 @@ * @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 @@ -36,6 +37,7 @@ 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 */ @@ -50,6 +52,7 @@ export const handleV2CertificationScoring = async ({ areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, dependencies = { calculateCertificationAssessmentScore }, }) => { const certificationAssessmentScore = await dependencies.calculateCertificationAssessmentScore({ @@ -57,6 +60,7 @@ export const handleV2CertificationScoring = async ({ areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); const certificationCourse = await certificationCourseRepository.get({ id: certificationAssessment.certificationCourseId, @@ -85,17 +89,23 @@ export const handleV2CertificationScoring = async ({ /** * @param {Object} params * @param {CertificationAssessment} params.certificationAssessment - * @param {ScoringService} params.dependencies.scoringService + * @param {ScoringService} params.scoringService + * @param {CertificationCandidateRepository} params.certificationCandidateRepository */ export const calculateCertificationAssessmentScore = async function ({ certificationAssessment, 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, }); 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 8114e6c1364..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,10 +975,15 @@ 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)({ @@ -982,6 +991,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1022,10 +1032,15 @@ 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)({ @@ -1034,6 +1049,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1136,10 +1152,15 @@ 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)({ @@ -1148,6 +1169,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1164,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(), @@ -1171,10 +1194,15 @@ 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 () { @@ -1184,6 +1212,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1208,6 +1237,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1223,6 +1253,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1266,6 +1297,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1302,6 +1334,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1342,6 +1375,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1359,6 +1393,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1410,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 }); @@ -1424,6 +1465,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // Then @@ -1443,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 }); @@ -1463,6 +1514,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1506,6 +1558,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1540,6 +1593,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1580,6 +1634,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1588,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 = [ @@ -1624,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 }); @@ -1664,6 +1728,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1672,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), @@ -1715,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 }); @@ -1763,6 +1838,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1814,6 +1890,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1823,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({ @@ -1832,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 }); @@ -1859,6 +1945,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct areaRepository, placementProfileService, scoringService, + certificationCandidateRepository, }); // then @@ -1883,6 +1970,7 @@ describe('Certification | Shared | Unit | Domain | Services | Scoring V2', funct 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..8ef2ede5781 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 @@ -138,6 +138,7 @@ describe('Certification | Session Management | Acceptance | Application | Routes }); const { assessment, assessmentResult } = await createSuccessfulCertificationCourse({ + sessionId: session.id, userId, certificationCourse, }); @@ -276,6 +277,7 @@ describe('Certification | Session Management | Acceptance | Application | Routes }); 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..cbdecc8f90b 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 @@ -86,7 +86,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, @@ -212,9 +220,15 @@ describe('Certification | Session Management | Acceptance | Application | Route 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, @@ -326,6 +340,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, @@ -477,6 +496,7 @@ describe('Certification | Session Management | Acceptance | Application | Route const userId = databaseBuilder.factory.buildUser().id; const session = databaseBuilder.factory.buildSession(); const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ + userId, sessionId: session.id, completedAt: new Date(), }).id; 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, From f5a40298ec2b413b1fb4214c8139ca1ba5bac840 Mon Sep 17 00:00:00 2001 From: AndreiaPena Date: Mon, 18 Nov 2024 16:44:36 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20api:=20fixing=20V3=20t?= =?UTF-8?q?ests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Steph0 Co-authored-by: Guillaume Lagorce --- .../certification-course-route_test.js | 32 ++++ .../application/finalize-route_test.js | 157 +++++------------- 2 files changed, 75 insertions(+), 114 deletions(-) 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 8ef2ede5781..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, @@ -268,14 +270,44 @@ 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, 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 cbdecc8f90b..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, @@ -190,33 +192,6 @@ 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({ @@ -305,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({ @@ -466,39 +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, @@ -514,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, }); @@ -577,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, @@ -627,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, }); @@ -647,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 = { @@ -776,7 +705,7 @@ const _createSession = async ({ version = 2 } = {}) => { competences: [ { id: 'recCompetence0', - index: '1', + index: '1.1', tubes: [ { id: 'recTube0_0',