diff --git a/api/lib/application/sco-organization-learners/index.js b/api/lib/application/sco-organization-learners/index.js index c9eab833d79..c08011ca5bc 100644 --- a/api/lib/application/sco-organization-learners/index.js +++ b/api/lib/application/sco-organization-learners/index.js @@ -21,37 +21,6 @@ const inaPattern = new RegExp('^[0-9]{10}[a-zA-Z]{1}$'); const register = async function (server) { server.route([ - { - method: 'POST', - path: '/api/sco-organization-learners/association', - config: { - handler: scoOrganizationLearnerController.reconcileScoOrganizationLearnerManually, - validate: { - options: { - allowUnknown: false, - }, - payload: Joi.object({ - data: { - attributes: { - 'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), - 'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), - birthdate: Joi.date().format('YYYY-MM-DD').required(), - 'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), - }, - type: 'sco-organization-learners', - }, - }), - failAction: (request, h) => { - return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h); - }, - }, - notes: [ - '- **Cette route est restreinte aux utilisateurs authentifiés**\n' + - '- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation', - ], - tags: ['api', 'sco-organization-learners'], - }, - }, { method: 'PUT', path: '/api/sco-organization-learners/possibilities', diff --git a/api/lib/application/sco-organization-learners/sco-organization-learner-controller.js b/api/lib/application/sco-organization-learners/sco-organization-learner-controller.js index a311e3bf6da..28f1ec48b3f 100644 --- a/api/lib/application/sco-organization-learners/sco-organization-learner-controller.js +++ b/api/lib/application/sco-organization-learners/sco-organization-learner-controller.js @@ -1,44 +1,11 @@ import dayjs from 'dayjs'; +import * as scoOrganizationLearnerSerializer from '../../../src/prescription/learner-management/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js'; import * as requestResponseUtils from '../../../src/shared/infrastructure/utils/request-response-utils.js'; import { usecases } from '../../domain/usecases/index.js'; import { DomainTransaction } from '../../infrastructure/DomainTransaction.js'; -import * as scoOrganizationLearnerSerializer from '../../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js'; import * as studentInformationForAccountRecoverySerializer from '../../infrastructure/serializers/jsonapi/student-information-for-account-recovery-serializer.js'; -const reconcileScoOrganizationLearnerManually = async function ( - request, - h, - dependencies = { scoOrganizationLearnerSerializer }, -) { - const authenticatedUserId = request.auth.credentials.userId; - const payload = request.payload.data.attributes; - const campaignCode = payload['campaign-code']; - const withReconciliation = request.query.withReconciliation === 'true'; - - const reconciliationInfo = { - id: authenticatedUserId, - firstName: payload['first-name'], - lastName: payload['last-name'], - birthdate: payload['birthdate'], - }; - - const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({ - campaignCode, - reconciliationInfo, - withReconciliation, - }); - - let response; - if (withReconciliation) { - const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner); - response = h.response(serializedData).code(200); - } else { - response = h.response().code(204); - } - return response; -}; - const generateUsername = async function (request, h, dependencies = { scoOrganizationLearnerSerializer }) { const payload = request.payload.data.attributes; const { 'campaign-code': campaignCode } = payload; @@ -191,7 +158,6 @@ const batchGenerateOrganizationLearnersUsernameWithTemporaryPassword = async fun }; const scoOrganizationLearnerController = { - reconcileScoOrganizationLearnerManually, generateUsername, createAndReconcileUserToOrganizationLearner, createUserAndReconcileToOrganizationLearnerFromExternalUser, diff --git a/api/src/prescription/learner-management/application/organization-learners-controller.js b/api/src/prescription/learner-management/application/organization-learners-controller.js index 2943263207d..098dd9bfdad 100644 --- a/api/src/prescription/learner-management/application/organization-learners-controller.js +++ b/api/src/prescription/learner-management/application/organization-learners-controller.js @@ -1,7 +1,7 @@ -import * as scoOrganizationLearnerSerializer from '../../../../lib/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js'; import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js'; import { ApplicationTransaction } from '../../shared/infrastructure/ApplicationTransaction.js'; import { usecases } from '../domain/usecases/index.js'; +import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js'; const deleteOrganizationLearners = async function (request, h) { const authenticatedUserId = request.auth.credentials.userId; diff --git a/api/src/prescription/learner-management/application/sco-organization-management-controller.js b/api/src/prescription/learner-management/application/sco-organization-management-controller.js index 6171ce6b264..cac2ab9a541 100644 --- a/api/src/prescription/learner-management/application/sco-organization-management-controller.js +++ b/api/src/prescription/learner-management/application/sco-organization-management-controller.js @@ -7,6 +7,7 @@ import { } from '../../../../src/shared/infrastructure/monitoring-tools.js'; import { usecases } from '../domain/usecases/index.js'; import { OrganizationLearnerParser } from '../infrastructure/serializers/csv/organization-learner-parser.js'; +import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js'; const INVALID_FILE_EXTENSION_ERROR = 'INVALID_FILE_EXTENSION'; @@ -66,8 +67,42 @@ const importOrganizationLearnersFromSIECLE = async function ( return h.response(null).code(204); }; +const reconcileScoOrganizationLearnerManually = async function ( + request, + h, + dependencies = { scoOrganizationLearnerSerializer }, +) { + const authenticatedUserId = request.auth.credentials.userId; + const payload = request.payload.data.attributes; + const campaignCode = payload['campaign-code']; + const withReconciliation = request.query.withReconciliation === 'true'; + + const reconciliationInfo = { + id: authenticatedUserId, + firstName: payload['first-name'], + lastName: payload['last-name'], + birthdate: payload['birthdate'], + }; + + const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({ + campaignCode, + reconciliationInfo, + withReconciliation, + }); + + let response; + if (withReconciliation) { + const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner); + response = h.response(serializedData).code(200); + } else { + response = h.response().code(204); + } + return response; +}; + const scoOrganizationManagementController = { importOrganizationLearnersFromSIECLE, + reconcileScoOrganizationLearnerManually, }; export { scoOrganizationManagementController }; diff --git a/api/src/prescription/learner-management/application/sco-organization-management-route.js b/api/src/prescription/learner-management/application/sco-organization-management-route.js index ff159ef1657..f4fab92d21e 100644 --- a/api/src/prescription/learner-management/application/sco-organization-management-route.js +++ b/api/src/prescription/learner-management/application/sco-organization-management-route.js @@ -2,7 +2,7 @@ import JoiDate from '@joi/date'; import BaseJoi from 'joi'; const Joi = BaseJoi.extend(JoiDate); -import { sendJsonApiError } from '../../../shared/application/http-errors.js'; +import { sendJsonApiError, UnprocessableEntityError } from '../../../shared/application/http-errors.js'; import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js'; import { identifiersType } from '../../../shared/domain/types/identifiers-type.js'; import { usecases } from '../domain/usecases/index.js'; @@ -12,6 +12,37 @@ const TWENTY_MEGABYTES = 1048576 * 20; const register = async function (server) { server.route([ + { + method: 'POST', + path: '/api/sco-organization-learners/association', + config: { + handler: scoOrganizationManagementController.reconcileScoOrganizationLearnerManually, + validate: { + options: { + allowUnknown: false, + }, + payload: Joi.object({ + data: { + attributes: { + 'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), + 'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), + birthdate: Joi.date().format('YYYY-MM-DD').required(), + 'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(), + }, + type: 'sco-organization-learners', + }, + }), + failAction: (request, h) => { + return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h); + }, + }, + notes: [ + '- **Cette route est restreinte aux utilisateurs authentifiés**\n' + + '- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation', + ], + tags: ['api', 'sco-organization-learners'], + }, + }, { method: 'POST', path: '/api/organizations/{id}/sco-organization-learners/import-siecle', diff --git a/api/tests/acceptance/application/sco-organization-learners/sco-organization-learner-controller_test.js b/api/tests/acceptance/application/sco-organization-learners/sco-organization-learner-controller_test.js index a3ef255713b..e17c7356d26 100644 --- a/api/tests/acceptance/application/sco-organization-learners/sco-organization-learner-controller_test.js +++ b/api/tests/acceptance/application/sco-organization-learners/sco-organization-learner-controller_test.js @@ -15,103 +15,6 @@ describe('Acceptance | Controller | sco-organization-learners', function () { server = await createServer(); }); - describe('POST /api/sco-organization-learners/association', function () { - let organization; - let campaign; - let options; - let organizationLearner; - let user; - - beforeEach(async function () { - // given - options = { - method: 'POST', - url: '/api/sco-organization-learners/association', - headers: {}, - payload: {}, - }; - - user = databaseBuilder.factory.buildUser(); - organization = databaseBuilder.factory.buildOrganization({ type: 'SCO' }); - organizationLearner = databaseBuilder.factory.buildOrganizationLearner({ - firstName: 'france', - lastName: 'gall', - birthdate: '2001-01-01', - organizationId: organization.id, - userId: null, - nationalStudentId: 'francegall123', - }); - campaign = databaseBuilder.factory.buildCampaign({ organizationId: organization.id }); - - await databaseBuilder.commit(); - }); - - context('associate user with firstName, lastName and birthdate', function () { - it('should return an 200 status after having successfully associated user to organizationLearner', async function () { - // given - options.headers.authorization = generateValidRequestAuthorizationHeader(user.id); - options.payload.data = { - attributes: { - 'campaign-code': campaign.code, - 'first-name': organizationLearner.firstName, - 'last-name': organizationLearner.lastName, - birthdate: organizationLearner.birthdate, - }, - }; - options.url += '?withReconciliation=true'; - - // when - const response = await server.inject(options); - - // then - expect(response.statusCode).to.equal(200); - }); - - context('when user is not authenticated', function () { - it('should respond with a 401 - unauthorized access', async function () { - // given - options.headers.authorization = 'invalid.access.token'; - - // when - const response = await server.inject(options); - - // then - expect(response.statusCode).to.equal(401); - }); - }); - - context('When withReconciliation query param is set to false', function () { - it('should not reconcile user and return a 204 No Content', async function () { - // given - options.headers.authorization = generateValidRequestAuthorizationHeader(user.id); - options.payload.data = { - attributes: { - 'campaign-code': campaign.code, - 'first-name': organizationLearner.firstName, - 'last-name': organizationLearner.lastName, - birthdate: organizationLearner.birthdate, - }, - }; - options.url += '?withReconciliation=false'; - - // when - const response = await server.inject(options); - - // then - expect(response.statusCode).to.equal(204); - const organizationLearnerInDB = await knex('organization-learners') - .where({ - firstName: organizationLearner.firstName, - lastName: organizationLearner.lastName, - birthdate: organizationLearner.birthdate, - }) - .select(); - expect(organizationLearnerInDB.userId).to.be.undefined; - }); - }); - }); - }); - describe('PUT /api/sco-organization-learners/possibilities', function () { it('returns the organizationLearner linked to the user and a 200 status code response', async function () { //given diff --git a/api/tests/integration/application/sco-organization-learners/index_test.js b/api/tests/integration/application/sco-organization-learners/index_test.js index ef6048f193a..24f4c9b189e 100644 --- a/api/tests/integration/application/sco-organization-learners/index_test.js +++ b/api/tests/integration/application/sco-organization-learners/index_test.js @@ -7,10 +7,6 @@ describe('Integration | Application | Route | sco-organization-learners', functi let httpTestServer; beforeEach(async function () { - sinon - .stub(scoOrganizationLearnerController, 'reconcileScoOrganizationLearnerManually') - .callsFake((request, h) => h.response('ok').code(204)); - sinon .stub(scoOrganizationLearnerController, 'generateUsername') .callsFake((request, h) => h.response('ok').code(200)); @@ -34,190 +30,6 @@ describe('Integration | Application | Route | sco-organization-learners', functi await httpTestServer.register(moduleUnderTest); }); - describe('POST /api/sco-organization-learners/association', function () { - const method = 'POST'; - const url = '/api/sco-organization-learners/association'; - - context('User association with firstName, lastName, birthdate and campaignCode', function () { - it('should succeed', async function () { - // given - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': 'Smith', - birthdate: '2012-12-12', - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(204); - }); - - it('should succeed when there is a space', async function () { - // given - const payload = { - data: { - attributes: { - 'first-name': 'Robert ', - 'last-name': 'Smith', - birthdate: '2012-12-12', - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(204); - expect(response.request.payload.data.attributes['first-name']).to.equal('Robert '); - }); - - it('should return an error when there is no payload', async function () { - // when - const response = await httpTestServer.request(method, url); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid first name attribute in the payload', async function () { - // given - const INVALID_FIRSTNAME = ' '; - const payload = { - data: { - attributes: { - 'first-name': INVALID_FIRSTNAME, - 'last-name': 'Smith', - birthdate: '2012-12-12', - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid last name attribute in the payload', async function () { - // given - const INVALID_LASTNAME = ''; - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': INVALID_LASTNAME, - birthdate: '2012-12-12', - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid a birthdate attribute (with space) in the payload', async function () { - // given - const INVALID_BIRTHDATE = '2012- 12-12'; - - // when - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': 'Smith', - birthdate: INVALID_BIRTHDATE, - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid birthdate attribute (with extra zeros) in the payload', async function () { - // given - const INVALID_BIRTHDATE = '2012-012-12'; - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': 'Smith', - birthdate: INVALID_BIRTHDATE, - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid birthdate attribute (not a proper date) in the payload', async function () { - // given - const INVALID_BIRTHDATE = '1999-99-99'; - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': 'Smith', - birthdate: INVALID_BIRTHDATE, - 'campaign-code': 'RESTRICTD', - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - - it('should return an error when there is an invalid campaign code attribute in the payload', async function () { - // given - const INVALID_CAMPAIGNCODE = ''; - const payload = { - data: { - attributes: { - 'first-name': 'Robert', - 'last-name': 'Smith', - birthdate: '2012-12-12', - 'campaign-code': INVALID_CAMPAIGNCODE, - }, - }, - }; - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(422); - }); - }); - }); - describe('PUT /api/sco-organization-learners/possibilities', function () { const method = 'PUT'; const url = '/api/sco-organization-learners/possibilities'; diff --git a/api/tests/prescription/learner-management/acceptance/application/sco-organization-management-route_test.js b/api/tests/prescription/learner-management/acceptance/application/sco-organization-management-route_test.js new file mode 100644 index 00000000000..cd149890916 --- /dev/null +++ b/api/tests/prescription/learner-management/acceptance/application/sco-organization-management-route_test.js @@ -0,0 +1,112 @@ +import { + createServer, + databaseBuilder, + expect, + generateValidRequestAuthorizationHeader, + knex, +} from '../../../../test-helper.js'; + +describe('Acceptance | Route | sco-organization-management-route', function () { + let server; + + beforeEach(async function () { + server = await createServer(); + }); + + describe('POST /api/sco-organization-learners/association', function () { + let organization; + let campaign; + let options; + let organizationLearner; + let user; + + beforeEach(async function () { + // given + options = { + method: 'POST', + url: '/api/sco-organization-learners/association', + headers: {}, + payload: {}, + }; + + user = databaseBuilder.factory.buildUser(); + organization = databaseBuilder.factory.buildOrganization({ type: 'SCO' }); + organizationLearner = databaseBuilder.factory.buildOrganizationLearner({ + firstName: 'france', + lastName: 'gall', + birthdate: '2001-01-01', + organizationId: organization.id, + userId: null, + nationalStudentId: 'francegall123', + }); + campaign = databaseBuilder.factory.buildCampaign({ organizationId: organization.id }); + + await databaseBuilder.commit(); + }); + + context('associate user with firstName, lastName and birthdate', function () { + it('should return an 200 status after having successfully associated user to organizationLearner', async function () { + // given + options.headers.authorization = generateValidRequestAuthorizationHeader(user.id); + options.payload.data = { + attributes: { + 'campaign-code': campaign.code, + 'first-name': organizationLearner.firstName, + 'last-name': organizationLearner.lastName, + birthdate: organizationLearner.birthdate, + }, + }; + options.url += '?withReconciliation=true'; + + // when + const response = await server.inject(options); + + // then + expect(response.statusCode).to.equal(200); + }); + + context('when user is not authenticated', function () { + it('should respond with a 401 - unauthorized access', async function () { + // given + options.headers.authorization = 'invalid.access.token'; + + // when + const response = await server.inject(options); + + // then + expect(response.statusCode).to.equal(401); + }); + }); + + context('When withReconciliation query param is set to false', function () { + it('should not reconcile user and return a 204 No Content', async function () { + // given + options.headers.authorization = generateValidRequestAuthorizationHeader(user.id); + options.payload.data = { + attributes: { + 'campaign-code': campaign.code, + 'first-name': organizationLearner.firstName, + 'last-name': organizationLearner.lastName, + birthdate: organizationLearner.birthdate, + }, + }; + options.url += '?withReconciliation=false'; + + // when + const response = await server.inject(options); + + // then + expect(response.statusCode).to.equal(204); + const organizationLearnerInDB = await knex('organization-learners') + .where({ + firstName: organizationLearner.firstName, + lastName: organizationLearner.lastName, + birthdate: organizationLearner.birthdate, + }) + .select(); + expect(organizationLearnerInDB.userId).to.be.undefined; + }); + }); + }); + }); +}); diff --git a/api/tests/prescription/learner-management/integration/application/sco-organization-management-route_test.js b/api/tests/prescription/learner-management/integration/application/sco-organization-management-route_test.js new file mode 100644 index 00000000000..a72afe34312 --- /dev/null +++ b/api/tests/prescription/learner-management/integration/application/sco-organization-management-route_test.js @@ -0,0 +1,200 @@ +import { scoOrganizationManagementController } from '../../../../../src/prescription/learner-management/application/sco-organization-management-controller.js'; +import * as moduleUnderTest from '../../../../../src/prescription/learner-management/application/sco-organization-management-route.js'; +import { expect, HttpTestServer, sinon } from '../../../../test-helper.js'; + +describe('Integration | Application | Route | sco-organization-learners', function () { + let httpTestServer; + + beforeEach(async function () { + sinon + .stub(scoOrganizationManagementController, 'reconcileScoOrganizationLearnerManually') + .callsFake((request, h) => h.response('ok').code(204)); + + httpTestServer = new HttpTestServer(); + await httpTestServer.register(moduleUnderTest); + }); + + describe('POST /api/sco-organization-learners/association', function () { + const method = 'POST'; + const url = '/api/sco-organization-learners/association'; + + context('User association with firstName, lastName, birthdate and campaignCode', function () { + it('should succeed', async function () { + // given + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': 'Smith', + birthdate: '2012-12-12', + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(204); + }); + + it('should succeed when there is a space', async function () { + // given + const payload = { + data: { + attributes: { + 'first-name': 'Robert ', + 'last-name': 'Smith', + birthdate: '2012-12-12', + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(204); + expect(response.request.payload.data.attributes['first-name']).to.equal('Robert '); + }); + + it('should return an error when there is no payload', async function () { + // when + const response = await httpTestServer.request(method, url); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid first name attribute in the payload', async function () { + // given + const INVALID_FIRSTNAME = ' '; + const payload = { + data: { + attributes: { + 'first-name': INVALID_FIRSTNAME, + 'last-name': 'Smith', + birthdate: '2012-12-12', + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid last name attribute in the payload', async function () { + // given + const INVALID_LASTNAME = ''; + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': INVALID_LASTNAME, + birthdate: '2012-12-12', + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid a birthdate attribute (with space) in the payload', async function () { + // given + const INVALID_BIRTHDATE = '2012- 12-12'; + + // when + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': 'Smith', + birthdate: INVALID_BIRTHDATE, + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid birthdate attribute (with extra zeros) in the payload', async function () { + // given + const INVALID_BIRTHDATE = '2012-012-12'; + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': 'Smith', + birthdate: INVALID_BIRTHDATE, + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid birthdate attribute (not a proper date) in the payload', async function () { + // given + const INVALID_BIRTHDATE = '1999-99-99'; + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': 'Smith', + birthdate: INVALID_BIRTHDATE, + 'campaign-code': 'RESTRICTD', + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + + it('should return an error when there is an invalid campaign code attribute in the payload', async function () { + // given + const INVALID_CAMPAIGNCODE = ''; + const payload = { + data: { + attributes: { + 'first-name': 'Robert', + 'last-name': 'Smith', + birthdate: '2012-12-12', + 'campaign-code': INVALID_CAMPAIGNCODE, + }, + }, + }; + + // when + const response = await httpTestServer.request(method, url, payload); + + // then + expect(response.statusCode).to.equal(422); + }); + }); + }); +});