diff --git a/api/src/parcoursup/domain/read-models/CertificationResult.js b/api/src/parcoursup/domain/read-models/CertificationResult.js index 2ca59e1e645..d16fdead1fe 100644 --- a/api/src/parcoursup/domain/read-models/CertificationResult.js +++ b/api/src/parcoursup/domain/read-models/CertificationResult.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('./Competence.js').Competence} Competence + */ + export class CertificationResult { /** * @param {Object} props @@ -9,11 +13,7 @@ export class CertificationResult { * @param {string} props.status * @param {string} props.pixScore * @param {Date} props.certificationDate - * @param {Array} props.competences - * @param {string} props.competences.competence_code - * @param {string} props.competences.competence_name - * @param {string} props.competences.area_name - * @param {string} props.competences.competence_level + * @param {Array} props.competences */ constructor({ ine, diff --git a/api/src/parcoursup/domain/read-models/Competence.js b/api/src/parcoursup/domain/read-models/Competence.js new file mode 100644 index 00000000000..2c61ef9707a --- /dev/null +++ b/api/src/parcoursup/domain/read-models/Competence.js @@ -0,0 +1,15 @@ +export class Competence { + /** + * @param {Object} props + * @param {string} props.code + * @param {string} props.name + * @param {string} props.areaName + * @param {string} props.level + */ + constructor({ code, name, areaName, level }) { + this.code = code; + this.name = name; + this.areaName = areaName; + this.level = level; + } +} diff --git a/api/src/parcoursup/infrastructure/repositories/certification-repository.js b/api/src/parcoursup/infrastructure/repositories/certification-repository.js index 9c088f92f80..2076c750b87 100644 --- a/api/src/parcoursup/infrastructure/repositories/certification-repository.js +++ b/api/src/parcoursup/infrastructure/repositories/certification-repository.js @@ -1,6 +1,7 @@ import { datamartKnex } from '../../../../db/knex-database-connection.js'; import { NotFoundError } from '../../../shared/domain/errors.js'; import { CertificationResult } from '../../domain/read-models/CertificationResult.js'; +import { Competence } from '../../domain/read-models/Competence.js'; const getByINE = async ({ ine }) => { return _getBySearchParams({ @@ -91,6 +92,15 @@ const getByVerificationCode = async ({ verificationCode }) => { */ const _toDomain = (certificationResultDto) => { return certificationResultDto.map((certificationResult) => { + const competences = certificationResult.competences.map((competence) => { + return new Competence({ + code: competence.competence_code, + name: competence.competence_name, + areaName: competence.area_name, + level: competence.competence_level, + }); + }); + return new CertificationResult({ ine: certificationResult.national_student_id, organizationUai: certificationResult.organization_uai, @@ -100,14 +110,7 @@ const _toDomain = (certificationResultDto) => { status: certificationResult.status, pixScore: certificationResult.pix_score, certificationDate: certificationResult.certification_date, - competences: certificationResult.competences.map((competence) => { - return { - code: competence.competence_code, - name: competence.competence_name, - areaName: competence.area_name, - level: competence.competence_level, - }; - }), + competences, }); }); }; diff --git a/api/tests/parcoursup/acceptance/application/certification-route_test.js b/api/tests/parcoursup/acceptance/application/certification-route_test.js index fe1498db5be..0ad293880da 100644 --- a/api/tests/parcoursup/acceptance/application/certification-route_test.js +++ b/api/tests/parcoursup/acceptance/application/certification-route_test.js @@ -217,36 +217,5 @@ describe('Parcoursup | Acceptance | Application | certification-route', function expect(response.statusCode).to.equal(200); expect(response.result).to.deep.equal(expectedCertification); }); - - it('should return 409 HTTP status code in case of duplicated certification results', async function () { - // given - // Same candidate but with another birthdate - datamartBuilder.factory.buildCertificationResult({ - ...certificationResultData, - birthdate: '1999-01-01', - }); - await datamartBuilder.commit(); - - const options = { - method: 'POST', - url: `/api/application/parcoursup/certification/search`, - headers: { - authorization: generateValidRequestAuthorizationHeaderForApplication( - PARCOURSUP_CLIENT_ID, - PARCOURSUP_SOURCE, - PARCOURSUP_SCOPE, - ), - }, - payload: { - ine, - }, - }; - - // when - const response = await server.inject(options); - - // then - expect(response.statusCode).to.equal(409); - }); }); }); diff --git a/api/tests/parcoursup/integration/repositories/certification-repository_test.js b/api/tests/parcoursup/integration/repositories/certification-repository_test.js index 6b274e64c1c..400fca848fc 100644 --- a/api/tests/parcoursup/integration/repositories/certification-repository_test.js +++ b/api/tests/parcoursup/integration/repositories/certification-repository_test.js @@ -48,18 +48,18 @@ describe('Parcoursup | Infrastructure | Integration | Repositories | certificati pixScore: 327, certificationDate: new Date('2024-11-22T09:39:54Z'), competences: [ - { + domainBuilder.parcoursup.buildCompetence({ code: '1.1', name: 'Mener une recherche et une veille d’information', areaName: 'Informations et données', level: 3, - }, - { + }), + domainBuilder.parcoursup.buildCompetence({ code: '1.2', name: 'Gérer des données', areaName: 'Informations et données', level: 5, - }, + }), ], }); expect(results).to.deep.equal([expectedCertification]); @@ -134,18 +134,18 @@ describe('Parcoursup | Infrastructure | Integration | Repositories | certificati pixScore: 327, certificationDate: new Date('2024-11-22T09:39:54Z'), competences: [ - { + domainBuilder.parcoursup.buildCompetence({ code: '1.1', name: 'Mener une recherche et une veille d’information', areaName: 'Informations et données', level: 3, - }, - { + }), + domainBuilder.parcoursup.buildCompetence({ code: '1.2', name: 'Gérer des données', areaName: 'Informations et données', level: 5, - }, + }), ], }); expect(results).to.deep.equal([expectedCertification]); @@ -222,18 +222,18 @@ describe('Parcoursup | Infrastructure | Integration | Repositories | certificati pixScore: 327, certificationDate: new Date('2024-11-22T09:39:54Z'), competences: [ - { + domainBuilder.parcoursup.buildCompetence({ code: '1.1', name: 'Mener une recherche et une veille d’information', areaName: 'Informations et données', level: 3, - }, - { + }), + domainBuilder.parcoursup.buildCompetence({ code: '1.2', name: 'Gérer des données', areaName: 'Informations et données', level: 5, - }, + }), ], }); expect(results).to.deep.equal([expectedCertification]); diff --git a/api/tests/parcoursup/unit/application/certification-route_test.js b/api/tests/parcoursup/unit/application/certification-route_test.js index 320c207ee4d..46b7ab9755a 100644 --- a/api/tests/parcoursup/unit/application/certification-route_test.js +++ b/api/tests/parcoursup/unit/application/certification-route_test.js @@ -1,5 +1,6 @@ import { certificationController } from '../../../../src/parcoursup/application/certification-controller.js'; import * as moduleUnderTest from '../../../../src/parcoursup/application/certification-route.js'; +import { MoreThanOneMatchingCertificationError } from '../../../../src/parcoursup/domain/errors.js'; import { expect, generateValidRequestAuthorizationHeaderForApplication, @@ -8,11 +9,11 @@ import { } from '../../../test-helper.js'; describe('Parcoursup | Unit | Application | Routes | Certification', function () { - let url, method, headers, httpTestServer; + let url, method, headers, httpTestServer, controllerStub; beforeEach(async function () { url = '/api/application/parcoursup/certification/search'; - sinon.stub(certificationController, 'getCertificationResult').callsFake((request, h) => h.response().code(200)); + controllerStub = sinon.stub(certificationController, 'getCertificationResult'); httpTestServer = new HttpTestServer(); httpTestServer.setupAuthentication(); @@ -33,45 +34,51 @@ describe('Parcoursup | Unit | Application | Routes | Certification', function () }); describe('POST /parcoursup/certification/search', function () { - it('should return 200 with a valid ine params in body', async function () { - //given - const payload = { - ine: '123456789OK', - }; - // when - const response = await httpTestServer.request(method, url, payload, null, headers); + context('return a result', function () { + beforeEach(function () { + controllerStub.callsFake((request, h) => h.response().code(200)); + }); - // then - expect(response.statusCode).to.equal(200); - }); + it('with a valid ine params in body', async function () { + //given + const payload = { + ine: '123456789OK', + }; + // when + const response = await httpTestServer.request(method, url, payload, null, headers); - it('should return 200 with with valid organizationUai, lastName, firstName and birthdate in body params', async function () { - //given - const payload = { - organizationUai: '1234567A', - lastName: 'LEPONGE', - firstName: 'BOB', - birthdate: '2000-01-01', - }; + // then + expect(response.statusCode).to.equal(200); + }); - // when - const response = await httpTestServer.request(method, url, payload, null, headers); + it('with valid organizationUai, lastName, firstName and birthdate in body params', async function () { + //given + const payload = { + organizationUai: '1234567A', + lastName: 'LEPONGE', + firstName: 'BOB', + birthdate: '2000-01-01', + }; - // then - expect(response.statusCode).to.equal(200); - }); + // when + const response = await httpTestServer.request(method, url, payload, null, headers); - it('should return 200 with a valide verificationCode params in body', async function () { - //given - const payload = { - verificationCode: 'P-1234567A', - }; + // then + expect(response.statusCode).to.equal(200); + }); - // when - const response = await httpTestServer.request(method, url, payload, null, headers); + it('with a valid verificationCode params in body', async function () { + //given + const payload = { + verificationCode: 'P-1234567A', + }; - // then - expect(response.statusCode).to.equal(200); + // when + const response = await httpTestServer.request(method, url, payload, null, headers); + + // then + expect(response.statusCode).to.equal(200); + }); }); context('return 400 error', function () { @@ -186,6 +193,24 @@ describe('Parcoursup | Unit | Application | Routes | Certification', function () }); }); + context('return 409 error', function () { + it('with duplicated certification results', async function () { + // given + const payload = { + verificationCode: 'P-1234567A', + }; + controllerStub.throws(function () { + return new MoreThanOneMatchingCertificationError(); + }); + + // when + const response = await httpTestServer.request(method, url, payload, null, headers); + + // then + expect(response.statusCode).to.equal(409); + }); + }); + it('should return 403 with a wrong scope', async function () { //given const PARCOURSUP_CLIENT_ID = 'test-parcoursupClientId'; diff --git a/api/tests/tooling/domain-builder/factory/index.js b/api/tests/tooling/domain-builder/factory/index.js index f70f63d3d96..caa671c7e35 100644 --- a/api/tests/tooling/domain-builder/factory/index.js +++ b/api/tests/tooling/domain-builder/factory/index.js @@ -200,6 +200,7 @@ import { buildCompetenceForScoring } from './certification/shared/build-competen import { buildJuryComment } from './certification/shared/build-jury-comment.js'; import { buildV3CertificationScoring } from './certification/shared/build-v3-certification-scoring.js'; import { buildCertificationResult as parcoursupCertificationResult } from './parcoursup/build-certification-result.js'; +import { buildCompetence as parcoursupCompetence } from './parcoursup/build-competence.js'; import { buildCampaign as boundedContextCampaignBuildCampaign } from './prescription/campaign/build-campaign.js'; import { buildCampaignParticipation as boundedContextCampaignParticipationBuildCampaignParticipation } from './prescription/campaign-participation/build-campaign-participation.js'; import { buildStageCollection as buildStageCollectionForTargetProfileManagement } from './target-profile-management/build-stage-collection.js'; @@ -262,6 +263,7 @@ const certification = { const parcoursup = { buildCertificationResult: parcoursupCertificationResult, + buildCompetence: parcoursupCompetence, }; const prescription = { diff --git a/api/tests/tooling/domain-builder/factory/parcoursup/build-competence.js b/api/tests/tooling/domain-builder/factory/parcoursup/build-competence.js new file mode 100644 index 00000000000..adad19bf7a5 --- /dev/null +++ b/api/tests/tooling/domain-builder/factory/parcoursup/build-competence.js @@ -0,0 +1,5 @@ +import { Competence } from '../../../../../src/parcoursup/domain/read-models/Competence.js'; + +export const buildCompetence = function ({ code, name, areaName, level }) { + return new Competence({ code, name, areaName, level }); +};