From ebce281cc19af9255dc4c3dee1be74efbbac18f7 Mon Sep 17 00:00:00 2001 From: Laura Bergoens Date: Wed, 27 Nov 2024 17:22:31 +0100 Subject: [PATCH 1/5] add job controller for release creation --- .../lcms-create-release-job-controller.js | 20 +++++++++++++++++++ .../domain/models/LcmsCreateReleaseJob.js | 5 +++++ .../create-learning-content-release.js | 3 +++ ...lcms-create-release-job-controller_test.js | 19 ++++++++++++++++++ .../lcms-refresh-cache-job-controller_test.js | 10 +++++----- .../create-learning-content-release_test.js | 19 ++++++++++++++++++ 6 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 api/src/learning-content/application/jobs/lcms-create-release-job-controller.js create mode 100644 api/src/learning-content/domain/models/LcmsCreateReleaseJob.js create mode 100644 api/src/learning-content/domain/usecases/create-learning-content-release.js create mode 100644 api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js create mode 100644 api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js diff --git a/api/src/learning-content/application/jobs/lcms-create-release-job-controller.js b/api/src/learning-content/application/jobs/lcms-create-release-job-controller.js new file mode 100644 index 00000000000..7f4e5ddc483 --- /dev/null +++ b/api/src/learning-content/application/jobs/lcms-create-release-job-controller.js @@ -0,0 +1,20 @@ +import { JobController } from '../../../shared/application/jobs/job-controller.js'; +import { logger } from '../../../shared/infrastructure/utils/logger.js'; +import { LcmsCreateReleaseJob } from '../../domain/models/LcmsCreateReleaseJob.js'; +import { usecases } from '../../domain/usecases/index.js'; + +export class LcmsCreateReleaseJobController extends JobController { + constructor() { + super(LcmsCreateReleaseJob.name); + } + + async handle() { + try { + await usecases.createLearningContentRelease(); + logger.info('Learning Content cache updated with newly created release'); + } catch (e) { + logger.error('Error while updating cache with newly created release', e); + throw e; + } + } +} diff --git a/api/src/learning-content/domain/models/LcmsCreateReleaseJob.js b/api/src/learning-content/domain/models/LcmsCreateReleaseJob.js new file mode 100644 index 00000000000..ac66becd546 --- /dev/null +++ b/api/src/learning-content/domain/models/LcmsCreateReleaseJob.js @@ -0,0 +1,5 @@ +export class LcmsCreateReleaseJob { + constructor({ userId }) { + this.userId = userId; + } +} diff --git a/api/src/learning-content/domain/usecases/create-learning-content-release.js b/api/src/learning-content/domain/usecases/create-learning-content-release.js new file mode 100644 index 00000000000..9dcb29c8406 --- /dev/null +++ b/api/src/learning-content/domain/usecases/create-learning-content-release.js @@ -0,0 +1,3 @@ +export async function createLearningContentRelease({ LearningContentCache }) { + await LearningContentCache.instance.update(); +} diff --git a/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js b/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js new file mode 100644 index 00000000000..03cb73b2bf7 --- /dev/null +++ b/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js @@ -0,0 +1,19 @@ +import { LcmsRefreshCacheJobController } from '../../../../../src/learning-content/application/jobs/lcms-refresh-cache-job-controller.js'; +import { usecases } from '../../../../../src/learning-content/domain/usecases/index.js'; +import { expect, sinon } from '../../../../test-helper.js'; + +describe('Learning Content | Unit | Application | Jobs | LcmsRefreshCacheJobController', function () { + describe('#handle', function () { + it('should call usecase', async function () { + // given + sinon.stub(usecases, 'refreshLearningContentCache'); + const handler = new LcmsRefreshCacheJobController(); + + // when + await handler.handle(); + + // then + expect(usecases.refreshLearningContentCache).to.have.been.calledOnce; + }); + }); +}); diff --git a/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js b/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js index f15811c0a70..c66f331d8d7 100644 --- a/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js +++ b/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js @@ -1,19 +1,19 @@ -import { LcmsRefreshCacheJobController } from '../../../../../src/learning-content/application/jobs/lcms-refresh-cache-job-controller.js'; +import { LcmsCreateReleaseJobController } from '../../../../../src/learning-content/application/jobs/lcms-create-release-job-controller.js'; import { usecases } from '../../../../../src/learning-content/domain/usecases/index.js'; import { expect, sinon } from '../../../../test-helper.js'; -describe('Unit | Shared | Application | Jobs | LcmsRefreshCacheJobController', function () { +describe('Learning Content | Unit | Application | Jobs | LcmsCreateReleaseJobController', function () { describe('#handle', function () { it('should call usecase', async function () { // given - sinon.stub(usecases, 'refreshLearningContentCache'); - const handler = new LcmsRefreshCacheJobController(); + sinon.stub(usecases, 'createLearningContentRelease'); + const handler = new LcmsCreateReleaseJobController(); // when await handler.handle(); // then - expect(usecases.refreshLearningContentCache).to.have.been.calledOnce; + expect(usecases.createLearningContentRelease).to.have.been.calledOnce; }); }); }); diff --git a/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js b/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js new file mode 100644 index 00000000000..ebf83a02af4 --- /dev/null +++ b/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js @@ -0,0 +1,19 @@ +import { createLearningContentRelease } from '../../../../../src/learning-content/domain/usecases/create-learning-content-release.js'; +import { expect, sinon } from '../../../../test-helper.js'; + +describe('Unit | UseCase | create-learning-content-release', function () { + it('should call update on the learning content cache', async function () { + // given + const LearningContentCache = { + instance: { + update: sinon.stub(), + }, + }; + + // when + await createLearningContentRelease({ LearningContentCache }); + + // then + expect(LearningContentCache.instance.update).to.have.been.called; + }); +}); From 2cb3fca49c19b3a8a722d7b5aa89d7f72a6e9ba2 Mon Sep 17 00:00:00 2001 From: Laura Bergoens Date: Wed, 27 Nov 2024 22:05:07 +0100 Subject: [PATCH 2/5] transform post releases route into a scheduling job route --- .../learning-content-controller.js | 13 +++--------- .../application/learning-content-route.js | 6 ++++-- .../domain/usecases/dependencies.js | 2 ++ ...ule-create-learning-content-release-job.js | 10 ++++++++++ .../lcms-create-release-job-repository.js | 13 ++++++++++++ ...lcms-create-release-job-repository_test.js | 20 +++++++++++++++++++ .../lcms-refresh-cache-job-repository_test.js | 2 +- .../learning-content-controller_test.js | 19 +++++++++++++----- ...hedule-create-learning-content-job_test.js | 20 +++++++++++++++++++ 9 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 api/src/learning-content/domain/usecases/schedule-create-learning-content-release-job.js create mode 100644 api/src/learning-content/infrastructure/repositories/jobs/lcms-create-release-job-repository.js create mode 100644 api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-create-release-job-repository_test.js create mode 100644 api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js diff --git a/api/src/learning-content/application/learning-content-controller.js b/api/src/learning-content/application/learning-content-controller.js index bfb6e47d90e..f408549029e 100644 --- a/api/src/learning-content/application/learning-content-controller.js +++ b/api/src/learning-content/application/learning-content-controller.js @@ -1,17 +1,10 @@ import { sharedUsecases } from '../../shared/domain/usecases/index.js'; -import { logger } from '../../shared/infrastructure/utils/logger.js'; import { usecases } from '../domain/usecases/index.js'; const createRelease = async function (request, h) { - sharedUsecases - .createLcmsRelease() - .then(() => { - logger.info('Release created and cache reloaded'); - }) - .catch((e) => { - logger.error('Error while creating the release and reloading cache', e); - }); - return h.response({}).code(204); + const { userId } = request.auth.credentials; + await usecases.scheduleCreateLearningContentReleaseJob({ userId }); + return h.response({}).code(202); }; const refreshCache = async function (request, h) { diff --git a/api/src/learning-content/application/learning-content-route.js b/api/src/learning-content/application/learning-content-route.js index b6514627c0b..576ecdf1e84 100644 --- a/api/src/learning-content/application/learning-content-route.js +++ b/api/src/learning-content/application/learning-content-route.js @@ -19,7 +19,8 @@ export async function register(server) { tags: ['api', 'lcms'], notes: [ 'Cette route est restreinte aux utilisateurs authentifiés avec le rôle Super Admin', - 'Elle permet de demander la création d’une nouvelle version au référentiel et de recharger le cache', + 'Elle permet de lancer un job pour demander la création d’une nouvelle version au référentiel\n' + + ' et de recharger le cache', ], }, }, @@ -74,7 +75,8 @@ export async function register(server) { tags: ['api', 'cache'], notes: [ 'Cette route est restreinte aux utilisateurs authentifiés avec le rôle Super Admin', - 'Elle permet de précharger les entrées du cache de l’application (les requêtes les plus longues)', + 'Elle permet de lancer un job pour précharger les entrées du cache de l’application \n' + + '(les requêtes les plus longues)', ], }, }, diff --git a/api/src/learning-content/domain/usecases/dependencies.js b/api/src/learning-content/domain/usecases/dependencies.js index dacbe112799..79c7419f4a9 100644 --- a/api/src/learning-content/domain/usecases/dependencies.js +++ b/api/src/learning-content/domain/usecases/dependencies.js @@ -4,6 +4,7 @@ import { challengeRepository } from '../../infrastructure/repositories/challenge import { competenceRepository } from '../../infrastructure/repositories/competence-repository.js'; import { courseRepository } from '../../infrastructure/repositories/course-repository.js'; import { frameworkRepository } from '../../infrastructure/repositories/framework-repository.js'; +import { lcmsCreateReleaseJobRepository } from '../../infrastructure/repositories/jobs/lcms-create-release-job-repository.js'; import { lcmsRefreshCacheJobRepository } from '../../infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js'; import { missionRepository } from '../../infrastructure/repositories/mission-repository.js'; import { skillRepository } from '../../infrastructure/repositories/skill-repository.js'; @@ -23,6 +24,7 @@ export const dependencies = { tutorialRepository, missionRepository, lcmsRefreshCacheJobRepository, + lcmsCreateReleaseJobRepository, LearningContentCache, }; diff --git a/api/src/learning-content/domain/usecases/schedule-create-learning-content-release-job.js b/api/src/learning-content/domain/usecases/schedule-create-learning-content-release-job.js new file mode 100644 index 00000000000..161cff0b71b --- /dev/null +++ b/api/src/learning-content/domain/usecases/schedule-create-learning-content-release-job.js @@ -0,0 +1,10 @@ +import { LcmsCreateReleaseJob } from '../models/LcmsCreateReleaseJob.js'; + +/** + * @param {{ + * userId: number + * } & import('./dependencies.js').Dependencies} + */ +export async function scheduleCreateLearningContentReleaseJob({ userId, lcmsCreateReleaseJobRepository }) { + await lcmsCreateReleaseJobRepository.performAsync(new LcmsCreateReleaseJob({ userId })); +} diff --git a/api/src/learning-content/infrastructure/repositories/jobs/lcms-create-release-job-repository.js b/api/src/learning-content/infrastructure/repositories/jobs/lcms-create-release-job-repository.js new file mode 100644 index 00000000000..c9aca9fef92 --- /dev/null +++ b/api/src/learning-content/infrastructure/repositories/jobs/lcms-create-release-job-repository.js @@ -0,0 +1,13 @@ +import { JobRepository, JobRetry } from '../../../../shared/infrastructure/repositories/jobs/job-repository.js'; +import { LcmsCreateReleaseJob } from '../../../domain/models/LcmsCreateReleaseJob.js'; + +class LcmsCreateReleaseJobRepository extends JobRepository { + constructor() { + super({ + name: LcmsCreateReleaseJob.name, + retry: JobRetry.NO_RETRY, + }); + } +} + +export const lcmsCreateReleaseJobRepository = new LcmsCreateReleaseJobRepository(); diff --git a/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-create-release-job-repository_test.js b/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-create-release-job-repository_test.js new file mode 100644 index 00000000000..1dd20c39957 --- /dev/null +++ b/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-create-release-job-repository_test.js @@ -0,0 +1,20 @@ +import { LcmsCreateReleaseJob } from '../../../../../../src/learning-content/domain/models/LcmsCreateReleaseJob.js'; +import { lcmsCreateReleaseJobRepository } from '../../../../../../src/learning-content/infrastructure/repositories/jobs/lcms-create-release-job-repository.js'; +import { expect } from '../../../../../test-helper.js'; + +describe('Learning Content | Integration | Repository | Jobs | LcmsCreateReleaseJobRepository', function () { + describe('#performAsync', function () { + it('publish a job', async function () { + // when + await lcmsCreateReleaseJobRepository.performAsync({ userId: 123 }); + + // then + await expect(LcmsCreateReleaseJob.name).to.have.been.performed.withJob({ + retrylimit: 0, + retrydelay: 0, + retrybackoff: false, + data: { userId: 123 }, + }); + }); + }); +}); diff --git a/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository_test.js b/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository_test.js index 72ce09b7956..9c92667cea7 100644 --- a/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository_test.js +++ b/api/tests/learning-content/integration/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository_test.js @@ -2,7 +2,7 @@ import { LcmsRefreshCacheJob } from '../../../../../../src/learning-content/doma import { lcmsRefreshCacheJobRepository } from '../../../../../../src/learning-content/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js'; import { expect } from '../../../../../test-helper.js'; -describe('Integration | Repository | Jobs | LcmsRefreshCacheJobRepository', function () { +describe('Learning Content | Integration | Repository | Jobs | LcmsRefreshCacheJobRepository', function () { describe('#performAsync', function () { it('publish a job', async function () { // when diff --git a/api/tests/learning-content/unit/application/learning-content-controller_test.js b/api/tests/learning-content/unit/application/learning-content-controller_test.js index 028449d510b..7f527c64f91 100644 --- a/api/tests/learning-content/unit/application/learning-content-controller_test.js +++ b/api/tests/learning-content/unit/application/learning-content-controller_test.js @@ -5,16 +5,25 @@ import { expect, hFake, sinon } from '../../../test-helper.js'; describe('Unit | Controller | learning-content-controller', function () { describe('#createRelease', function () { - it('should call the createRelease', async function () { + it('should schedule createRelease job', async function () { // given - sinon.stub(sharedUsecases, 'createLcmsRelease').resolves(); - const request = {}; + sinon.stub(usecases, 'scheduleCreateLearningContentReleaseJob').resolves(); // when - await learningContentController.createRelease(request, hFake); + await learningContentController.createRelease( + { + auth: { + credentials: { + userId: 123, + }, + }, + }, + hFake, + ); // then - expect(sharedUsecases.createLcmsRelease).to.have.been.called; + expect(usecases.scheduleCreateLearningContentReleaseJob).to.have.been.calledOnce; + expect(usecases.scheduleCreateLearningContentReleaseJob).to.have.been.calledWithExactly({ userId: 123 }); }); }); diff --git a/api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js b/api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js new file mode 100644 index 00000000000..499dddbb6bd --- /dev/null +++ b/api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js @@ -0,0 +1,20 @@ +import { LcmsCreateReleaseJob } from '../../../../../src/learning-content/domain/models/LcmsCreateReleaseJob.js'; +import { scheduleCreateLearningContentReleaseJob } from '../../../../../src/learning-content/domain/usecases/schedule-create-learning-content-release-job.js'; +import { expect, sinon } from '../../../../test-helper.js'; + +describe('Learning Content | Unit | Domain | Usecases | Schedule Create Learning Content Release Job', function () { + it('should use repository to schedule create learning content release job', async function () { + // given + const userId = 1234; + const lcmsCreateReleaseJobRepository = { performAsync: sinon.stub() }; + + // when + await scheduleCreateLearningContentReleaseJob({ userId, lcmsCreateReleaseJobRepository }); + + // then + expect(lcmsCreateReleaseJobRepository.performAsync).to.have.been.calledOnce; + expect(lcmsCreateReleaseJobRepository.performAsync).to.have.been.calledWithExactly( + new LcmsCreateReleaseJob({ userId }), + ); + }); +}); From 28a10f72cf35201a94b75c98b3ca310629b41833 Mon Sep 17 00:00:00 2001 From: Laura Bergoens Date: Wed, 27 Nov 2024 22:12:04 +0100 Subject: [PATCH 3/5] delete unused usecase in shared --- .../domain/usecases/create-lcms-release.js | 3 --- .../usecases/create-lcms-release_test.js | 19 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 api/src/shared/domain/usecases/create-lcms-release.js delete mode 100644 api/tests/shared/unit/domain/usecases/create-lcms-release_test.js diff --git a/api/src/shared/domain/usecases/create-lcms-release.js b/api/src/shared/domain/usecases/create-lcms-release.js deleted file mode 100644 index 9e77541609b..00000000000 --- a/api/src/shared/domain/usecases/create-lcms-release.js +++ /dev/null @@ -1,3 +0,0 @@ -export async function createLcmsRelease({ LearningContentCache }) { - await LearningContentCache.instance.update(); -} diff --git a/api/tests/shared/unit/domain/usecases/create-lcms-release_test.js b/api/tests/shared/unit/domain/usecases/create-lcms-release_test.js deleted file mode 100644 index fe4830ecd91..00000000000 --- a/api/tests/shared/unit/domain/usecases/create-lcms-release_test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { createLcmsRelease } from '../../../../../src/shared/domain/usecases/create-lcms-release.js'; -import { expect, sinon } from '../../../../test-helper.js'; - -describe('Unit | UseCase | create-lcms-release', function () { - it('should trigger an update of the learning content cache', async function () { - // given - const LearningContentCache = { - instance: { - update: sinon.stub(), - }, - }; - - // when - await createLcmsRelease({ LearningContentCache }); - - // then - expect(LearningContentCache.instance.update).to.have.been.called; - }); -}); From 145944de420c5a73d7014d01e4a4480cc1190166 Mon Sep 17 00:00:00 2001 From: Laura Bergoens Date: Wed, 27 Nov 2024 22:17:53 +0100 Subject: [PATCH 4/5] write in pg when creating a new release --- .../create-learning-content-release.js | 34 +- ...lcms-create-release-job-controller_test.js | 2235 +++++++++++++++++ ...lcms-create-release-job-controller_test.js | 19 - .../lcms-refresh-cache-job-controller_test.js | 19 - .../create-learning-content-release_test.js | 108 +- .../refresh-learning-content-cache_test.js | 2 +- 6 files changed, 2363 insertions(+), 54 deletions(-) create mode 100644 api/tests/learning-content/integration/application/jobs/lcms-create-release-job-controller_test.js delete mode 100644 api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js delete mode 100644 api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js diff --git a/api/src/learning-content/domain/usecases/create-learning-content-release.js b/api/src/learning-content/domain/usecases/create-learning-content-release.js index 9dcb29c8406..1cefe737e77 100644 --- a/api/src/learning-content/domain/usecases/create-learning-content-release.js +++ b/api/src/learning-content/domain/usecases/create-learning-content-release.js @@ -1,3 +1,31 @@ -export async function createLearningContentRelease({ LearningContentCache }) { - await LearningContentCache.instance.update(); -} +import { withTransaction } from '../../../shared/domain/DomainTransaction.js'; + +export const createLearningContentRelease = withTransaction( + /** @param {import('./dependencies.js').Dependencies} */ + async function createLearningContentRelease({ + LearningContentCache, + frameworkRepository, + areaRepository, + competenceRepository, + thematicRepository, + tubeRepository, + challengeRepository, + skillRepository, + courseRepository, + tutorialRepository, + missionRepository, + }) { + const learningContent = await LearningContentCache.instance.update(); + + await frameworkRepository.save(learningContent.frameworks); + await areaRepository.save(learningContent.areas); + await competenceRepository.save(learningContent.competences); + await thematicRepository.save(learningContent.thematics); + await tubeRepository.save(learningContent.tubes); + await skillRepository.save(learningContent.skills); + await challengeRepository.save(learningContent.challenges); + await courseRepository.save(learningContent.courses); + await tutorialRepository.save(learningContent.tutorials); + await missionRepository.save(learningContent.missions); + }, +); diff --git a/api/tests/learning-content/integration/application/jobs/lcms-create-release-job-controller_test.js b/api/tests/learning-content/integration/application/jobs/lcms-create-release-job-controller_test.js new file mode 100644 index 00000000000..d5ac7c4ecca --- /dev/null +++ b/api/tests/learning-content/integration/application/jobs/lcms-create-release-job-controller_test.js @@ -0,0 +1,2235 @@ +import { LcmsCreateReleaseJobController } from '../../../../../src/learning-content/application/jobs/lcms-create-release-job-controller.js'; +import { catchErr, databaseBuilder, expect, knex, nock } from '../../../../test-helper.js'; + +describe('Learning Content | Integration | Application | Jobs | Create release', function () { + let lcmsCreateReleaseJobController; + let lcmsApiCall; + let newLearningContent; + + beforeEach(async function () { + lcmsCreateReleaseJobController = new LcmsCreateReleaseJobController(); + newLearningContent = { + frameworks: [ + { id: 'aboutToBeRefreshedFrameworkId', name: 'name About to be refreshed Framework - new' }, + { id: 'untouchedFrameworkId', name: 'name Untouched Framework' }, + { id: 'newFrameworkId', name: 'name New Framework' }, + ], + areas: [ + { + id: 'aboutToBeRefreshedAreaId', + name: 'name About to be refreshed Domaine - new', + code: 'code About to be refreshed Domaine - new', + title_i18n: { + fr: 'title_i18n FR About to be refreshed Domaine - new', + en: 'title_i18n EN About to be refreshed Domaine - new', + }, + color: 'color About to be refreshed Domaine - new', + frameworkId: 'frameworkId About to be refreshed Domaine - new', + competenceIds: ['competenceId1 About to be refreshed Domaine - new'], + }, + { + id: 'untouchedAreaId', + name: 'name Untouched Domaine', + code: 'code Untouched Domaine', + title_i18n: { fr: 'title_i18n FR Untouched Domaine', nl: 'title_i18n NL Untouched Domaine' }, + color: 'color Untouched Domaine', + frameworkId: 'frameworkId Untouched Domaine', + competenceIds: ['competenceId Untouched Domaine'], + }, + { + id: 'newAreaId', + name: 'name New Domaine', + code: 'code New Domaine', + title_i18n: { fr: 'title_i18n FR New Domaine', nl: 'title_i18n NL New Domaine' }, + color: 'color New Domaine', + frameworkId: 'frameworkId New Domaine', + competenceIds: ['competenceId1 New Domaine', 'competenceId2 New Domaine'], + }, + ], + competences: [ + { + id: 'aboutToBeRefreshedCompetenceId', + index: 'index About to be refreshed Competence - new', + areaId: 'areaId About to be refreshed Competence - new', + skillIds: ['skillId About to be refreshed Competence - new'], + thematicIds: ['thematicId About to be refreshed Competence - new'], + origin: 'origin About to be refreshed Competence - new', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Competence - new', + en: 'name_i18n EN About to be refreshed Competence - new', + }, + description_i18n: { + fr: 'description_i18n FR About to be refreshed Competence - new', + nl: 'description_i18n NL About to be refreshed Competence - new', + }, + }, + { + id: 'untouchedCompetenceId', + index: 'index Untouched Competence', + areaId: 'areaId Untouched Competence', + skillIds: ['skillId Untouched Competence'], + thematicIds: ['thematicId Untouched Competence'], + origin: 'origin Untouched Competence', + name_i18n: { + fr: 'name_i18n FR Untouched Competence', + en: 'name_i18n EN Untouched Competence', + }, + description_i18n: { + fr: 'description_i18n FR Untouched Competence', + nl: 'description_i18n NL Untouched Competence', + }, + }, + { + id: 'newCompetenceId', + index: 'index New Competence', + areaId: 'areaId New Competence', + skillIds: ['skillId New Competence'], + thematicIds: ['thematicId New Competence'], + origin: 'origin New Competence', + name_i18n: { + fr: 'name_i18n FR New Competence', + en: 'name_i18n EN New Competence', + }, + description_i18n: { + fr: 'description_i18n FR New Competence', + nl: 'description_i18n NL New Competence', + }, + }, + ], + thematics: [ + { + id: 'aboutToBeRefreshedThematicId', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Thematique - new', + nl: 'name_i18n NL About to be refreshed Thematique - new', + }, + index: 11, + competenceId: 'competenceId About to be refreshed Thematique - new', + tubeIds: ['tubeId About to be refreshed Thematique - new'], + }, + { + id: 'untouchedThematicId', + name_i18n: { + fr: 'name_i18n FR Untouched Thematique', + en: 'name_i18n EN Untouched Thematique', + }, + index: 2, + competenceId: 'competenceId Untouched Thematique', + tubeIds: ['tubeId Untouched Thematique'], + }, + { + id: 'newThematicId', + name_i18n: { + fr: 'name_i18n FR New Thematique', + en: 'name_i18n EN New Thematique', + }, + index: 3, + competenceId: 'competenceId New Thematique', + tubeIds: ['tubeId New Thematique'], + }, + ], + tubes: [ + { + id: 'aboutToBeRefreshedTubeId', + name: 'name About to be refreshed Tube - new', + title: 'title About to be refreshed Tube - new', + description: 'description About to be refreshed Tube - new', + practicalTitle_i18n: { + fr: 'practicalTitle FR About to be refreshed Tube - new', + nl: 'practicalTitle NL About to be refreshed Tube - new', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR About to be refreshed Tube - new', + en: 'practicalDescription EN About to be refreshed Tube - new', + }, + competenceId: 'competenceId About to be refreshed Tube - new', + thematicId: 'thematicId About to be refreshed Tube - new', + skillIds: ['skillId About to be refreshed Tube - new'], + isMobileCompliant: true, + isTabletCompliant: true, + }, + { + id: 'untouchedTubeId', + name: 'name Untouched Tube', + title: 'title Untouched Tube', + description: 'description Untouched Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Untouched Tube', + en: 'practicalTitle EN Untouched Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Untouched Tube', + en: 'practicalDescription EN Untouched Tube', + }, + competenceId: 'competenceId Untouched Tube', + thematicId: 'thematicId Untouched Tube', + skillIds: ['skillId Untouched Tube'], + isMobileCompliant: false, + isTabletCompliant: true, + }, + { + id: 'newTubeId', + name: 'name New Tube', + title: 'title New Tube', + description: 'description New Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR New Tube', + en: 'practicalTitle EN New Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR New Tube', + en: 'practicalDescription EN New Tube', + }, + competenceId: 'competenceId New Tube', + thematicId: 'thematicId New Tube', + skillIds: ['skillId New Tube'], + isMobileCompliant: true, + isTabletCompliant: false, + }, + ], + skills: [ + { + id: 'aboutToBeRefreshedSkillId', + name: 'name about to be refreshed skill - new', + hintStatus: 'hintStatus about to be refreshed skill - new', + tutorialIds: [ + 'tutorialId1 about to be refreshed skill - new', + 'tutorialId2 about to be refreshed skill - new', + ], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 about to be refreshed skill - new', + 'learningMoreTutorialId2 about to be refreshed skill - new', + ], + pixValue: 2, + competenceId: 'competenceId about to be refreshed skill - new', + status: 'status about to be refreshed skill - new', + tubeId: 'tubeId about to be refreshed skill - new', + version: 2, + level: 2, + hint_i18n: { + fr: 'hint_i18n.fr about to be refreshed skill - new', + en: 'hint_i18n.en about to be refreshed skill - new', + nl: 'hint_i18n.nl about to be refreshed skill - new', + }, + }, + { + id: 'untouchedSkillId', + name: 'name untouched skill', + hintStatus: 'hintStatus untouched skill', + tutorialIds: ['tutorialId1 untouched skill', 'tutorialId2 untouched skill'], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 untouched skill', + 'learningMoreTutorialId2 untouched skill', + ], + pixValue: 3, + competenceId: 'competenceId untouched skill', + status: 'status untouched skill', + tubeId: 'tubeId untouched skill', + version: 3, + level: 3, + hint_i18n: { + fr: 'hint_i18n.fr untouched skill', + en: 'hint_i18n.en untouched skill', + nl: 'hint_i18n.nl untouched skill', + }, + }, + { + id: 'newSkillId', + name: 'name new skill', + hintStatus: 'hintStatus new skill', + tutorialIds: ['tutorialId1 new skill', 'tutorialId2 new skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 new skill', 'learningMoreTutorialId2 new skill'], + pixValue: 4, + competenceId: 'competenceId new skill', + status: 'status new skill', + tubeId: 'tubeId new skill', + version: 4, + level: 4, + hint_i18n: { + fr: 'hint_i18n.fr new skill', + en: 'hint_i18n.en new skill', + nl: 'hint_i18n.nl new skill', + }, + }, + ], + challenges: [ + { + id: 'aboutToBeRefreshedChallengeId', + instruction: 'instruction About to be refreshed Epreuve - new', + alternativeInstruction: 'alternativeInstruction About to be refreshed Epreuve - new', + proposals: 'proposals About to be refreshed Epreuve - new', + type: 'type About to be refreshed Epreuve - new', + solution: 'solution About to be refreshed Epreuve - new', + solutionToDisplay: 'solutionToDisplay About to be refreshed Epreuve - new', + t1Status: true, + t2Status: true, + t3Status: true, + status: 'status About to be refreshed Epreuve - new', + genealogy: 'genealogy About to be refreshed Epreuve - new', + accessibility1: 'accessibility1 About to be refreshed Epreuve - new', + accessibility2: 'accessibility2 About to be refreshed Epreuve - new', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing About to be refreshed Epreuve - new', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 8, + shuffled: true, + illustrationAlt: 'illustrationAlt About to be refreshed Epreuve - new', + illustrationUrl: 'illustrationUrl About to be refreshed Epreuve - new', + attachments: [ + 'attachment1 About to be refreshed Epreuve - new', + 'attachment2 About to be refreshed Epreuve - new', + ], + responsive: 'responsive About to be refreshed Epreuve - new', + alpha: 1.1, + delta: 3.3, + autoReply: true, + focusable: true, + format: 'format About to be refreshed Epreuve - new', + timer: 180, + embedHeight: 800, + embedUrl: 'embedUrl About to be refreshed Epreuve - new', + embedTitle: 'embedTitle About to be refreshed Epreuve - new', + locales: ['fr'], + competenceId: 'competenceId About to be refreshed Epreuve - new', + skillId: 'skillId About to be refreshed Epreuve - new', + }, + { + id: 'untouchedChallengeId', + instruction: 'instruction Untouched Epreuve', + alternativeInstruction: 'alternativeInstruction Untouched Epreuve', + proposals: 'proposals Untouched Epreuve', + type: 'type Untouched Epreuve', + solution: 'solution Untouched Epreuve', + solutionToDisplay: 'solutionToDisplay Untouched Epreuve', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status Untouched Epreuve', + genealogy: 'genealogy Untouched Epreuve', + accessibility1: 'accessibility1 Untouched Epreuve', + accessibility2: 'accessibility2 Untouched Epreuve', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: false, + deafAndHardOfHearing: 'deafAndHardOfHearing Untouched Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt Untouched Epreuve', + illustrationUrl: 'illustrationUrl Untouched Epreuve', + attachments: [], + responsive: 'responsive Untouched Epreuve', + alpha: 77, + delta: 66, + autoReply: true, + focusable: false, + format: 'format Untouched Epreuve', + timer: null, + embedHeight: 1800, + embedUrl: 'embedUrl Untouched Epreuve', + embedTitle: 'embedTitle Untouched Epreuve', + locales: ['fr', 'en'], + competenceId: 'competenceId Untouched Epreuve', + skillId: 'skillId Untouched Epreuve', + }, + { + id: 'newChallengeId', + instruction: 'instruction New Epreuve', + alternativeInstruction: 'alternativeInstruction New Epreuve', + proposals: 'proposals New Epreuve', + type: 'type New Epreuve', + solution: 'solution New Epreuve', + solutionToDisplay: 'solutionToDisplay New Epreuve', + t1Status: false, + t2Status: false, + t3Status: true, + status: 'status New Epreuve', + genealogy: 'genealogy New Epreuve', + accessibility1: 'accessibility1 New Epreuve', + accessibility2: 'accessibility2 New Epreuve', + requireGafamWebsiteAccess: false, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing New Epreuve', + isAwarenessChallenge: false, + toRephrase: true, + alternativeVersion: 15, + shuffled: true, + illustrationAlt: 'illustrationAlt New Epreuve', + illustrationUrl: 'illustrationUrl New Epreuve', + attachments: ['attachment1 New Epreuve'], + responsive: 'responsive New Epreuve', + alpha: 100.1, + delta: 200.2, + autoReply: false, + focusable: true, + format: 'format New Epreuve', + timer: 250, + embedHeight: null, + embedUrl: 'embedUrl New Epreuve', + embedTitle: 'embedTitle New Epreuve', + locales: ['fr', 'nl'], + competenceId: 'competenceId New Epreuve', + skillId: 'skillId New Epreuve', + }, + ], + courses: [ + { + id: 'aboutToBeRefreshedCourseId', + name: 'name About to be refreshed Course - new', + description: 'description About to be refreshed Course - new', + isActive: true, + competences: ['competenceId About to be refreshed Course - new'], + challenges: ['challengeId About to be refreshed Course - new'], + }, + { + id: 'untouchedCourseId', + name: 'name Untouched', + description: 'description Untouched', + isActive: false, + competences: ['competenceId Untouched'], + challenges: ['challengeId Untouched'], + }, + { + id: 'newCourseId', + name: 'name New', + description: 'description New', + isActive: true, + competences: ['competenceId New'], + challenges: ['challengeId New'], + }, + ], + tutorials: [ + { + id: 'aboutToBeRefreshedTutorialId', + duration: 'duration About to be refreshed Tutoriel - new', + format: 'format About to be refreshed Tutoriel - new', + title: 'title About to be refreshed Tutoriel - new', + source: 'source About to be refreshed Tutoriel - new', + link: 'link About to be refreshed Tutoriel - new', + locale: 'fr', + }, + { + id: 'untouchedTutorialId', + duration: 'duration Untouched Tutorial', + format: 'format Untouched Tutorial', + title: 'title Untouched Tutorial', + source: 'source Untouched Tutorial', + link: 'link Untouched Tutorial', + locale: 'nl', + }, + { + id: 'newTutorialId', + duration: 'duration New Tutorial', + format: 'format New Tutorial', + title: 'title New Tutorial', + source: 'source New Tutorial', + link: 'link New Tutorial', + locale: 'fr', + }, + ], + missions: [ + { + id: 1, + status: 'status About to be refreshed Mission - new', + name_i18n: { + fr: 'name FR About to be refreshed Mission - new', + en: 'name EN About to be refreshed Mission - new', + }, + content: { some: 'content About to be refreshed Mission - new' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR About to be refreshed Mission - new', + en: 'learningObjectives EN About to be refreshed Mission - new', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR About to be refreshed Mission - new', + en: 'validatedObjectives EN About to be refreshed Mission - new', + }, + introductionMediaType: 'introductionMediaType About to be refreshed Mission - new', + introductionMediaUrl: 'introductionMediaUrl About to be refreshed Mission - new', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR About to be refreshed Mission - new', + en: 'introductionMediaAlt EN About to be refreshed Mission - new', + }, + documentationUrl: 'documentationUrl About to be refreshed Mission - new', + cardImageUrl: 'cardImageUrl About to be refreshed Mission - new', + competenceId: 'competenceId About to be refreshed Mission - new', + }, + { + id: 2, + status: 'status Untouched Mission', + name_i18n: { + fr: 'name FR Untouched Mission', + en: 'name EN Untouched Mission', + }, + content: { some: 'content Untouched Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Untouched Mission', + en: 'learningObjectives EN Untouched Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Untouched Mission', + en: 'validatedObjectives EN Untouched Mission', + }, + introductionMediaType: 'introductionMediaType Untouched Mission', + introductionMediaUrl: 'introductionMediaUrl Untouched Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Untouched Mission', + en: 'introductionMediaAlt EN Untouched Mission', + }, + documentationUrl: 'documentationUrl Untouched Mission', + cardImageUrl: 'cardImageUrl Untouched Mission', + competenceId: 'competenceId Untouched Mission', + }, + { + id: 4, + status: 'status New Mission', + name_i18n: { + fr: 'name FR New Mission', + en: 'name EN New Mission', + }, + content: { some: 'content New Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR New Mission', + en: 'learningObjectives EN New Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR New Mission', + en: 'validatedObjectives EN New Mission', + }, + introductionMediaType: 'introductionMediaType New Mission', + introductionMediaUrl: 'introductionMediaUrl New Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR New Mission', + en: 'introductionMediaAlt EN New Mission', + }, + documentationUrl: 'documentationUrl New Mission', + cardImageUrl: 'cardImageUrl New Mission', + competenceId: 'competenceId New Mission', + }, + ], + }; + + databaseBuilder.factory.learningContent.buildFramework({ + id: 'aboutToBeRefreshedFrameworkId', + name: 'name About to be refreshed Framework - old', + }); + databaseBuilder.factory.learningContent.buildFramework({ + id: 'untouchedFrameworkId', + name: 'name Untouched Framework', + }); + databaseBuilder.factory.learningContent.buildFramework({ + id: 'missingFrameworkId', + name: 'name Missing Framework', + }); + databaseBuilder.factory.learningContent.buildArea({ + id: 'aboutToBeRefreshedAreaId', + name: 'name About to be refreshed Domaine - old', + code: 'code About to be refreshed Domaine - old', + title_i18n: { + fr: 'title_i18n FR About to be refreshed Domaine - old', + nl: 'title_i18n NL About to be refreshed Domaine - old', + }, + color: 'color About to be refreshed Domaine - old', + frameworkId: 'frameworkId About to be refreshed Domaine - old', + competenceIds: [ + 'competenceId1 About to be refreshed Domaine - old', + 'competenceId2 About to be refreshed Domaine - old', + ], + }); + databaseBuilder.factory.learningContent.buildArea({ + id: 'untouchedAreaId', + name: 'name Untouched Domaine', + code: 'code Untouched Domaine', + title_i18n: { fr: 'title_i18n FR Untouched Domaine', nl: 'title_i18n NL Untouched Domaine' }, + color: 'color Untouched Domaine', + frameworkId: 'frameworkId Untouched Domaine', + competenceIds: ['competenceId Untouched Domaine'], + }); + databaseBuilder.factory.learningContent.buildArea({ + id: 'missingAreaId', + name: 'name Missing Domaine', + code: 'code Missing Domaine', + title_i18n: { fr: 'title_i18n FR Missing Domaine', nl: 'title_i18n NL Missing Domaine' }, + color: 'color Missing Domaine', + frameworkId: 'frameworkId Missing Domaine', + competenceIds: ['competenceId Missing Domaine'], + }); + databaseBuilder.factory.learningContent.buildCompetence({ + id: 'aboutToBeRefreshedCompetenceId', + index: 'index About to be refreshed Competence - old', + areaId: 'areaId About to be refreshed Competence - old', + skillIds: ['skillId About to be refreshed Competence - old'], + thematicIds: ['thematicId About to be refreshed Competence - old'], + origin: 'origin About to be refreshed Competence - old', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Competence - old', + nl: 'name_i18n NL About to be refreshed Competence - old', + }, + description_i18n: { + fr: 'description_i18n FR About to be refreshed Competence - old', + en: 'description_i18n EN About to be refreshed Competence - old', + }, + }); + databaseBuilder.factory.learningContent.buildCompetence({ + id: 'untouchedCompetenceId', + index: 'index Untouched Competence', + areaId: 'areaId Untouched Competence', + skillIds: ['skillId Untouched Competence'], + thematicIds: ['thematicId Untouched Competence'], + origin: 'origin Untouched Competence', + name_i18n: { + fr: 'name_i18n FR Untouched Competence', + en: 'name_i18n EN Untouched Competence', + }, + description_i18n: { + fr: 'description_i18n FR Untouched Competence', + nl: 'description_i18n NL Untouched Competence', + }, + }); + databaseBuilder.factory.learningContent.buildCompetence({ + id: 'missingCompetenceId', + index: 'index Missing Competence', + areaId: 'areaId Missing Competence', + skillIds: ['skillId Missing Competence'], + thematicIds: ['thematicId Missing Competence'], + origin: 'origin Missing Competence', + name_i18n: { + fr: 'name_i18n FR Missing Competence', + en: 'name_i18n EN Missing Competence', + }, + description_i18n: { + fr: 'description_i18n FR Missing Competence', + nl: 'description_i18n NL Missing Competence', + }, + }); + databaseBuilder.factory.learningContent.buildThematic({ + id: 'aboutToBeRefreshedThematicId', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Thematique - old', + en: 'name_i18n EN About to be refreshed Thematique - old', + }, + index: 1, + competenceId: 'competenceId About to be refreshed Thematique - old', + tubeIds: ['tubeId About to be refreshed Thematique - old'], + }); + databaseBuilder.factory.learningContent.buildThematic({ + id: 'untouchedThematicId', + name_i18n: { + fr: 'name_i18n FR Untouched Thematique', + en: 'name_i18n EN Untouched Thematique', + }, + index: 2, + competenceId: 'competenceId Untouched Thematique', + tubeIds: ['tubeId Untouched Thematique'], + }); + databaseBuilder.factory.learningContent.buildThematic({ + id: 'missingThematicId', + name_i18n: { + fr: 'name_i18n FR Missing Thematique', + en: 'name_i18n EN Missing Thematique', + }, + index: 4, + competenceId: 'competenceId Missing Thematique', + tubeIds: ['tubeId Missing Thematique'], + }); + databaseBuilder.factory.learningContent.buildTube({ + id: 'aboutToBeRefreshedTubeId', + name: 'name About to be refreshed Tube - old', + title: 'title About to be refreshed Tube - old', + description: 'description About to be refreshed Tube - old', + practicalTitle_i18n: { + fr: 'practicalTitle FR About to be refreshed Tube - old', + en: 'practicalTitle EN About to be refreshed Tube - old', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR About to be refreshed Tube - old', + en: 'practicalDescription EN About to be refreshed Tube - old', + }, + competenceId: 'competenceId About to be refreshed Tube - old', + thematicId: 'thematicId About to be refreshed Tube - old', + skillIds: ['skillId About to be refreshed Tube - old'], + isMobileCompliant: false, + isTabletCompliant: true, + }); + databaseBuilder.factory.learningContent.buildTube({ + id: 'untouchedTubeId', + name: 'name Untouched Tube', + title: 'title Untouched Tube', + description: 'description Untouched Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Untouched Tube', + en: 'practicalTitle EN Untouched Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Untouched Tube', + en: 'practicalDescription EN Untouched Tube', + }, + competenceId: 'competenceId Untouched Tube', + thematicId: 'thematicId Untouched Tube', + skillIds: ['skillId Untouched Tube'], + isMobileCompliant: false, + isTabletCompliant: true, + }); + databaseBuilder.factory.learningContent.buildTube({ + id: 'missingTubeId', + name: 'name Missing Tube', + title: 'title Missing Tube', + description: 'description Missing Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Missing Tube', + en: 'practicalTitle EN Missing Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Missing Tube', + en: 'practicalDescription EN Missing Tube', + }, + competenceId: 'competenceId Missing Tube', + thematicId: 'thematicId Missing Tube', + skillIds: ['skillId Missing Tube'], + isMobileCompliant: true, + isTabletCompliant: true, + }); + databaseBuilder.factory.learningContent.buildSkill({ + id: 'aboutToBeRefreshedSkillId', + name: 'name about to be refreshed skill - old', + hintStatus: 'hintStatus about to be refreshed skill - old', + tutorialIds: ['tutorialId1 about to be refreshed skill - old', 'tutorialId2 about to be refreshed skill - old'], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 about to be refreshed skill - old', + 'learningMoreTutorialId2 about to be refreshed skill - old', + ], + pixValue: 1, + competenceId: 'competenceId about to be refreshed skill - old', + status: 'status about to be refreshed skill - old', + tubeId: 'tubeId about to be refreshed skill - old', + version: 1, + level: 1, + hint_i18n: { + fr: 'hint_i18n.fr about to be refreshed skill - old', + en: 'hint_i18n.en about to be refreshed skill - old', + nl: 'hint_i18n.nl about to be refreshed skill - old', + }, + }); + databaseBuilder.factory.learningContent.buildSkill({ + id: 'untouchedSkillId', + name: 'name untouched skill', + hintStatus: 'hintStatus untouched skill', + tutorialIds: ['tutorialId1 untouched skill', 'tutorialId2 untouched skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 untouched skill', 'learningMoreTutorialId2 untouched skill'], + pixValue: 3, + competenceId: 'competenceId untouched skill', + status: 'status untouched skill', + tubeId: 'tubeId untouched skill', + version: 3, + level: 3, + hint_i18n: { + fr: 'hint_i18n.fr untouched skill', + en: 'hint_i18n.en untouched skill', + nl: 'hint_i18n.nl untouched skill', + }, + }); + databaseBuilder.factory.learningContent.buildSkill({ + id: 'missingSkillId', + name: 'name missing skill', + hintStatus: 'hintStatus missing skill', + tutorialIds: ['tutorialId1 missing skill', 'tutorialId2 missing skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 missing skill', 'learningMoreTutorialId2 missing skill'], + pixValue: 5, + competenceId: 'competenceId missing skill', + status: 'status missing skill', + tubeId: 'tubeId missing skill', + version: 5, + level: 5, + hint_i18n: { + fr: 'hint_i18n.fr missing skill', + en: 'hint_i18n.en missing skill', + nl: 'hint_i18n.nl missing skill', + }, + }); + databaseBuilder.factory.learningContent.buildChallenge({ + id: 'aboutToBeRefreshedChallengeId', + instruction: 'instruction About to be refreshed Epreuve - old', + alternativeInstruction: 'alternativeInstruction About to be refreshed Epreuve - old', + proposals: 'proposals About to be refreshed Epreuve - old', + type: 'type About to be refreshed Epreuve - old', + solution: 'solution About to be refreshed Epreuve - old', + solutionToDisplay: 'solutionToDisplay About to be refreshed Epreuve - old', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status About to be refreshed Epreuve - old', + genealogy: 'genealogy About to be refreshed Epreuve - old', + accessibility1: 'accessibility1 About to be refreshed Epreuve - old', + accessibility2: 'accessibility2 About to be refreshed Epreuve - old', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing About to be refreshed Epreuve - old', + isAwarenessChallenge: false, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt About to be refreshed Epreuve - old', + illustrationUrl: 'illustrationUrl About to be refreshed Epreuve - old', + attachments: ['attachment1 About to be refreshed Epreuve - old'], + responsive: 'responsive About to be refreshed Epreuve - old', + alpha: 10.5, + delta: 3.3, + autoReply: true, + focusable: false, + format: 'format About to be refreshed Epreuve - old', + timer: 180, + embedHeight: 2800, + embedUrl: 'embedUrl About to be refreshed Epreuve - old', + embedTitle: 'embedTitle About to be refreshed Epreuve - old', + locales: ['fr', 'nl'], + competenceId: 'competenceId About to be refreshed Epreuve - old', + skillId: 'skillId About to be refreshed Epreuve - old', + }); + databaseBuilder.factory.learningContent.buildChallenge({ + id: 'untouchedChallengeId', + instruction: 'instruction Untouched Epreuve', + alternativeInstruction: 'alternativeInstruction Untouched Epreuve', + proposals: 'proposals Untouched Epreuve', + type: 'type Untouched Epreuve', + solution: 'solution Untouched Epreuve', + solutionToDisplay: 'solutionToDisplay Untouched Epreuve', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status Untouched Epreuve', + genealogy: 'genealogy Untouched Epreuve', + accessibility1: 'accessibility1 Untouched Epreuve', + accessibility2: 'accessibility2 Untouched Epreuve', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: false, + deafAndHardOfHearing: 'deafAndHardOfHearing Untouched Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt Untouched Epreuve', + illustrationUrl: 'illustrationUrl Untouched Epreuve', + attachments: [], + responsive: 'responsive Untouched Epreuve', + alpha: 77, + delta: 66, + autoReply: true, + focusable: false, + format: 'format Untouched Epreuve', + timer: null, + embedHeight: 1800, + embedUrl: 'embedUrl Untouched Epreuve', + embedTitle: 'embedTitle Untouched Epreuve', + locales: ['fr', 'en'], + competenceId: 'competenceId Untouched Epreuve', + skillId: 'skillId Untouched Epreuve', + }); + databaseBuilder.factory.learningContent.buildChallenge({ + id: 'missingChallengeId', + instruction: 'instruction Missing Epreuve', + alternativeInstruction: 'alternativeInstruction Missing Epreuve', + proposals: 'proposals Missing Epreuve', + type: 'type Missing Epreuve', + solution: 'solution Missing Epreuve', + solutionToDisplay: 'solutionToDisplay Missing Epreuve', + t1Status: false, + t2Status: true, + t3Status: true, + status: 'status Missing Epreuve', + genealogy: 'genealogy Missing Epreuve', + accessibility1: 'accessibility1 Missing Epreuve', + accessibility2: 'accessibility2 Missing Epreuve', + requireGafamWebsiteAccess: false, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing Missing Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 99, + shuffled: true, + illustrationAlt: 'illustrationAlt Missing Epreuve', + illustrationUrl: 'illustrationUrl Missing Epreuve', + attachments: ['attachment8 Missing Epreuve'], + responsive: 'responsive Missing Epreuve', + alpha: 58.2, + delta: 20.4, + autoReply: true, + focusable: true, + format: 'format Missing Epreuve', + timer: null, + embedHeight: 55, + embedUrl: 'embedUrl Missing Epreuve', + embedTitle: 'embedTitle Missing Epreuve', + locales: ['en', 'nl'], + competenceId: 'competenceId Missing Epreuve', + skillId: 'skillId Missing Epreuve', + }); + databaseBuilder.factory.learningContent.buildCourse({ + id: 'aboutToBeRefreshedCourseId', + name: 'name About to be refreshed Course - old', + description: 'description About to be refreshed Course - old', + isActive: false, + competences: [ + 'competenceId1 About to be refreshed Course - old', + 'competenceId2 About to be refreshed Course - old', + ], + challenges: [ + 'challengeId1 About to be refreshed Course - old', + 'challengeId2 About to be refreshed Course - old', + ], + }); + databaseBuilder.factory.learningContent.buildCourse({ + id: 'untouchedCourseId', + name: 'name Untouched', + description: 'description Untouched', + isActive: false, + competences: ['competenceId Untouched'], + challenges: ['challengeId Untouched'], + }); + databaseBuilder.factory.learningContent.buildCourse({ + id: 'missingCourseId', + name: 'name Missing', + description: 'description Missing', + isActive: true, + competences: ['competenceId Missing'], + challenges: ['challengeId Missing'], + }); + databaseBuilder.factory.learningContent.buildTutorial({ + id: 'aboutToBeRefreshedTutorialId', + duration: 'duration About to be refreshed Tutoriel - old', + format: 'format About to be refreshed Tutoriel - old', + title: 'title About to be refreshed Tutoriel - old', + source: 'source About to be refreshed Tutoriel - old', + link: 'link About to be refreshed Tutoriel - old', + locale: 'en', + }); + databaseBuilder.factory.learningContent.buildTutorial({ + id: 'untouchedTutorialId', + duration: 'duration Untouched Tutorial', + format: 'format Untouched Tutorial', + title: 'title Untouched Tutorial', + source: 'source Untouched Tutorial', + link: 'link Untouched Tutorial', + locale: 'nl', + }); + databaseBuilder.factory.learningContent.buildTutorial({ + id: 'missingTutorialId', + duration: 'duration Missing Tutorial', + format: 'format Missing Tutorial', + title: 'title Missing Tutorial', + source: 'source Missing Tutorial', + link: 'link Missing Tutorial', + locale: 'fr', + }); + databaseBuilder.factory.learningContent.buildMission({ + id: 1, + status: 'status About to be refreshed Mission - old', + name_i18n: { + fr: 'name FR About to be refreshed Mission - old', + en: 'name EN About to be refreshed Mission - old', + }, + content: { some: 'content About to be refreshed Mission - old' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR About to be refreshed Mission - old', + en: 'learningObjectives EN About to be refreshed Mission - old', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR About to be refreshed Mission - old', + en: 'validatedObjectives EN About to be refreshed Mission - old', + }, + introductionMediaType: 'introductionMediaType About to be refreshed Mission - old', + introductionMediaUrl: 'introductionMediaUrl About to be refreshed Mission - old', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR About to be refreshed Mission - old', + en: 'introductionMediaAlt EN About to be refreshed Mission - old', + }, + documentationUrl: 'documentationUrl About to be refreshed Mission - old', + cardImageUrl: 'cardImageUrl About to be refreshed Mission - old', + competenceId: 'competenceId About to be refreshed Mission - old', + }); + databaseBuilder.factory.learningContent.buildMission({ + id: 2, + status: 'status Untouched Mission', + name_i18n: { + fr: 'name FR Untouched Mission', + en: 'name EN Untouched Mission', + }, + content: { some: 'content Untouched Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Untouched Mission', + en: 'learningObjectives EN Untouched Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Untouched Mission', + en: 'validatedObjectives EN Untouched Mission', + }, + introductionMediaType: 'introductionMediaType Untouched Mission', + introductionMediaUrl: 'introductionMediaUrl Untouched Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Untouched Mission', + en: 'introductionMediaAlt EN Untouched Mission', + }, + documentationUrl: 'documentationUrl Untouched Mission', + cardImageUrl: 'cardImageUrl Untouched Mission', + competenceId: 'competenceId Untouched Mission', + }); + databaseBuilder.factory.learningContent.buildMission({ + id: 3, + status: 'status Missing Mission', + name_i18n: { + fr: 'name FR Missing Mission', + en: 'name EN Missing Mission', + }, + content: { some: 'content Missing Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Missing Mission', + en: 'learningObjectives EN Missing Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Missing Mission', + en: 'validatedObjectives EN Missing Mission', + }, + introductionMediaType: 'introductionMediaType Missing Mission', + introductionMediaUrl: 'introductionMediaUrl Missing Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Missing Mission', + en: 'introductionMediaAlt EN Missing Mission', + }, + documentationUrl: 'documentationUrl Missing Mission', + cardImageUrl: 'cardImageUrl Missing Mission', + competenceId: 'competenceId Missing Mission', + }); + await databaseBuilder.commit(); + }); + + afterEach(async function () { + await knex('learningcontent.frameworks').truncate(); + await knex('learningcontent.areas').truncate(); + await knex('learningcontent.competences').truncate(); + await knex('learningcontent.thematics').truncate(); + await knex('learningcontent.tubes').truncate(); + await knex('learningcontent.challenges').truncate(); + await knex('learningcontent.courses').truncate(); + await knex('learningcontent.tutorials').truncate(); + await knex('learningcontent.missions').truncate(); + }); + + describe('#handle', function () { + context('success', function () { + beforeEach(function () { + lcmsApiCall = nock('https://lcms-test.pix.fr/api') + .post('/releases') + .matchHeader('Authorization', 'Bearer test-api-key') + .reply(201, { content: newLearningContent }); + }); + + it("should update learning content in database with what's in the newly created release", async function () { + // when + await lcmsCreateReleaseJobController.handle(); + + // then + const updatedFrameworks = await knex.select('*').from('learningcontent.frameworks').orderBy('name'); + expect(updatedFrameworks).to.deep.equal([ + { id: 'aboutToBeRefreshedFrameworkId', name: 'name About to be refreshed Framework - new' }, + { id: 'missingFrameworkId', name: 'name Missing Framework' }, + { id: 'newFrameworkId', name: 'name New Framework' }, + { id: 'untouchedFrameworkId', name: 'name Untouched Framework' }, + ]); + const updatedAreas = await knex.select('*').from('learningcontent.areas').orderBy('id'); + expect(updatedAreas).to.deep.equal([ + { + id: 'aboutToBeRefreshedAreaId', + name: 'name About to be refreshed Domaine - new', + code: 'code About to be refreshed Domaine - new', + title_i18n: { + fr: 'title_i18n FR About to be refreshed Domaine - new', + en: 'title_i18n EN About to be refreshed Domaine - new', + }, + color: 'color About to be refreshed Domaine - new', + frameworkId: 'frameworkId About to be refreshed Domaine - new', + competenceIds: ['competenceId1 About to be refreshed Domaine - new'], + }, + { + id: 'missingAreaId', + name: 'name Missing Domaine', + code: 'code Missing Domaine', + title_i18n: { fr: 'title_i18n FR Missing Domaine', nl: 'title_i18n NL Missing Domaine' }, + color: 'color Missing Domaine', + frameworkId: 'frameworkId Missing Domaine', + competenceIds: ['competenceId Missing Domaine'], + }, + { + id: 'newAreaId', + name: 'name New Domaine', + code: 'code New Domaine', + title_i18n: { fr: 'title_i18n FR New Domaine', nl: 'title_i18n NL New Domaine' }, + color: 'color New Domaine', + frameworkId: 'frameworkId New Domaine', + competenceIds: ['competenceId1 New Domaine', 'competenceId2 New Domaine'], + }, + { + id: 'untouchedAreaId', + name: 'name Untouched Domaine', + code: 'code Untouched Domaine', + title_i18n: { fr: 'title_i18n FR Untouched Domaine', nl: 'title_i18n NL Untouched Domaine' }, + color: 'color Untouched Domaine', + frameworkId: 'frameworkId Untouched Domaine', + competenceIds: ['competenceId Untouched Domaine'], + }, + ]); + const updatedCompetences = await knex.select('*').from('learningcontent.competences').orderBy('id'); + expect(updatedCompetences).to.deep.equal([ + { + id: 'aboutToBeRefreshedCompetenceId', + index: 'index About to be refreshed Competence - new', + areaId: 'areaId About to be refreshed Competence - new', + skillIds: ['skillId About to be refreshed Competence - new'], + thematicIds: ['thematicId About to be refreshed Competence - new'], + origin: 'origin About to be refreshed Competence - new', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Competence - new', + en: 'name_i18n EN About to be refreshed Competence - new', + }, + description_i18n: { + fr: 'description_i18n FR About to be refreshed Competence - new', + nl: 'description_i18n NL About to be refreshed Competence - new', + }, + }, + { + id: 'missingCompetenceId', + index: 'index Missing Competence', + areaId: 'areaId Missing Competence', + skillIds: ['skillId Missing Competence'], + thematicIds: ['thematicId Missing Competence'], + origin: 'origin Missing Competence', + name_i18n: { + fr: 'name_i18n FR Missing Competence', + en: 'name_i18n EN Missing Competence', + }, + description_i18n: { + fr: 'description_i18n FR Missing Competence', + nl: 'description_i18n NL Missing Competence', + }, + }, + { + id: 'newCompetenceId', + index: 'index New Competence', + areaId: 'areaId New Competence', + skillIds: ['skillId New Competence'], + thematicIds: ['thematicId New Competence'], + origin: 'origin New Competence', + name_i18n: { + fr: 'name_i18n FR New Competence', + en: 'name_i18n EN New Competence', + }, + description_i18n: { + fr: 'description_i18n FR New Competence', + nl: 'description_i18n NL New Competence', + }, + }, + { + id: 'untouchedCompetenceId', + index: 'index Untouched Competence', + areaId: 'areaId Untouched Competence', + skillIds: ['skillId Untouched Competence'], + thematicIds: ['thematicId Untouched Competence'], + origin: 'origin Untouched Competence', + name_i18n: { + fr: 'name_i18n FR Untouched Competence', + en: 'name_i18n EN Untouched Competence', + }, + description_i18n: { + fr: 'description_i18n FR Untouched Competence', + nl: 'description_i18n NL Untouched Competence', + }, + }, + ]); + const updatedThematics = await knex.select('*').from('learningcontent.thematics').orderBy('id'); + expect(updatedThematics).to.deep.equal([ + { + id: 'aboutToBeRefreshedThematicId', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Thematique - new', + nl: 'name_i18n NL About to be refreshed Thematique - new', + }, + index: 11, + competenceId: 'competenceId About to be refreshed Thematique - new', + tubeIds: ['tubeId About to be refreshed Thematique - new'], + }, + { + id: 'missingThematicId', + name_i18n: { + fr: 'name_i18n FR Missing Thematique', + en: 'name_i18n EN Missing Thematique', + }, + index: 4, + competenceId: 'competenceId Missing Thematique', + tubeIds: ['tubeId Missing Thematique'], + }, + { + id: 'newThematicId', + name_i18n: { + fr: 'name_i18n FR New Thematique', + en: 'name_i18n EN New Thematique', + }, + index: 3, + competenceId: 'competenceId New Thematique', + tubeIds: ['tubeId New Thematique'], + }, + { + id: 'untouchedThematicId', + name_i18n: { + fr: 'name_i18n FR Untouched Thematique', + en: 'name_i18n EN Untouched Thematique', + }, + index: 2, + competenceId: 'competenceId Untouched Thematique', + tubeIds: ['tubeId Untouched Thematique'], + }, + ]); + const updatedTubes = await knex.select('*').from('learningcontent.tubes').orderBy('id'); + expect(updatedTubes).to.deep.equal([ + { + id: 'aboutToBeRefreshedTubeId', + name: 'name About to be refreshed Tube - new', + title: 'title About to be refreshed Tube - new', + description: 'description About to be refreshed Tube - new', + practicalTitle_i18n: { + fr: 'practicalTitle FR About to be refreshed Tube - new', + nl: 'practicalTitle NL About to be refreshed Tube - new', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR About to be refreshed Tube - new', + en: 'practicalDescription EN About to be refreshed Tube - new', + }, + competenceId: 'competenceId About to be refreshed Tube - new', + thematicId: 'thematicId About to be refreshed Tube - new', + skillIds: ['skillId About to be refreshed Tube - new'], + isMobileCompliant: true, + isTabletCompliant: true, + }, + { + id: 'missingTubeId', + name: 'name Missing Tube', + title: 'title Missing Tube', + description: 'description Missing Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Missing Tube', + en: 'practicalTitle EN Missing Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Missing Tube', + en: 'practicalDescription EN Missing Tube', + }, + competenceId: 'competenceId Missing Tube', + thematicId: 'thematicId Missing Tube', + skillIds: ['skillId Missing Tube'], + isMobileCompliant: true, + isTabletCompliant: true, + }, + { + id: 'newTubeId', + name: 'name New Tube', + title: 'title New Tube', + description: 'description New Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR New Tube', + en: 'practicalTitle EN New Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR New Tube', + en: 'practicalDescription EN New Tube', + }, + competenceId: 'competenceId New Tube', + thematicId: 'thematicId New Tube', + skillIds: ['skillId New Tube'], + isMobileCompliant: true, + isTabletCompliant: false, + }, + { + id: 'untouchedTubeId', + name: 'name Untouched Tube', + title: 'title Untouched Tube', + description: 'description Untouched Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Untouched Tube', + en: 'practicalTitle EN Untouched Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Untouched Tube', + en: 'practicalDescription EN Untouched Tube', + }, + competenceId: 'competenceId Untouched Tube', + thematicId: 'thematicId Untouched Tube', + skillIds: ['skillId Untouched Tube'], + isMobileCompliant: false, + isTabletCompliant: true, + }, + ]); + const updatedSkills = await knex.select('*').from('learningcontent.skills').orderBy('id'); + expect(updatedSkills).to.deep.equal([ + { + id: 'aboutToBeRefreshedSkillId', + name: 'name about to be refreshed skill - new', + hintStatus: 'hintStatus about to be refreshed skill - new', + tutorialIds: [ + 'tutorialId1 about to be refreshed skill - new', + 'tutorialId2 about to be refreshed skill - new', + ], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 about to be refreshed skill - new', + 'learningMoreTutorialId2 about to be refreshed skill - new', + ], + pixValue: 2, + competenceId: 'competenceId about to be refreshed skill - new', + status: 'status about to be refreshed skill - new', + tubeId: 'tubeId about to be refreshed skill - new', + version: 2, + level: 2, + hint_i18n: { + fr: 'hint_i18n.fr about to be refreshed skill - new', + en: 'hint_i18n.en about to be refreshed skill - new', + nl: 'hint_i18n.nl about to be refreshed skill - new', + }, + }, + { + id: 'missingSkillId', + name: 'name missing skill', + hintStatus: 'hintStatus missing skill', + tutorialIds: ['tutorialId1 missing skill', 'tutorialId2 missing skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 missing skill', 'learningMoreTutorialId2 missing skill'], + pixValue: 5, + competenceId: 'competenceId missing skill', + status: 'status missing skill', + tubeId: 'tubeId missing skill', + version: 5, + level: 5, + hint_i18n: { + fr: 'hint_i18n.fr missing skill', + en: 'hint_i18n.en missing skill', + nl: 'hint_i18n.nl missing skill', + }, + }, + { + id: 'newSkillId', + name: 'name new skill', + hintStatus: 'hintStatus new skill', + tutorialIds: ['tutorialId1 new skill', 'tutorialId2 new skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 new skill', 'learningMoreTutorialId2 new skill'], + pixValue: 4, + competenceId: 'competenceId new skill', + status: 'status new skill', + tubeId: 'tubeId new skill', + version: 4, + level: 4, + hint_i18n: { + fr: 'hint_i18n.fr new skill', + en: 'hint_i18n.en new skill', + nl: 'hint_i18n.nl new skill', + }, + }, + { + id: 'untouchedSkillId', + name: 'name untouched skill', + hintStatus: 'hintStatus untouched skill', + tutorialIds: ['tutorialId1 untouched skill', 'tutorialId2 untouched skill'], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 untouched skill', + 'learningMoreTutorialId2 untouched skill', + ], + pixValue: 3, + competenceId: 'competenceId untouched skill', + status: 'status untouched skill', + tubeId: 'tubeId untouched skill', + version: 3, + level: 3, + hint_i18n: { + fr: 'hint_i18n.fr untouched skill', + en: 'hint_i18n.en untouched skill', + nl: 'hint_i18n.nl untouched skill', + }, + }, + ]); + const updatedChallenges = await knex.select('*').from('learningcontent.challenges').orderBy('id'); + expect(updatedChallenges).to.deep.equal([ + { + id: 'aboutToBeRefreshedChallengeId', + instruction: 'instruction About to be refreshed Epreuve - new', + alternativeInstruction: 'alternativeInstruction About to be refreshed Epreuve - new', + proposals: 'proposals About to be refreshed Epreuve - new', + type: 'type About to be refreshed Epreuve - new', + solution: 'solution About to be refreshed Epreuve - new', + solutionToDisplay: 'solutionToDisplay About to be refreshed Epreuve - new', + t1Status: true, + t2Status: true, + t3Status: true, + status: 'status About to be refreshed Epreuve - new', + genealogy: 'genealogy About to be refreshed Epreuve - new', + accessibility1: 'accessibility1 About to be refreshed Epreuve - new', + accessibility2: 'accessibility2 About to be refreshed Epreuve - new', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing About to be refreshed Epreuve - new', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 8, + shuffled: true, + illustrationAlt: 'illustrationAlt About to be refreshed Epreuve - new', + illustrationUrl: 'illustrationUrl About to be refreshed Epreuve - new', + attachments: [ + 'attachment1 About to be refreshed Epreuve - new', + 'attachment2 About to be refreshed Epreuve - new', + ], + responsive: 'responsive About to be refreshed Epreuve - new', + alpha: 1.1, + delta: 3.3, + autoReply: true, + focusable: true, + format: 'format About to be refreshed Epreuve - new', + timer: 180, + embedHeight: 800, + embedUrl: 'embedUrl About to be refreshed Epreuve - new', + embedTitle: 'embedTitle About to be refreshed Epreuve - new', + locales: ['fr'], + competenceId: 'competenceId About to be refreshed Epreuve - new', + skillId: 'skillId About to be refreshed Epreuve - new', + }, + { + id: 'missingChallengeId', + instruction: 'instruction Missing Epreuve', + alternativeInstruction: 'alternativeInstruction Missing Epreuve', + proposals: 'proposals Missing Epreuve', + type: 'type Missing Epreuve', + solution: 'solution Missing Epreuve', + solutionToDisplay: 'solutionToDisplay Missing Epreuve', + t1Status: false, + t2Status: true, + t3Status: true, + status: 'status Missing Epreuve', + genealogy: 'genealogy Missing Epreuve', + accessibility1: 'accessibility1 Missing Epreuve', + accessibility2: 'accessibility2 Missing Epreuve', + requireGafamWebsiteAccess: false, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing Missing Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 99, + shuffled: true, + illustrationAlt: 'illustrationAlt Missing Epreuve', + illustrationUrl: 'illustrationUrl Missing Epreuve', + attachments: ['attachment8 Missing Epreuve'], + responsive: 'responsive Missing Epreuve', + alpha: 58.2, + delta: 20.4, + autoReply: true, + focusable: true, + format: 'format Missing Epreuve', + timer: null, + embedHeight: 55, + embedUrl: 'embedUrl Missing Epreuve', + embedTitle: 'embedTitle Missing Epreuve', + locales: ['en', 'nl'], + competenceId: 'competenceId Missing Epreuve', + skillId: 'skillId Missing Epreuve', + }, + { + id: 'newChallengeId', + instruction: 'instruction New Epreuve', + alternativeInstruction: 'alternativeInstruction New Epreuve', + proposals: 'proposals New Epreuve', + type: 'type New Epreuve', + solution: 'solution New Epreuve', + solutionToDisplay: 'solutionToDisplay New Epreuve', + t1Status: false, + t2Status: false, + t3Status: true, + status: 'status New Epreuve', + genealogy: 'genealogy New Epreuve', + accessibility1: 'accessibility1 New Epreuve', + accessibility2: 'accessibility2 New Epreuve', + requireGafamWebsiteAccess: false, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing New Epreuve', + isAwarenessChallenge: false, + toRephrase: true, + alternativeVersion: 15, + shuffled: true, + illustrationAlt: 'illustrationAlt New Epreuve', + illustrationUrl: 'illustrationUrl New Epreuve', + attachments: ['attachment1 New Epreuve'], + responsive: 'responsive New Epreuve', + alpha: 100.1, + delta: 200.2, + autoReply: false, + focusable: true, + format: 'format New Epreuve', + timer: 250, + embedHeight: null, + embedUrl: 'embedUrl New Epreuve', + embedTitle: 'embedTitle New Epreuve', + locales: ['fr', 'nl'], + competenceId: 'competenceId New Epreuve', + skillId: 'skillId New Epreuve', + }, + { + id: 'untouchedChallengeId', + instruction: 'instruction Untouched Epreuve', + alternativeInstruction: 'alternativeInstruction Untouched Epreuve', + proposals: 'proposals Untouched Epreuve', + type: 'type Untouched Epreuve', + solution: 'solution Untouched Epreuve', + solutionToDisplay: 'solutionToDisplay Untouched Epreuve', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status Untouched Epreuve', + genealogy: 'genealogy Untouched Epreuve', + accessibility1: 'accessibility1 Untouched Epreuve', + accessibility2: 'accessibility2 Untouched Epreuve', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: false, + deafAndHardOfHearing: 'deafAndHardOfHearing Untouched Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt Untouched Epreuve', + illustrationUrl: 'illustrationUrl Untouched Epreuve', + attachments: [], + responsive: 'responsive Untouched Epreuve', + alpha: 77, + delta: 66, + autoReply: true, + focusable: false, + format: 'format Untouched Epreuve', + timer: null, + embedHeight: 1800, + embedUrl: 'embedUrl Untouched Epreuve', + embedTitle: 'embedTitle Untouched Epreuve', + locales: ['fr', 'en'], + competenceId: 'competenceId Untouched Epreuve', + skillId: 'skillId Untouched Epreuve', + }, + ]); + const updatedCourses = await knex.select('*').from('learningcontent.courses').orderBy('id'); + expect(updatedCourses).to.deep.equal([ + { + id: 'aboutToBeRefreshedCourseId', + name: 'name About to be refreshed Course - new', + description: 'description About to be refreshed Course - new', + isActive: true, + competences: ['competenceId About to be refreshed Course - new'], + challenges: ['challengeId About to be refreshed Course - new'], + }, + { + id: 'missingCourseId', + name: 'name Missing', + description: 'description Missing', + isActive: true, + competences: ['competenceId Missing'], + challenges: ['challengeId Missing'], + }, + { + id: 'newCourseId', + name: 'name New', + description: 'description New', + isActive: true, + competences: ['competenceId New'], + challenges: ['challengeId New'], + }, + { + id: 'untouchedCourseId', + name: 'name Untouched', + description: 'description Untouched', + isActive: false, + competences: ['competenceId Untouched'], + challenges: ['challengeId Untouched'], + }, + ]); + const updatedTutorials = await knex.select('*').from('learningcontent.tutorials').orderBy('id'); + expect(updatedTutorials).to.deep.equal([ + { + id: 'aboutToBeRefreshedTutorialId', + duration: 'duration About to be refreshed Tutoriel - new', + format: 'format About to be refreshed Tutoriel - new', + title: 'title About to be refreshed Tutoriel - new', + source: 'source About to be refreshed Tutoriel - new', + link: 'link About to be refreshed Tutoriel - new', + locale: 'fr', + }, + { + id: 'missingTutorialId', + duration: 'duration Missing Tutorial', + format: 'format Missing Tutorial', + title: 'title Missing Tutorial', + source: 'source Missing Tutorial', + link: 'link Missing Tutorial', + locale: 'fr', + }, + { + id: 'newTutorialId', + duration: 'duration New Tutorial', + format: 'format New Tutorial', + title: 'title New Tutorial', + source: 'source New Tutorial', + link: 'link New Tutorial', + locale: 'fr', + }, + { + id: 'untouchedTutorialId', + duration: 'duration Untouched Tutorial', + format: 'format Untouched Tutorial', + title: 'title Untouched Tutorial', + source: 'source Untouched Tutorial', + link: 'link Untouched Tutorial', + locale: 'nl', + }, + ]); + const updatedMissions = await knex.select('*').from('learningcontent.missions').orderBy('id'); + expect(updatedMissions).to.deep.equal([ + { + id: 1, + status: 'status About to be refreshed Mission - new', + name_i18n: { + fr: 'name FR About to be refreshed Mission - new', + en: 'name EN About to be refreshed Mission - new', + }, + content: { some: 'content About to be refreshed Mission - new' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR About to be refreshed Mission - new', + en: 'learningObjectives EN About to be refreshed Mission - new', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR About to be refreshed Mission - new', + en: 'validatedObjectives EN About to be refreshed Mission - new', + }, + introductionMediaType: 'introductionMediaType About to be refreshed Mission - new', + introductionMediaUrl: 'introductionMediaUrl About to be refreshed Mission - new', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR About to be refreshed Mission - new', + en: 'introductionMediaAlt EN About to be refreshed Mission - new', + }, + documentationUrl: 'documentationUrl About to be refreshed Mission - new', + cardImageUrl: 'cardImageUrl About to be refreshed Mission - new', + competenceId: 'competenceId About to be refreshed Mission - new', + }, + { + id: 2, + status: 'status Untouched Mission', + name_i18n: { + fr: 'name FR Untouched Mission', + en: 'name EN Untouched Mission', + }, + content: { some: 'content Untouched Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Untouched Mission', + en: 'learningObjectives EN Untouched Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Untouched Mission', + en: 'validatedObjectives EN Untouched Mission', + }, + introductionMediaType: 'introductionMediaType Untouched Mission', + introductionMediaUrl: 'introductionMediaUrl Untouched Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Untouched Mission', + en: 'introductionMediaAlt EN Untouched Mission', + }, + documentationUrl: 'documentationUrl Untouched Mission', + cardImageUrl: 'cardImageUrl Untouched Mission', + competenceId: 'competenceId Untouched Mission', + }, + { + id: 3, + status: 'status Missing Mission', + name_i18n: { + fr: 'name FR Missing Mission', + en: 'name EN Missing Mission', + }, + content: { some: 'content Missing Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Missing Mission', + en: 'learningObjectives EN Missing Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Missing Mission', + en: 'validatedObjectives EN Missing Mission', + }, + introductionMediaType: 'introductionMediaType Missing Mission', + introductionMediaUrl: 'introductionMediaUrl Missing Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Missing Mission', + en: 'introductionMediaAlt EN Missing Mission', + }, + documentationUrl: 'documentationUrl Missing Mission', + cardImageUrl: 'cardImageUrl Missing Mission', + competenceId: 'competenceId Missing Mission', + }, + { + id: 4, + status: 'status New Mission', + name_i18n: { + fr: 'name FR New Mission', + en: 'name EN New Mission', + }, + content: { some: 'content New Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR New Mission', + en: 'learningObjectives EN New Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR New Mission', + en: 'validatedObjectives EN New Mission', + }, + introductionMediaType: 'introductionMediaType New Mission', + introductionMediaUrl: 'introductionMediaUrl New Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR New Mission', + en: 'introductionMediaAlt EN New Mission', + }, + documentationUrl: 'documentationUrl New Mission', + cardImageUrl: 'cardImageUrl New Mission', + competenceId: 'competenceId New Mission', + }, + ]); + expect(lcmsApiCall.isDone()).to.be.true; + }); + }); + + context('failure', function () { + beforeEach(function () { + newLearningContent.missions[0].id = null; + lcmsApiCall = nock('https://lcms-test.pix.fr/api') + .post('/releases') + .matchHeader('Authorization', 'Bearer test-api-key') + .reply(201, { content: newLearningContent }); + }); + + it('should update nothing', async function () { + // when + await catchErr(lcmsCreateReleaseJobController.handle)(); + + // then + const updatedFrameworks = await knex.select('*').from('learningcontent.frameworks').orderBy('name'); + expect(updatedFrameworks).to.deep.equal([ + { id: 'aboutToBeRefreshedFrameworkId', name: 'name About to be refreshed Framework - old' }, + { id: 'missingFrameworkId', name: 'name Missing Framework' }, + { id: 'untouchedFrameworkId', name: 'name Untouched Framework' }, + ]); + const updatedAreas = await knex.select('*').from('learningcontent.areas').orderBy('id'); + expect(updatedAreas).to.deep.equal([ + { + id: 'aboutToBeRefreshedAreaId', + name: 'name About to be refreshed Domaine - old', + code: 'code About to be refreshed Domaine - old', + title_i18n: { + fr: 'title_i18n FR About to be refreshed Domaine - old', + nl: 'title_i18n NL About to be refreshed Domaine - old', + }, + color: 'color About to be refreshed Domaine - old', + frameworkId: 'frameworkId About to be refreshed Domaine - old', + competenceIds: [ + 'competenceId1 About to be refreshed Domaine - old', + 'competenceId2 About to be refreshed Domaine - old', + ], + }, + { + id: 'missingAreaId', + name: 'name Missing Domaine', + code: 'code Missing Domaine', + title_i18n: { fr: 'title_i18n FR Missing Domaine', nl: 'title_i18n NL Missing Domaine' }, + color: 'color Missing Domaine', + frameworkId: 'frameworkId Missing Domaine', + competenceIds: ['competenceId Missing Domaine'], + }, + { + id: 'untouchedAreaId', + name: 'name Untouched Domaine', + code: 'code Untouched Domaine', + title_i18n: { fr: 'title_i18n FR Untouched Domaine', nl: 'title_i18n NL Untouched Domaine' }, + color: 'color Untouched Domaine', + frameworkId: 'frameworkId Untouched Domaine', + competenceIds: ['competenceId Untouched Domaine'], + }, + ]); + const updatedCompetences = await knex.select('*').from('learningcontent.competences').orderBy('id'); + expect(updatedCompetences).to.deep.equal([ + { + id: 'aboutToBeRefreshedCompetenceId', + index: 'index About to be refreshed Competence - old', + areaId: 'areaId About to be refreshed Competence - old', + skillIds: ['skillId About to be refreshed Competence - old'], + thematicIds: ['thematicId About to be refreshed Competence - old'], + origin: 'origin About to be refreshed Competence - old', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Competence - old', + nl: 'name_i18n NL About to be refreshed Competence - old', + }, + description_i18n: { + fr: 'description_i18n FR About to be refreshed Competence - old', + en: 'description_i18n EN About to be refreshed Competence - old', + }, + }, + { + id: 'missingCompetenceId', + index: 'index Missing Competence', + areaId: 'areaId Missing Competence', + skillIds: ['skillId Missing Competence'], + thematicIds: ['thematicId Missing Competence'], + origin: 'origin Missing Competence', + name_i18n: { + fr: 'name_i18n FR Missing Competence', + en: 'name_i18n EN Missing Competence', + }, + description_i18n: { + fr: 'description_i18n FR Missing Competence', + nl: 'description_i18n NL Missing Competence', + }, + }, + { + id: 'untouchedCompetenceId', + index: 'index Untouched Competence', + areaId: 'areaId Untouched Competence', + skillIds: ['skillId Untouched Competence'], + thematicIds: ['thematicId Untouched Competence'], + origin: 'origin Untouched Competence', + name_i18n: { + fr: 'name_i18n FR Untouched Competence', + en: 'name_i18n EN Untouched Competence', + }, + description_i18n: { + fr: 'description_i18n FR Untouched Competence', + nl: 'description_i18n NL Untouched Competence', + }, + }, + ]); + const updatedThematics = await knex.select('*').from('learningcontent.thematics').orderBy('id'); + expect(updatedThematics).to.deep.equal([ + { + id: 'aboutToBeRefreshedThematicId', + name_i18n: { + fr: 'name_i18n FR About to be refreshed Thematique - old', + en: 'name_i18n EN About to be refreshed Thematique - old', + }, + index: 1, + competenceId: 'competenceId About to be refreshed Thematique - old', + tubeIds: ['tubeId About to be refreshed Thematique - old'], + }, + { + id: 'missingThematicId', + name_i18n: { + fr: 'name_i18n FR Missing Thematique', + en: 'name_i18n EN Missing Thematique', + }, + index: 4, + competenceId: 'competenceId Missing Thematique', + tubeIds: ['tubeId Missing Thematique'], + }, + { + id: 'untouchedThematicId', + name_i18n: { + fr: 'name_i18n FR Untouched Thematique', + en: 'name_i18n EN Untouched Thematique', + }, + index: 2, + competenceId: 'competenceId Untouched Thematique', + tubeIds: ['tubeId Untouched Thematique'], + }, + ]); + const updatedTubes = await knex.select('*').from('learningcontent.tubes').orderBy('id'); + expect(updatedTubes).to.deep.equal([ + { + id: 'aboutToBeRefreshedTubeId', + name: 'name About to be refreshed Tube - old', + title: 'title About to be refreshed Tube - old', + description: 'description About to be refreshed Tube - old', + practicalTitle_i18n: { + fr: 'practicalTitle FR About to be refreshed Tube - old', + en: 'practicalTitle EN About to be refreshed Tube - old', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR About to be refreshed Tube - old', + en: 'practicalDescription EN About to be refreshed Tube - old', + }, + competenceId: 'competenceId About to be refreshed Tube - old', + thematicId: 'thematicId About to be refreshed Tube - old', + skillIds: ['skillId About to be refreshed Tube - old'], + isMobileCompliant: false, + isTabletCompliant: true, + }, + { + id: 'missingTubeId', + name: 'name Missing Tube', + title: 'title Missing Tube', + description: 'description Missing Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Missing Tube', + en: 'practicalTitle EN Missing Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Missing Tube', + en: 'practicalDescription EN Missing Tube', + }, + competenceId: 'competenceId Missing Tube', + thematicId: 'thematicId Missing Tube', + skillIds: ['skillId Missing Tube'], + isMobileCompliant: true, + isTabletCompliant: true, + }, + { + id: 'untouchedTubeId', + name: 'name Untouched Tube', + title: 'title Untouched Tube', + description: 'description Untouched Tube', + practicalTitle_i18n: { + fr: 'practicalTitle FR Untouched Tube', + en: 'practicalTitle EN Untouched Tube', + }, + practicalDescription_i18n: { + fr: 'practicalDescription FR Untouched Tube', + en: 'practicalDescription EN Untouched Tube', + }, + competenceId: 'competenceId Untouched Tube', + thematicId: 'thematicId Untouched Tube', + skillIds: ['skillId Untouched Tube'], + isMobileCompliant: false, + isTabletCompliant: true, + }, + ]); + const updatedSkills = await knex.select('*').from('learningcontent.skills').orderBy('id'); + expect(updatedSkills).to.deep.equal([ + { + id: 'aboutToBeRefreshedSkillId', + name: 'name about to be refreshed skill - old', + hintStatus: 'hintStatus about to be refreshed skill - old', + tutorialIds: [ + 'tutorialId1 about to be refreshed skill - old', + 'tutorialId2 about to be refreshed skill - old', + ], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 about to be refreshed skill - old', + 'learningMoreTutorialId2 about to be refreshed skill - old', + ], + pixValue: 1, + competenceId: 'competenceId about to be refreshed skill - old', + status: 'status about to be refreshed skill - old', + tubeId: 'tubeId about to be refreshed skill - old', + version: 1, + level: 1, + hint_i18n: { + fr: 'hint_i18n.fr about to be refreshed skill - old', + en: 'hint_i18n.en about to be refreshed skill - old', + nl: 'hint_i18n.nl about to be refreshed skill - old', + }, + }, + { + id: 'missingSkillId', + name: 'name missing skill', + hintStatus: 'hintStatus missing skill', + tutorialIds: ['tutorialId1 missing skill', 'tutorialId2 missing skill'], + learningMoreTutorialIds: ['learningMoreTutorialId1 missing skill', 'learningMoreTutorialId2 missing skill'], + pixValue: 5, + competenceId: 'competenceId missing skill', + status: 'status missing skill', + tubeId: 'tubeId missing skill', + version: 5, + level: 5, + hint_i18n: { + fr: 'hint_i18n.fr missing skill', + en: 'hint_i18n.en missing skill', + nl: 'hint_i18n.nl missing skill', + }, + }, + { + id: 'untouchedSkillId', + name: 'name untouched skill', + hintStatus: 'hintStatus untouched skill', + tutorialIds: ['tutorialId1 untouched skill', 'tutorialId2 untouched skill'], + learningMoreTutorialIds: [ + 'learningMoreTutorialId1 untouched skill', + 'learningMoreTutorialId2 untouched skill', + ], + pixValue: 3, + competenceId: 'competenceId untouched skill', + status: 'status untouched skill', + tubeId: 'tubeId untouched skill', + version: 3, + level: 3, + hint_i18n: { + fr: 'hint_i18n.fr untouched skill', + en: 'hint_i18n.en untouched skill', + nl: 'hint_i18n.nl untouched skill', + }, + }, + ]); + const updatedChallenges = await knex.select('*').from('learningcontent.challenges').orderBy('id'); + expect(updatedChallenges).to.deep.equal([ + { + id: 'aboutToBeRefreshedChallengeId', + instruction: 'instruction About to be refreshed Epreuve - old', + alternativeInstruction: 'alternativeInstruction About to be refreshed Epreuve - old', + proposals: 'proposals About to be refreshed Epreuve - old', + type: 'type About to be refreshed Epreuve - old', + solution: 'solution About to be refreshed Epreuve - old', + solutionToDisplay: 'solutionToDisplay About to be refreshed Epreuve - old', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status About to be refreshed Epreuve - old', + genealogy: 'genealogy About to be refreshed Epreuve - old', + accessibility1: 'accessibility1 About to be refreshed Epreuve - old', + accessibility2: 'accessibility2 About to be refreshed Epreuve - old', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing About to be refreshed Epreuve - old', + isAwarenessChallenge: false, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt About to be refreshed Epreuve - old', + illustrationUrl: 'illustrationUrl About to be refreshed Epreuve - old', + attachments: ['attachment1 About to be refreshed Epreuve - old'], + responsive: 'responsive About to be refreshed Epreuve - old', + alpha: 10.5, + delta: 3.3, + autoReply: true, + focusable: false, + format: 'format About to be refreshed Epreuve - old', + timer: 180, + embedHeight: 2800, + embedUrl: 'embedUrl About to be refreshed Epreuve - old', + embedTitle: 'embedTitle About to be refreshed Epreuve - old', + locales: ['fr', 'nl'], + competenceId: 'competenceId About to be refreshed Epreuve - old', + skillId: 'skillId About to be refreshed Epreuve - old', + }, + { + id: 'missingChallengeId', + instruction: 'instruction Missing Epreuve', + alternativeInstruction: 'alternativeInstruction Missing Epreuve', + proposals: 'proposals Missing Epreuve', + type: 'type Missing Epreuve', + solution: 'solution Missing Epreuve', + solutionToDisplay: 'solutionToDisplay Missing Epreuve', + t1Status: false, + t2Status: true, + t3Status: true, + status: 'status Missing Epreuve', + genealogy: 'genealogy Missing Epreuve', + accessibility1: 'accessibility1 Missing Epreuve', + accessibility2: 'accessibility2 Missing Epreuve', + requireGafamWebsiteAccess: false, + isIncompatibleIpadCertif: true, + deafAndHardOfHearing: 'deafAndHardOfHearing Missing Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 99, + shuffled: true, + illustrationAlt: 'illustrationAlt Missing Epreuve', + illustrationUrl: 'illustrationUrl Missing Epreuve', + attachments: ['attachment8 Missing Epreuve'], + responsive: 'responsive Missing Epreuve', + alpha: 58.2, + delta: 20.4, + autoReply: true, + focusable: true, + format: 'format Missing Epreuve', + timer: null, + embedHeight: 55, + embedUrl: 'embedUrl Missing Epreuve', + embedTitle: 'embedTitle Missing Epreuve', + locales: ['en', 'nl'], + competenceId: 'competenceId Missing Epreuve', + skillId: 'skillId Missing Epreuve', + }, + { + id: 'untouchedChallengeId', + instruction: 'instruction Untouched Epreuve', + alternativeInstruction: 'alternativeInstruction Untouched Epreuve', + proposals: 'proposals Untouched Epreuve', + type: 'type Untouched Epreuve', + solution: 'solution Untouched Epreuve', + solutionToDisplay: 'solutionToDisplay Untouched Epreuve', + t1Status: false, + t2Status: true, + t3Status: false, + status: 'status Untouched Epreuve', + genealogy: 'genealogy Untouched Epreuve', + accessibility1: 'accessibility1 Untouched Epreuve', + accessibility2: 'accessibility2 Untouched Epreuve', + requireGafamWebsiteAccess: true, + isIncompatibleIpadCertif: false, + deafAndHardOfHearing: 'deafAndHardOfHearing Untouched Epreuve', + isAwarenessChallenge: true, + toRephrase: true, + alternativeVersion: 5, + shuffled: false, + illustrationAlt: 'illustrationAlt Untouched Epreuve', + illustrationUrl: 'illustrationUrl Untouched Epreuve', + attachments: [], + responsive: 'responsive Untouched Epreuve', + alpha: 77, + delta: 66, + autoReply: true, + focusable: false, + format: 'format Untouched Epreuve', + timer: null, + embedHeight: 1800, + embedUrl: 'embedUrl Untouched Epreuve', + embedTitle: 'embedTitle Untouched Epreuve', + locales: ['fr', 'en'], + competenceId: 'competenceId Untouched Epreuve', + skillId: 'skillId Untouched Epreuve', + }, + ]); + const updatedCourses = await knex.select('*').from('learningcontent.courses').orderBy('id'); + expect(updatedCourses).to.deep.equal([ + { + id: 'aboutToBeRefreshedCourseId', + name: 'name About to be refreshed Course - old', + description: 'description About to be refreshed Course - old', + isActive: false, + competences: [ + 'competenceId1 About to be refreshed Course - old', + 'competenceId2 About to be refreshed Course - old', + ], + challenges: [ + 'challengeId1 About to be refreshed Course - old', + 'challengeId2 About to be refreshed Course - old', + ], + }, + { + id: 'missingCourseId', + name: 'name Missing', + description: 'description Missing', + isActive: true, + competences: ['competenceId Missing'], + challenges: ['challengeId Missing'], + }, + { + id: 'untouchedCourseId', + name: 'name Untouched', + description: 'description Untouched', + isActive: false, + competences: ['competenceId Untouched'], + challenges: ['challengeId Untouched'], + }, + ]); + const updatedTutorials = await knex.select('*').from('learningcontent.tutorials').orderBy('id'); + expect(updatedTutorials).to.deep.equal([ + { + id: 'aboutToBeRefreshedTutorialId', + duration: 'duration About to be refreshed Tutoriel - old', + format: 'format About to be refreshed Tutoriel - old', + title: 'title About to be refreshed Tutoriel - old', + source: 'source About to be refreshed Tutoriel - old', + link: 'link About to be refreshed Tutoriel - old', + locale: 'en', + }, + { + id: 'missingTutorialId', + duration: 'duration Missing Tutorial', + format: 'format Missing Tutorial', + title: 'title Missing Tutorial', + source: 'source Missing Tutorial', + link: 'link Missing Tutorial', + locale: 'fr', + }, + { + id: 'untouchedTutorialId', + duration: 'duration Untouched Tutorial', + format: 'format Untouched Tutorial', + title: 'title Untouched Tutorial', + source: 'source Untouched Tutorial', + link: 'link Untouched Tutorial', + locale: 'nl', + }, + ]); + const updatedMissions = await knex.select('*').from('learningcontent.missions').orderBy('id'); + expect(updatedMissions).to.deep.equal([ + { + id: 1, + status: 'status About to be refreshed Mission - old', + name_i18n: { + fr: 'name FR About to be refreshed Mission - old', + en: 'name EN About to be refreshed Mission - old', + }, + content: { some: 'content About to be refreshed Mission - old' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR About to be refreshed Mission - old', + en: 'learningObjectives EN About to be refreshed Mission - old', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR About to be refreshed Mission - old', + en: 'validatedObjectives EN About to be refreshed Mission - old', + }, + introductionMediaType: 'introductionMediaType About to be refreshed Mission - old', + introductionMediaUrl: 'introductionMediaUrl About to be refreshed Mission - old', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR About to be refreshed Mission - old', + en: 'introductionMediaAlt EN About to be refreshed Mission - old', + }, + documentationUrl: 'documentationUrl About to be refreshed Mission - old', + cardImageUrl: 'cardImageUrl About to be refreshed Mission - old', + competenceId: 'competenceId About to be refreshed Mission - old', + }, + { + id: 2, + status: 'status Untouched Mission', + name_i18n: { + fr: 'name FR Untouched Mission', + en: 'name EN Untouched Mission', + }, + content: { some: 'content Untouched Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Untouched Mission', + en: 'learningObjectives EN Untouched Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Untouched Mission', + en: 'validatedObjectives EN Untouched Mission', + }, + introductionMediaType: 'introductionMediaType Untouched Mission', + introductionMediaUrl: 'introductionMediaUrl Untouched Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Untouched Mission', + en: 'introductionMediaAlt EN Untouched Mission', + }, + documentationUrl: 'documentationUrl Untouched Mission', + cardImageUrl: 'cardImageUrl Untouched Mission', + competenceId: 'competenceId Untouched Mission', + }, + { + id: 3, + status: 'status Missing Mission', + name_i18n: { + fr: 'name FR Missing Mission', + en: 'name EN Missing Mission', + }, + content: { some: 'content Missing Mission' }, + learningObjectives_i18n: { + fr: 'learningObjectives FR Missing Mission', + en: 'learningObjectives EN Missing Mission', + }, + validatedObjectives_i18n: { + fr: 'validatedObjectives FR Missing Mission', + en: 'validatedObjectives EN Missing Mission', + }, + introductionMediaType: 'introductionMediaType Missing Mission', + introductionMediaUrl: 'introductionMediaUrl Missing Mission', + introductionMediaAlt_i18n: { + fr: 'introductionMediaAlt FR Missing Mission', + en: 'introductionMediaAlt EN Missing Mission', + }, + documentationUrl: 'documentationUrl Missing Mission', + cardImageUrl: 'cardImageUrl Missing Mission', + competenceId: 'competenceId Missing Mission', + }, + ]); + expect(lcmsApiCall.isDone()).to.be.true; + }); + }); + }); +}); diff --git a/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js b/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js deleted file mode 100644 index 03cb73b2bf7..00000000000 --- a/api/tests/learning-content/unit/application/jobs/lcms-create-release-job-controller_test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { LcmsRefreshCacheJobController } from '../../../../../src/learning-content/application/jobs/lcms-refresh-cache-job-controller.js'; -import { usecases } from '../../../../../src/learning-content/domain/usecases/index.js'; -import { expect, sinon } from '../../../../test-helper.js'; - -describe('Learning Content | Unit | Application | Jobs | LcmsRefreshCacheJobController', function () { - describe('#handle', function () { - it('should call usecase', async function () { - // given - sinon.stub(usecases, 'refreshLearningContentCache'); - const handler = new LcmsRefreshCacheJobController(); - - // when - await handler.handle(); - - // then - expect(usecases.refreshLearningContentCache).to.have.been.calledOnce; - }); - }); -}); diff --git a/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js b/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js deleted file mode 100644 index c66f331d8d7..00000000000 --- a/api/tests/learning-content/unit/application/jobs/lcms-refresh-cache-job-controller_test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { LcmsCreateReleaseJobController } from '../../../../../src/learning-content/application/jobs/lcms-create-release-job-controller.js'; -import { usecases } from '../../../../../src/learning-content/domain/usecases/index.js'; -import { expect, sinon } from '../../../../test-helper.js'; - -describe('Learning Content | Unit | Application | Jobs | LcmsCreateReleaseJobController', function () { - describe('#handle', function () { - it('should call usecase', async function () { - // given - sinon.stub(usecases, 'createLearningContentRelease'); - const handler = new LcmsCreateReleaseJobController(); - - // when - await handler.handle(); - - // then - expect(usecases.createLearningContentRelease).to.have.been.calledOnce; - }); - }); -}); diff --git a/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js b/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js index ebf83a02af4..c2d889fd2ad 100644 --- a/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js +++ b/api/tests/learning-content/unit/domain/usecases/create-learning-content-release_test.js @@ -1,19 +1,103 @@ import { createLearningContentRelease } from '../../../../../src/learning-content/domain/usecases/create-learning-content-release.js'; +import { DomainTransaction } from '../../../../../src/shared/domain/DomainTransaction.js'; import { expect, sinon } from '../../../../test-helper.js'; -describe('Unit | UseCase | create-learning-content-release', function () { - it('should call update on the learning content cache', async function () { - // given - const LearningContentCache = { - instance: { - update: sinon.stub(), - }, - }; +describe('Learning Content | Unit | UseCase | create-learning-content-release', function () { + beforeEach(function () { + sinon.stub(DomainTransaction, 'execute').callsFake((callback) => { + return callback(); + }); + }); + + describe('#createLearningContentRelease', function () { + it('should trigger an update of the learning content cache', async function () { + // given + const frameworks = Symbol('frameworks'); + const areas = Symbol('areas'); + const competences = Symbol('competences'); + const thematics = Symbol('thematics'); + const tubes = Symbol('tubes'); + const skills = Symbol('skills'); + const challenges = Symbol('challenges'); + const courses = Symbol('courses'); + const tutorials = Symbol('tutorials'); + const missions = Symbol('missions'); + + const LearningContentCache = { + instance: { + update: sinon.stub().resolves({ + frameworks, + areas, + competences, + thematics, + tubes, + skills, + challenges, + courses, + tutorials, + missions, + }), + }, + }; + + const frameworkRepository = { + save: sinon.stub(), + }; + const areaRepository = { + save: sinon.stub(), + }; + const competenceRepository = { + save: sinon.stub(), + }; + const thematicRepository = { + save: sinon.stub(), + }; + const tubeRepository = { + save: sinon.stub(), + }; + const skillRepository = { + save: sinon.stub(), + }; + const challengeRepository = { + save: sinon.stub(), + }; + const courseRepository = { + save: sinon.stub(), + }; + const tutorialRepository = { + save: sinon.stub(), + }; + const missionRepository = { + save: sinon.stub(), + }; - // when - await createLearningContentRelease({ LearningContentCache }); + // when + await createLearningContentRelease({ + LearningContentCache, + frameworkRepository, + areaRepository, + competenceRepository, + thematicRepository, + tubeRepository, + skillRepository, + challengeRepository, + courseRepository, + tutorialRepository, + missionRepository, + }); - // then - expect(LearningContentCache.instance.update).to.have.been.called; + // then + expect(LearningContentCache.instance.update).to.have.been.calledOnce; + expect(frameworkRepository.save).to.have.been.calledOnceWithExactly(frameworks); + expect(areaRepository.save).to.have.been.calledOnceWithExactly(areas); + expect(competenceRepository.save).to.have.been.calledOnceWithExactly(competences); + expect(thematicRepository.save).to.have.been.calledOnceWithExactly(thematics); + expect(tubeRepository.save).to.have.been.calledOnceWithExactly(tubes); + expect(skillRepository.save).to.have.been.calledOnceWithExactly(skills); + expect(challengeRepository.save).to.have.been.calledOnceWithExactly(challenges); + expect(courseRepository.save).to.have.been.calledOnceWithExactly(courses); + expect(tutorialRepository.save).to.have.been.calledOnceWithExactly(tutorials); + expect(missionRepository.save).to.have.been.calledOnceWithExactly(missions); + }); }); }); diff --git a/api/tests/learning-content/unit/domain/usecases/refresh-learning-content-cache_test.js b/api/tests/learning-content/unit/domain/usecases/refresh-learning-content-cache_test.js index 15c075de7f5..b266a371c04 100644 --- a/api/tests/learning-content/unit/domain/usecases/refresh-learning-content-cache_test.js +++ b/api/tests/learning-content/unit/domain/usecases/refresh-learning-content-cache_test.js @@ -2,7 +2,7 @@ import { DomainTransaction } from '../../../../../lib/infrastructure/DomainTrans import { refreshLearningContentCache } from '../../../../../src/learning-content/domain/usecases/refresh-learning-content-cache.js'; import { expect, sinon } from '../../../../test-helper.js'; -describe('Unit | Domain | Usecase | Refresh learning content cache', function () { +describe('Learning Content | Unit | Domain | Usecase | Refresh learning content cache', function () { beforeEach(function () { sinon.stub(DomainTransaction, 'execute').callsFake((callback) => { return callback(); From db233669489c6a4f51ec39850f8f396d3688d88d Mon Sep 17 00:00:00 2001 From: Laura Bergoens Date: Thu, 28 Nov 2024 10:47:44 +0100 Subject: [PATCH 5/5] add integration tests for scheduling job --- ...hedule-create-learning-content-release-job_test.js | 11 +++++++++++ ...chedule-refresh-learning-content-cache-job_test.js | 11 +++++++++++ ...edule-create-learning-content-release-job_test.js} | 0 3 files changed, 22 insertions(+) create mode 100644 api/tests/learning-content/integration/domain/schedule-create-learning-content-release-job_test.js create mode 100644 api/tests/learning-content/integration/domain/schedule-refresh-learning-content-cache-job_test.js rename api/tests/learning-content/unit/domain/usecases/{schedule-create-learning-content-job_test.js => schedule-create-learning-content-release-job_test.js} (100%) diff --git a/api/tests/learning-content/integration/domain/schedule-create-learning-content-release-job_test.js b/api/tests/learning-content/integration/domain/schedule-create-learning-content-release-job_test.js new file mode 100644 index 00000000000..dee6682c2c5 --- /dev/null +++ b/api/tests/learning-content/integration/domain/schedule-create-learning-content-release-job_test.js @@ -0,0 +1,11 @@ +import { LcmsCreateReleaseJob } from '../../../../src/learning-content/domain/models/LcmsCreateReleaseJob.js'; +import { usecases } from '../../../../src/learning-content/domain/usecases/index.js'; +import { expect } from '../../../test-helper.js'; + +describe('Learning Content | Integration | Domain | Use case | scheduleCreateLearningContentReleaseJob', function () { + it('should schedule the job', async function () { + await usecases.scheduleCreateLearningContentReleaseJob({ userId: 123 }); + + await expect(LcmsCreateReleaseJob.name).to.have.been.performed.withJobPayload({ userId: 123 }); + }); +}); diff --git a/api/tests/learning-content/integration/domain/schedule-refresh-learning-content-cache-job_test.js b/api/tests/learning-content/integration/domain/schedule-refresh-learning-content-cache-job_test.js new file mode 100644 index 00000000000..db4795779b7 --- /dev/null +++ b/api/tests/learning-content/integration/domain/schedule-refresh-learning-content-cache-job_test.js @@ -0,0 +1,11 @@ +import { LcmsRefreshCacheJob } from '../../../../src/learning-content/domain/models/LcmsRefreshCacheJob.js'; +import { usecases } from '../../../../src/learning-content/domain/usecases/index.js'; +import { expect } from '../../../test-helper.js'; + +describe('Learning Content | Integration | Domain | Use case | scheduleRefreshLearningContentCacheJob', function () { + it('should schedule the job', async function () { + await usecases.scheduleRefreshLearningContentCacheJob({ userId: 123 }); + + await expect(LcmsRefreshCacheJob.name).to.have.been.performed.withJobPayload({ userId: 123 }); + }); +}); diff --git a/api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js b/api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-release-job_test.js similarity index 100% rename from api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-job_test.js rename to api/tests/learning-content/unit/domain/usecases/schedule-create-learning-content-release-job_test.js