fix(backend): Testen DatabaseLearningPathProvider en LearningPathService gerepareerd na refactoring.
This commit is contained in:
		
							parent
							
								
									1815371a7b
								
							
						
					
					
						commit
						c624e36680
					
				
					 6 changed files with 163 additions and 204 deletions
				
			
		|  | @ -2,167 +2,84 @@ import { beforeAll, describe, expect, it } from 'vitest'; | ||||||
| import { LearningObject } from '../../../src/entities/content/learning-object.entity.js'; | import { LearningObject } from '../../../src/entities/content/learning-object.entity.js'; | ||||||
| import { setupTestApp } from '../../setup-tests.js'; | import { setupTestApp } from '../../setup-tests.js'; | ||||||
| import { LearningPath } from '../../../src/entities/content/learning-path.entity.js'; | import { LearningPath } from '../../../src/entities/content/learning-path.entity.js'; | ||||||
| import { | import { getSubmissionRepository } from '../../../src/data/repositories.js'; | ||||||
|     getAssignmentRepository, | 
 | ||||||
|     getClassRepository, |  | ||||||
|     getGroupRepository, |  | ||||||
|     getLearningObjectRepository, |  | ||||||
|     getLearningPathRepository, |  | ||||||
|     getStudentRepository, |  | ||||||
|     getSubmissionRepository, |  | ||||||
| } from '../../../src/data/repositories.js'; |  | ||||||
| import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.js'; |  | ||||||
| import learningPathExample from '../../test-assets/learning-paths/pn-werking-example.js'; |  | ||||||
| import databaseLearningPathProvider from '../../../src/services/learning-paths/database-learning-path-provider.js'; | import databaseLearningPathProvider from '../../../src/services/learning-paths/database-learning-path-provider.js'; | ||||||
| import { expectToBeCorrectLearningPath } from '../../test-utils/expectations.js'; | import { expectToBeCorrectLearningPath } from '../../test-utils/expectations.js'; | ||||||
| import learningObjectService from '../../../src/services/learning-objects/learning-object-service.js'; | import learningObjectService from '../../../src/services/learning-objects/learning-object-service.js'; | ||||||
| import { Language } from '@dwengo-1/common/util/language'; | import { Language } from '@dwengo-1/common/util/language'; | ||||||
|  | 
 | ||||||
| import { | import { | ||||||
|     ConditionTestLearningPathAndLearningObjects, |     LearningObjectNode, | ||||||
|     createConditionTestLearningPathAndLearningObjects, |     LearningPathResponse | ||||||
| } from '../../test-assets/learning-paths/test-conditions-example.js'; | } from '@dwengo-1/common/interfaces/learning-content'; | ||||||
| import { Student } from '../../../src/entities/users/student.entity.js'; | import { | ||||||
| 
 |     testLearningObject01, testLearningObjectEssayQuestion, | ||||||
| import { LearningObjectNode, LearningPathResponse } from '@dwengo-1/common/interfaces/learning-content'; |     testLearningObjectMultipleChoice | ||||||
| 
 | } from "../../test_assets/content/learning-objects.testdata"; | ||||||
| const STUDENT_A_USERNAME = 'student_a'; | import {testLearningPathWithConditions} from "../../test_assets/content/learning-paths.testdata"; | ||||||
| const STUDENT_B_USERNAME = 'student_b'; | import {mapToLearningPath} from "../../../src/services/learning-paths/learning-path-service"; | ||||||
| const CLASS_NAME = 'test_class'; | import {getTestGroup01, getTestGroup02} from "../../test_assets/assignments/groups.testdata"; | ||||||
| 
 | import { Group } from '../../../src/entities/assignments/group.entity.js'; | ||||||
| async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { | import {RequiredEntityData} from "@mikro-orm/core"; | ||||||
|     const learningObjectRepo = getLearningObjectRepository(); |  | ||||||
|     const learningPathRepo = getLearningPathRepository(); |  | ||||||
|     const learningObject = learningObjectExample.createLearningObject(); |  | ||||||
|     const learningPath = learningPathExample.createLearningPath(); |  | ||||||
|     await learningObjectRepo.save(learningObject); |  | ||||||
|     await learningPathRepo.save(learningPath); |  | ||||||
|     return { learningObject, learningPath }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function initPersonalizationTestData(): Promise<{ |  | ||||||
|     learningContent: ConditionTestLearningPathAndLearningObjects; |  | ||||||
|     studentA: Student; |  | ||||||
|     studentB: Student; |  | ||||||
| }> { |  | ||||||
|     const studentRepo = getStudentRepository(); |  | ||||||
|     const classRepo = getClassRepository(); |  | ||||||
|     const assignmentRepo = getAssignmentRepository(); |  | ||||||
|     const groupRepo = getGroupRepository(); |  | ||||||
|     const submissionRepo = getSubmissionRepository(); |  | ||||||
|     const learningPathRepo = getLearningPathRepository(); |  | ||||||
|     const learningObjectRepo = getLearningObjectRepository(); |  | ||||||
|     const learningContent = createConditionTestLearningPathAndLearningObjects(); |  | ||||||
|     await learningObjectRepo.save(learningContent.branchingObject); |  | ||||||
|     await learningObjectRepo.save(learningContent.finalObject); |  | ||||||
|     await learningObjectRepo.save(learningContent.extraExerciseObject); |  | ||||||
|     await learningPathRepo.save(learningContent.learningPath); |  | ||||||
| 
 |  | ||||||
|     // Create students
 |  | ||||||
|     const studentA = studentRepo.create({ |  | ||||||
|         username: STUDENT_A_USERNAME, |  | ||||||
|         firstName: 'Aron', |  | ||||||
|         lastName: 'Student', |  | ||||||
|     }); |  | ||||||
|     await studentRepo.save(studentA); |  | ||||||
| 
 |  | ||||||
|     const studentB = studentRepo.create({ |  | ||||||
|         username: STUDENT_B_USERNAME, |  | ||||||
|         firstName: 'Bill', |  | ||||||
|         lastName: 'Student', |  | ||||||
|     }); |  | ||||||
|     await studentRepo.save(studentB); |  | ||||||
| 
 |  | ||||||
|     // Create class for students
 |  | ||||||
|     const testClass = classRepo.create({ |  | ||||||
|         classId: CLASS_NAME, |  | ||||||
|         displayName: 'Test class', |  | ||||||
|     }); |  | ||||||
|     await classRepo.save(testClass); |  | ||||||
| 
 |  | ||||||
|     // Create assignment for students and assign them to different groups
 |  | ||||||
|     const assignment = assignmentRepo.create({ |  | ||||||
|         id: 0, |  | ||||||
|         title: 'Test assignment', |  | ||||||
|         description: 'Test description', |  | ||||||
|         learningPathHruid: learningContent.learningPath.hruid, |  | ||||||
|         learningPathLanguage: learningContent.learningPath.language, |  | ||||||
|         within: testClass, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     const groupA = groupRepo.create({ |  | ||||||
|         groupNumber: 0, |  | ||||||
|         members: [studentA], |  | ||||||
|         assignment, |  | ||||||
|     }); |  | ||||||
|     await groupRepo.save(groupA); |  | ||||||
| 
 |  | ||||||
|     const groupB = groupRepo.create({ |  | ||||||
|         groupNumber: 1, |  | ||||||
|         members: [studentB], |  | ||||||
|         assignment, |  | ||||||
|     }); |  | ||||||
|     await groupRepo.save(groupB); |  | ||||||
| 
 |  | ||||||
|     // Let each of the students make a submission in his own group.
 |  | ||||||
|     const submissionA = submissionRepo.create({ |  | ||||||
|         learningObjectHruid: learningContent.branchingObject.hruid, |  | ||||||
|         learningObjectLanguage: learningContent.branchingObject.language, |  | ||||||
|         learningObjectVersion: learningContent.branchingObject.version, |  | ||||||
|         onBehalfOf: groupA, |  | ||||||
|         submitter: studentA, |  | ||||||
|         submissionTime: new Date(), |  | ||||||
|         content: '[0]', |  | ||||||
|     }); |  | ||||||
|     await submissionRepo.save(submissionA); |  | ||||||
| 
 |  | ||||||
|     const submissionB = submissionRepo.create({ |  | ||||||
|         learningObjectHruid: learningContent.branchingObject.hruid, |  | ||||||
|         learningObjectLanguage: learningContent.branchingObject.language, |  | ||||||
|         learningObjectVersion: learningContent.branchingObject.version, |  | ||||||
|         onBehalfOf: groupA, |  | ||||||
|         submitter: studentB, |  | ||||||
|         submissionTime: new Date(), |  | ||||||
|         content: '[1]', |  | ||||||
|     }); |  | ||||||
|     await submissionRepo.save(submissionB); |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         learningContent: learningContent, |  | ||||||
|         studentA: studentA, |  | ||||||
|         studentB: studentB, |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function expectBranchingObjectNode( | function expectBranchingObjectNode( | ||||||
|     result: LearningPathResponse, |     result: LearningPathResponse | ||||||
|     persTestData: { |  | ||||||
|         learningContent: ConditionTestLearningPathAndLearningObjects; |  | ||||||
|         studentA: Student; |  | ||||||
|         studentB: Student; |  | ||||||
|     } |  | ||||||
| ): LearningObjectNode { | ): LearningObjectNode { | ||||||
|     const branchingObjectMatches = result.data![0].nodes.filter( |     const branchingObjectMatches = result.data![0].nodes.filter( | ||||||
|         (it) => it.learningobject_hruid === persTestData.learningContent.branchingObject.hruid |         (it) => it.learningobject_hruid === testLearningObjectMultipleChoice.hruid | ||||||
|     ); |     ); | ||||||
|     expect(branchingObjectMatches.length).toBe(1); |     expect(branchingObjectMatches.length).toBe(1); | ||||||
|     return branchingObjectMatches[0]; |     return branchingObjectMatches[0]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| describe('DatabaseLearningPathProvider', () => { | describe('DatabaseLearningPathProvider', () => { | ||||||
|     let example: { learningObject: LearningObject; learningPath: LearningPath }; |     let testLearningPath: LearningPath; | ||||||
|     let persTestData: { learningContent: ConditionTestLearningPathAndLearningObjects; studentA: Student; studentB: Student }; |     let branchingLearningObject: RequiredEntityData<LearningObject>; | ||||||
|  |     let extraExerciseLearningObject: RequiredEntityData<LearningObject>; | ||||||
|  |     let finalLearningObject:  RequiredEntityData<LearningObject>; | ||||||
|  |     let groupA: Group; | ||||||
|  |     let groupB: Group; | ||||||
| 
 | 
 | ||||||
|     beforeAll(async () => { |     beforeAll(async () => { | ||||||
|         await setupTestApp(); |         await setupTestApp(); | ||||||
|         example = await initExampleData(); |         testLearningPath = mapToLearningPath(testLearningPathWithConditions, []); | ||||||
|         persTestData = await initPersonalizationTestData(); |         branchingLearningObject = testLearningObjectMultipleChoice; | ||||||
|  |         extraExerciseLearningObject = testLearningObject01; | ||||||
|  |         finalLearningObject = testLearningObjectEssayQuestion; | ||||||
|  |         groupA = getTestGroup01(); | ||||||
|  |         groupB = getTestGroup02(); | ||||||
|  | 
 | ||||||
|  |         // Place different submissions for group A and B.
 | ||||||
|  |         const submissionRepo = getSubmissionRepository(); | ||||||
|  |         const submissionA = submissionRepo.create({ | ||||||
|  |             learningObjectHruid: branchingLearningObject.hruid, | ||||||
|  |             learningObjectLanguage: branchingLearningObject.language, | ||||||
|  |             learningObjectVersion: branchingLearningObject.version, | ||||||
|  |             content: "[0]", | ||||||
|  |             onBehalfOf: groupA, | ||||||
|  |             submissionTime: new Date(), | ||||||
|  |             submitter: groupA.members[0] | ||||||
|  |         }); | ||||||
|  |         await submissionRepo.save(submissionA); | ||||||
|  | 
 | ||||||
|  |         const submissionB = submissionRepo.create({ | ||||||
|  |             learningObjectHruid: branchingLearningObject.hruid, | ||||||
|  |             learningObjectLanguage: branchingLearningObject.language, | ||||||
|  |             learningObjectVersion: branchingLearningObject.version, | ||||||
|  |             content: "[1]", | ||||||
|  |             onBehalfOf: groupB, | ||||||
|  |             submissionTime: new Date(), | ||||||
|  |             submitter: groupB.members[0] | ||||||
|  |         }); | ||||||
|  |         await submissionRepo.save(submissionB); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('fetchLearningPaths', () => { |     describe('fetchLearningPaths', () => { | ||||||
|         it('returns the learning path correctly', async () => { |         it('returns the learning path correctly', async () => { | ||||||
|             const result = await databaseLearningPathProvider.fetchLearningPaths( |             const result = await databaseLearningPathProvider.fetchLearningPaths( | ||||||
|                 [example.learningPath.hruid], |                 [testLearningPath.hruid], | ||||||
|                 example.learningPath.language, |                 testLearningPath.language as Language, | ||||||
|                 'the source' |                 'the source' | ||||||
|             ); |             ); | ||||||
|             expect(result.success).toBe(true); |             expect(result.success).toBe(true); | ||||||
|  | @ -170,7 +87,7 @@ describe('DatabaseLearningPathProvider', () => { | ||||||
| 
 | 
 | ||||||
|             const learningObjectsOnPath = ( |             const learningObjectsOnPath = ( | ||||||
|                 await Promise.all( |                 await Promise.all( | ||||||
|                     example.learningPath.nodes.map(async (node) => |                     testLearningPath.nodes.map(async (node) => | ||||||
|                         learningObjectService.getLearningObjectById({ |                         learningObjectService.getLearningObjectById({ | ||||||
|                             hruid: node.learningObjectHruid, |                             hruid: node.learningObjectHruid, | ||||||
|                             version: node.version, |                             version: node.version, | ||||||
|  | @ -180,49 +97,65 @@ describe('DatabaseLearningPathProvider', () => { | ||||||
|                 ) |                 ) | ||||||
|             ).filter((it) => it !== null); |             ).filter((it) => it !== null); | ||||||
| 
 | 
 | ||||||
|             expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath); |             expectToBeCorrectLearningPath( | ||||||
|  |                 result.data![0], | ||||||
|  |                 mapToLearningPath(testLearningPathWithConditions, []), | ||||||
|  |                 learningObjectsOnPath | ||||||
|  |             ); | ||||||
|         }); |         }); | ||||||
|         it('returns the correct personalized learning path', async () => { |         it('returns the correct personalized learning path', async () => { | ||||||
|             // For student A:
 |             // For student A:
 | ||||||
|             let result = await databaseLearningPathProvider.fetchLearningPaths( |             let result = await databaseLearningPathProvider.fetchLearningPaths( | ||||||
|                 [persTestData.learningContent.learningPath.hruid], |                 [testLearningPath.hruid], | ||||||
|                 persTestData.learningContent.learningPath.language, |                 testLearningPath.language, | ||||||
|                 'the source', |                 'the source', | ||||||
|                 { type: 'student', student: persTestData.studentA } |                 groupA | ||||||
|             ); |             ); | ||||||
|             expect(result.success).toBeTruthy(); |             expect(result.success).toBeTruthy(); | ||||||
|             expect(result.data?.length).toBe(1); |             expect(result.data?.length).toBe(1); | ||||||
| 
 | 
 | ||||||
|             // There should be exactly one branching object
 |             // There should be exactly one branching object
 | ||||||
|             let branchingObject = expectBranchingObjectNode(result, persTestData); |             let branchingObject = expectBranchingObjectNode(result); | ||||||
| 
 | 
 | ||||||
|             expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.finalObject.hruid).length).toBe(0); // StudentA picked the first option, therefore, there should be no direct path to the final object.
 |             expect( | ||||||
|             expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe( |                 branchingObject.transitions.filter( | ||||||
|                 1 |                     it => it.next.hruid === finalLearningObject.hruid | ||||||
|             ); // There should however be a path to the extra exercise object.
 |                 ).length | ||||||
|  |             ).toBe(0); // StudentA picked the first option, therefore, there should be no direct path to the final object.
 | ||||||
|  |             expect( | ||||||
|  |                 branchingObject.transitions.filter( | ||||||
|  |                     it => it.next.hruid === extraExerciseLearningObject.hruid | ||||||
|  |                 ).length | ||||||
|  |             ).toBe(1); // There should however be a path to the extra exercise object.
 | ||||||
| 
 | 
 | ||||||
|             // For student B:
 |             // For student B:
 | ||||||
|             result = await databaseLearningPathProvider.fetchLearningPaths( |             result = await databaseLearningPathProvider.fetchLearningPaths( | ||||||
|                 [persTestData.learningContent.learningPath.hruid], |                 [testLearningPath.hruid], | ||||||
|                 persTestData.learningContent.learningPath.language, |                 testLearningPath.language, | ||||||
|                 'the source', |                 'the source', | ||||||
|                 { type: 'student', student: persTestData.studentB } |                 groupB | ||||||
|             ); |             ); | ||||||
|             expect(result.success).toBeTruthy(); |             expect(result.success).toBeTruthy(); | ||||||
|             expect(result.data?.length).toBe(1); |             expect(result.data?.length).toBe(1); | ||||||
| 
 | 
 | ||||||
|             // There should still be exactly one branching object
 |             // There should still be exactly one branching object
 | ||||||
|             branchingObject = expectBranchingObjectNode(result, persTestData); |             branchingObject = expectBranchingObjectNode(result); | ||||||
| 
 | 
 | ||||||
|             // However, now the student picks the other option.
 |             // However, now the student picks the other option.
 | ||||||
|             expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.finalObject.hruid).length).toBe(1); // StudentB picked the second option, therefore, there should be a direct path to the final object.
 |             expect( | ||||||
|             expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe( |                 branchingObject.transitions.filter( | ||||||
|                 0 |                     (it) => it.next.hruid === finalLearningObject.hruid | ||||||
|             ); // There should not be a path anymore to the extra exercise object.
 |                 ).length | ||||||
|  |             ).toBe(1); // StudentB picked the second option, therefore, there should be a direct path to the final object.
 | ||||||
|  |             expect( | ||||||
|  |                 branchingObject.transitions.filter( | ||||||
|  |                     (it) => it.next.hruid === extraExerciseLearningObject.hruid | ||||||
|  |                 ).length | ||||||
|  |             ).toBe(0); // There should not be a path anymore to the extra exercise object.
 | ||||||
|         }); |         }); | ||||||
|         it('returns a non-successful response if a non-existing learning path is queried', async () => { |         it('returns a non-successful response if a non-existing learning path is queried', async () => { | ||||||
|             const result = await databaseLearningPathProvider.fetchLearningPaths( |             const result = await databaseLearningPathProvider.fetchLearningPaths( | ||||||
|                 [example.learningPath.hruid], |                 [testLearningPath.hruid], | ||||||
|                 Language.Abkhazian, // Wrong language
 |                 Language.Abkhazian, // Wrong language
 | ||||||
|                 'the source' |                 'the source' | ||||||
|             ); |             ); | ||||||
|  | @ -234,26 +167,26 @@ describe('DatabaseLearningPathProvider', () => { | ||||||
|     describe('searchLearningPaths', () => { |     describe('searchLearningPaths', () => { | ||||||
|         it('returns the correct learning path when queried with a substring of its title', async () => { |         it('returns the correct learning path when queried with a substring of its title', async () => { | ||||||
|             const result = await databaseLearningPathProvider.searchLearningPaths( |             const result = await databaseLearningPathProvider.searchLearningPaths( | ||||||
|                 example.learningPath.title.substring(2, 6), |                 testLearningPath.title.substring(2, 6), | ||||||
|                 example.learningPath.language |                 testLearningPath.language | ||||||
|             ); |             ); | ||||||
|             expect(result.length).toBe(1); |             expect(result.length).toBe(1); | ||||||
|             expect(result[0].title).toBe(example.learningPath.title); |             expect(result[0].title).toBe(testLearningPath.title); | ||||||
|             expect(result[0].description).toBe(example.learningPath.description); |             expect(result[0].description).toBe(testLearningPath.description); | ||||||
|         }); |         }); | ||||||
|         it('returns the correct learning path when queried with a substring of the description', async () => { |         it('returns the correct learning path when queried with a substring of the description', async () => { | ||||||
|             const result = await databaseLearningPathProvider.searchLearningPaths( |             const result = await databaseLearningPathProvider.searchLearningPaths( | ||||||
|                 example.learningPath.description.substring(5, 12), |                 testLearningPath.description.substring(5, 12), | ||||||
|                 example.learningPath.language |                 testLearningPath.language | ||||||
|             ); |             ); | ||||||
|             expect(result.length).toBe(1); |             expect(result.length).toBe(1); | ||||||
|             expect(result[0].title).toBe(example.learningPath.title); |             expect(result[0].title).toBe(testLearningPath.title); | ||||||
|             expect(result[0].description).toBe(example.learningPath.description); |             expect(result[0].description).toBe(testLearningPath.description); | ||||||
|         }); |         }); | ||||||
|         it('returns an empty result when queried with a text which is not a substring of the title or the description of a learning path', async () => { |         it('returns an empty result when queried with a text which is not a substring of the title or the description of a learning path', async () => { | ||||||
|             const result = await databaseLearningPathProvider.searchLearningPaths( |             const result = await databaseLearningPathProvider.searchLearningPaths( | ||||||
|                 'substring which does not occur in the title or the description of a learning object', |                 'substring which does not occur in the title or the description of a learning object', | ||||||
|                 example.learningPath.language |                 testLearningPath.language | ||||||
|             ); |             ); | ||||||
|             expect(result.length).toBe(0); |             expect(result.length).toBe(0); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -1,22 +1,11 @@ | ||||||
| import { beforeAll, describe, expect, it } from 'vitest'; | import { beforeAll, describe, expect, it } from 'vitest'; | ||||||
| import { setupTestApp } from '../../setup-tests'; | import { setupTestApp } from '../../setup-tests'; | ||||||
| import { LearningObject } from '../../../src/entities/content/learning-object.entity'; |  | ||||||
| import { LearningPath } from '../../../src/entities/content/learning-path.entity'; |  | ||||||
| import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; |  | ||||||
| import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; |  | ||||||
| import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; |  | ||||||
| import learningPathService from '../../../src/services/learning-paths/learning-path-service'; | import learningPathService from '../../../src/services/learning-paths/learning-path-service'; | ||||||
| import { Language } from '@dwengo-1/common/util/language'; | import { Language } from '@dwengo-1/common/util/language'; | ||||||
| 
 | import { | ||||||
| async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { |     testPartiallyDatabaseAndPartiallyDwengoApiLearningPath | ||||||
|     const learningObjectRepo = getLearningObjectRepository(); | } from "../../test_assets/content/learning-paths.testdata"; | ||||||
|     const learningPathRepo = getLearningPathRepository(); | import {LearningPath as LearningPathDTO} from "@dwengo-1/common/interfaces/learning-content"; | ||||||
|     const learningObject = learningObjectExample.createLearningObject(); |  | ||||||
|     const learningPath = learningPathExample.createLearningPath(); |  | ||||||
|     await learningObjectRepo.save(learningObject); |  | ||||||
|     await learningPathRepo.save(learningPath); |  | ||||||
|     return { learningObject, learningPath }; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| const TEST_DWENGO_LEARNING_PATH_HRUID = 'pn_werking'; | const TEST_DWENGO_LEARNING_PATH_HRUID = 'pn_werking'; | ||||||
| const TEST_DWENGO_LEARNING_PATH_TITLE = 'Werken met notebooks'; | const TEST_DWENGO_LEARNING_PATH_TITLE = 'Werken met notebooks'; | ||||||
|  | @ -24,42 +13,49 @@ const TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY = 'Microscopie'; | ||||||
| const TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES = 'su$m8f9usf89ud<p9<U8SDP8UP9'; | const TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES = 'su$m8f9usf89ud<p9<U8SDP8UP9'; | ||||||
| 
 | 
 | ||||||
| describe('LearningPathService', () => { | describe('LearningPathService', () => { | ||||||
|     let example: { learningObject: LearningObject; learningPath: LearningPath }; |     let testLearningPath: LearningPathDTO; | ||||||
|     beforeAll(async () => { |     beforeAll(async () => { | ||||||
|         await setupTestApp(); |         await setupTestApp(); | ||||||
|         example = await initExampleData(); |         testLearningPath = testPartiallyDatabaseAndPartiallyDwengoApiLearningPath | ||||||
|     }); |     }); | ||||||
|     describe('fetchLearningPaths', () => { |     describe('fetchLearningPaths', () => { | ||||||
|         it('should return learning paths both from the database and from the Dwengo API', async () => { |         it('should return learning paths both from the database and from the Dwengo API', async () => { | ||||||
|             const result = await learningPathService.fetchLearningPaths( |             const result = await learningPathService.fetchLearningPaths( | ||||||
|                 [example.learningPath.hruid, TEST_DWENGO_LEARNING_PATH_HRUID], |                 [testLearningPath.hruid, TEST_DWENGO_LEARNING_PATH_HRUID], | ||||||
|                 example.learningPath.language, |                 testLearningPath.language as Language, | ||||||
|                 'the source' |                 'the source' | ||||||
|             ); |             ); | ||||||
|             expect(result.success).toBeTruthy(); |             expect(result.success).toBeTruthy(); | ||||||
|             expect(result.data?.filter((it) => it.hruid === TEST_DWENGO_LEARNING_PATH_HRUID).length).not.toBe(0); |             expect(result.data?.filter((it) => it.hruid === TEST_DWENGO_LEARNING_PATH_HRUID).length).not.toBe(0); | ||||||
|             expect(result.data?.filter((it) => it.hruid === example.learningPath.hruid).length).not.toBe(0); |             expect(result.data?.filter((it) => it.hruid === testLearningPath.hruid).length).not.toBe(0); | ||||||
|             expect(result.data?.find((it) => it.hruid === TEST_DWENGO_LEARNING_PATH_HRUID)?.title).toEqual(TEST_DWENGO_LEARNING_PATH_TITLE); |             expect(result.data?.find((it) => it.hruid === TEST_DWENGO_LEARNING_PATH_HRUID)?.title).toEqual(TEST_DWENGO_LEARNING_PATH_TITLE); | ||||||
|             expect(result.data?.find((it) => it.hruid === example.learningPath.hruid)?.title).toEqual(example.learningPath.title); |             expect(result.data?.find((it) => it.hruid === testLearningPath.hruid)?.title).toEqual(testLearningPath.title); | ||||||
|         }); |         }); | ||||||
|         it('should include both the learning objects from the Dwengo API and learning objects from the database in its response', async () => { |         it('should include both the learning objects from the Dwengo API and learning objects from the database in its response', async () => { | ||||||
|             const result = await learningPathService.fetchLearningPaths([example.learningPath.hruid], example.learningPath.language, 'the source'); |             const result = await learningPathService.fetchLearningPaths( | ||||||
|  |                 [testLearningPath.hruid], | ||||||
|  |                 testLearningPath.language as Language, | ||||||
|  |                 'the source' | ||||||
|  |             ); | ||||||
|             expect(result.success).toBeTruthy(); |             expect(result.success).toBeTruthy(); | ||||||
|             expect(result.data?.length).toBe(1); |             expect(result.data?.length).toBe(1); | ||||||
| 
 | 
 | ||||||
|             // Should include all the nodes, even those pointing to foreign learning objects.
 |             // Should include all the nodes, even those pointing to foreign learning objects.
 | ||||||
|             expect([...result.data![0].nodes.map((it) => it.learningobject_hruid)].sort((a, b) => a.localeCompare(b))).toEqual( |             expect([...result.data![0].nodes.map((it) => it.learningobject_hruid)].sort((a, b) => a.localeCompare(b))).toEqual( | ||||||
|                 example.learningPath.nodes.map((it) => it.learningObjectHruid).sort((a, b) => a.localeCompare(b)) |                 testLearningPath.nodes.map((it) => it.learningobject_hruid).sort((a, b) => a.localeCompare(b)) | ||||||
|             ); |             ); | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|     describe('searchLearningPath', () => { |     describe('searchLearningPath', () => { | ||||||
|         it('should include both the learning paths from the Dwengo API and those from the database in its response', async () => { |         it('should include both the learning paths from the Dwengo API and those from the database in its response', async () => { | ||||||
|             // This matches the learning object in the database, but definitely also some learning objects in the Dwengo API.
 |             // This matches the learning object in the database, but definitely also some learning objects in the Dwengo API.
 | ||||||
|             const result = await learningPathService.searchLearningPaths(example.learningPath.title.substring(2, 3), example.learningPath.language); |             const result = await learningPathService.searchLearningPaths( | ||||||
|  |                 testLearningPath.title.substring(2, 3), | ||||||
|  |                 testLearningPath.language as Language | ||||||
|  |             ); | ||||||
| 
 | 
 | ||||||
|             // Should find the one from the database
 |             // Should find the one from the database
 | ||||||
|             expect(result.filter((it) => it.hruid === example.learningPath.hruid && it.title === example.learningPath.title).length).toBe(1); |             expect(result.filter((it) => it.hruid === testLearningPath.hruid && it.title === testLearningPath.title).length).toBe(1); | ||||||
| 
 | 
 | ||||||
|             // But should not only find that one.
 |             // But should not only find that one.
 | ||||||
|             expect(result.length).not.toBeLessThan(2); |             expect(result.length).not.toBeLessThan(2); | ||||||
|  | @ -71,7 +67,7 @@ describe('LearningPathService', () => { | ||||||
|             expect(result.length).not.toBe(0); |             expect(result.length).not.toBe(0); | ||||||
| 
 | 
 | ||||||
|             // But not the example learning path.
 |             // But not the example learning path.
 | ||||||
|             expect(result.filter((it) => it.hruid === example.learningPath.hruid && it.title === example.learningPath.title).length).toBe(0); |             expect(result.filter((it) => it.hruid === testLearningPath.hruid && it.title === testLearningPath.title).length).toBe(0); | ||||||
|         }); |         }); | ||||||
|         it('should return an empty list if neither the Dwengo API nor the database contains matches', async () => { |         it('should return an empty list if neither the Dwengo API nor the database contains matches', async () => { | ||||||
|             const result = await learningPathService.searchLearningPaths(TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES, Language.Dutch); |             const result = await learningPathService.searchLearningPaths(TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES, Language.Dutch); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ import { makeTestAnswers } from './test_assets/questions/answers.testdata.js'; | ||||||
| import { makeTestSubmissions } from './test_assets/assignments/submission.testdata.js'; | import { makeTestSubmissions } from './test_assets/assignments/submission.testdata.js'; | ||||||
| import {Collection} from "@mikro-orm/core"; | import {Collection} from "@mikro-orm/core"; | ||||||
| import {Group} from "../src/entities/assignments/group.entity"; | import {Group} from "../src/entities/assignments/group.entity"; | ||||||
|  | import {LearningObject} from "../src/entities/content/learning-object.entity"; | ||||||
| 
 | 
 | ||||||
| export async function setupTestApp(): Promise<void> { | export async function setupTestApp(): Promise<void> { | ||||||
|     dotenv.config({ path: '.env.test' }); |     dotenv.config({ path: '.env.test' }); | ||||||
|  | @ -58,4 +59,6 @@ export async function setupTestApp(): Promise<void> { | ||||||
|         ...answers, |         ...answers, | ||||||
|         ...submissions, |         ...submissions, | ||||||
|     ]); |     ]); | ||||||
|  | 
 | ||||||
|  |     console.log(await em.findAll(LearningObject)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -133,7 +133,7 @@ export function expectToBeCorrectLearningPath( | ||||||
|         const expectedNode = [...expectedLearningPathNodes.entries()].find( |         const expectedNode = [...expectedLearningPathNodes.entries()].find( | ||||||
|             ([key, _]) => key.learningObjectHruid === nodeKey.learningObjectHruid && key.language === node.language && key.version === node.version |             ([key, _]) => key.learningObjectHruid === nodeKey.learningObjectHruid && key.language === node.language && key.version === node.version | ||||||
|         )![1]; |         )![1]; | ||||||
|         expect(node.start_node).toEqual(expectedNode.startNode); |         expect(Boolean(node.start_node)).toEqual(Boolean(expectedNode.startNode)); | ||||||
| 
 | 
 | ||||||
|         expect(new Set(node.transitions.map((it) => it.next.hruid))).toEqual( |         expect(new Set(node.transitions.map((it) => it.next.hruid))).toEqual( | ||||||
|             new Set(expectedNode.transitions.map((it) => it.next.learningObjectHruid)) |             new Set(expectedNode.transitions.map((it) => it.next.learningObjectHruid)) | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
|      * Group #1 for Assignment #1 in class 'id01' |      * Group #1 for Assignment #1 in class 'id01' | ||||||
|      * => Assigned to do learning path 'id02' |      * => Assigned to do learning path 'id02' | ||||||
|      */ |      */ | ||||||
|     const group01 = em.create(Group, { |     group01 = em.create(Group, { | ||||||
|         assignment: assignments[0], |         assignment: assignments[0], | ||||||
|         groupNumber: 1, |         groupNumber: 1, | ||||||
|         members: students.slice(0, 2), |         members: students.slice(0, 2), | ||||||
|  | @ -18,7 +18,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
|      * Group #2 for Assignment #1 in class 'id01' |      * Group #2 for Assignment #1 in class 'id01' | ||||||
|      * => Assigned to do learning path 'id02' |      * => Assigned to do learning path 'id02' | ||||||
|      */ |      */ | ||||||
|     const group02 = em.create(Group, { |     group02 = em.create(Group, { | ||||||
|         assignment: assignments[0], |         assignment: assignments[0], | ||||||
|         groupNumber: 2, |         groupNumber: 2, | ||||||
|         members: students.slice(2, 4), |         members: students.slice(2, 4), | ||||||
|  | @ -28,7 +28,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
|      * Group #3 for Assignment #1 in class 'id01' |      * Group #3 for Assignment #1 in class 'id01' | ||||||
|      * => Assigned to do learning path 'id02' |      * => Assigned to do learning path 'id02' | ||||||
|      */ |      */ | ||||||
|     const group03 = em.create(Group, { |     group03 = em.create(Group, { | ||||||
|         assignment: assignments[0], |         assignment: assignments[0], | ||||||
|         groupNumber: 3, |         groupNumber: 3, | ||||||
|         members: students.slice(4, 6), |         members: students.slice(4, 6), | ||||||
|  | @ -38,7 +38,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
|      * Group #4 for Assignment #2 in class 'id02' |      * Group #4 for Assignment #2 in class 'id02' | ||||||
|      * => Assigned to do learning path 'id01' |      * => Assigned to do learning path 'id01' | ||||||
|      */ |      */ | ||||||
|     const group04 = em.create(Group, { |     group04 = em.create(Group, { | ||||||
|         assignment: assignments[1], |         assignment: assignments[1], | ||||||
|         groupNumber: 4, |         groupNumber: 4, | ||||||
|         members: students.slice(3, 4), |         members: students.slice(3, 4), | ||||||
|  | @ -48,7 +48,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
|      * Group #5 for Assignment #4 in class 'id01' |      * Group #5 for Assignment #4 in class 'id01' | ||||||
|      * => Assigned to do learning path 'id01' |      * => Assigned to do learning path 'id01' | ||||||
|      */ |      */ | ||||||
|     const group05 = em.create(Group, { |     group05 = em.create(Group, { | ||||||
|         assignment: assignments[3], |         assignment: assignments[3], | ||||||
|         groupNumber: 1, |         groupNumber: 1, | ||||||
|         members: students.slice(0, 2), |         members: students.slice(0, 2), | ||||||
|  | @ -56,3 +56,30 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | ||||||
| 
 | 
 | ||||||
|     return [group01, group02, group03, group04, group05]; |     return [group01, group02, group03, group04, group05]; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | let group01: Group; | ||||||
|  | let group02: Group; | ||||||
|  | let group03: Group; | ||||||
|  | let group04: Group; | ||||||
|  | let group05: Group; | ||||||
|  | 
 | ||||||
|  | export function getTestGroup01() { | ||||||
|  |     return group01; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getTestGroup02() { | ||||||
|  |     return group02; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getTestGroup03() { | ||||||
|  |     return group03; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getTestGroup04() { | ||||||
|  |     return group04; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getTestGroup05() { | ||||||
|  |     return group05; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ export function createReturnValue(): ReturnValue { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const testLearningObject01: RequiredEntityData<LearningObject> = { | export const testLearningObject01: RequiredEntityData<LearningObject> = { | ||||||
|     hruid: 'id01', |     hruid: `${getEnvVar(envVars.UserContentPrefix)}id01`, | ||||||
|     language: Language.English, |     language: Language.English, | ||||||
|     version: 1, |     version: 1, | ||||||
|     admins: [], |     admins: [], | ||||||
|  | @ -61,7 +61,7 @@ export const testLearningObject01: RequiredEntityData<LearningObject> = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const testLearningObject02: RequiredEntityData<LearningObject> = { | export const testLearningObject02: RequiredEntityData<LearningObject> = { | ||||||
|     hruid: 'id02', |     hruid: `${getEnvVar(envVars.UserContentPrefix)}id02`, | ||||||
|     language: Language.English, |     language: Language.English, | ||||||
|     version: 1, |     version: 1, | ||||||
|     admins: [], |     admins: [], | ||||||
|  | @ -85,7 +85,7 @@ export const testLearningObject02: RequiredEntityData<LearningObject> = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const testLearningObject03: RequiredEntityData<LearningObject> = { | export const testLearningObject03: RequiredEntityData<LearningObject> = { | ||||||
|     hruid: 'id03', |     hruid: `${getEnvVar(envVars.UserContentPrefix)}id03`, | ||||||
|     language: Language.English, |     language: Language.English, | ||||||
|     version: 1, |     version: 1, | ||||||
|     admins: [], |     admins: [], | ||||||
|  | @ -112,7 +112,7 @@ export const testLearningObject03: RequiredEntityData<LearningObject> = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const testLearningObject04: RequiredEntityData<LearningObject> = { | export const testLearningObject04: RequiredEntityData<LearningObject> = { | ||||||
|     hruid: 'id04', |     hruid: `${getEnvVar(envVars.UserContentPrefix)}id04`, | ||||||
|     language: Language.English, |     language: Language.English, | ||||||
|     version: 1, |     version: 1, | ||||||
|     admins: [], |     admins: [], | ||||||
|  | @ -139,7 +139,7 @@ export const testLearningObject04: RequiredEntityData<LearningObject> = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const testLearningObject05: RequiredEntityData<LearningObject> = { | export const testLearningObject05: RequiredEntityData<LearningObject> = { | ||||||
|     hruid: 'id05', |     hruid: `${getEnvVar(envVars.UserContentPrefix)}id05`, | ||||||
|     language: Language.English, |     language: Language.English, | ||||||
|     version: 1, |     version: 1, | ||||||
|     admins: [], |     admins: [], | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger