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 { setupTestApp } from '../../setup-tests.js';
|
||||
import { LearningPath } from '../../../src/entities/content/learning-path.entity.js';
|
||||
import {
|
||||
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 { getSubmissionRepository } from '../../../src/data/repositories.js';
|
||||
|
||||
import databaseLearningPathProvider from '../../../src/services/learning-paths/database-learning-path-provider.js';
|
||||
import { expectToBeCorrectLearningPath } from '../../test-utils/expectations.js';
|
||||
import learningObjectService from '../../../src/services/learning-objects/learning-object-service.js';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
|
||||
import {
|
||||
ConditionTestLearningPathAndLearningObjects,
|
||||
createConditionTestLearningPathAndLearningObjects,
|
||||
} from '../../test-assets/learning-paths/test-conditions-example.js';
|
||||
import { Student } from '../../../src/entities/users/student.entity.js';
|
||||
|
||||
import { LearningObjectNode, LearningPathResponse } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
const STUDENT_A_USERNAME = 'student_a';
|
||||
const STUDENT_B_USERNAME = 'student_b';
|
||||
const CLASS_NAME = 'test_class';
|
||||
|
||||
async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> {
|
||||
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,
|
||||
};
|
||||
}
|
||||
LearningObjectNode,
|
||||
LearningPathResponse
|
||||
} from '@dwengo-1/common/interfaces/learning-content';
|
||||
import {
|
||||
testLearningObject01, testLearningObjectEssayQuestion,
|
||||
testLearningObjectMultipleChoice
|
||||
} from "../../test_assets/content/learning-objects.testdata";
|
||||
import {testLearningPathWithConditions} from "../../test_assets/content/learning-paths.testdata";
|
||||
import {mapToLearningPath} from "../../../src/services/learning-paths/learning-path-service";
|
||||
import {getTestGroup01, getTestGroup02} from "../../test_assets/assignments/groups.testdata";
|
||||
import { Group } from '../../../src/entities/assignments/group.entity.js';
|
||||
import {RequiredEntityData} from "@mikro-orm/core";
|
||||
|
||||
function expectBranchingObjectNode(
|
||||
result: LearningPathResponse,
|
||||
persTestData: {
|
||||
learningContent: ConditionTestLearningPathAndLearningObjects;
|
||||
studentA: Student;
|
||||
studentB: Student;
|
||||
}
|
||||
result: LearningPathResponse
|
||||
): LearningObjectNode {
|
||||
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);
|
||||
return branchingObjectMatches[0];
|
||||
}
|
||||
|
||||
describe('DatabaseLearningPathProvider', () => {
|
||||
let example: { learningObject: LearningObject; learningPath: LearningPath };
|
||||
let persTestData: { learningContent: ConditionTestLearningPathAndLearningObjects; studentA: Student; studentB: Student };
|
||||
let testLearningPath: LearningPath;
|
||||
let branchingLearningObject: RequiredEntityData<LearningObject>;
|
||||
let extraExerciseLearningObject: RequiredEntityData<LearningObject>;
|
||||
let finalLearningObject: RequiredEntityData<LearningObject>;
|
||||
let groupA: Group;
|
||||
let groupB: Group;
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupTestApp();
|
||||
example = await initExampleData();
|
||||
persTestData = await initPersonalizationTestData();
|
||||
testLearningPath = mapToLearningPath(testLearningPathWithConditions, []);
|
||||
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', () => {
|
||||
it('returns the learning path correctly', async () => {
|
||||
const result = await databaseLearningPathProvider.fetchLearningPaths(
|
||||
[example.learningPath.hruid],
|
||||
example.learningPath.language,
|
||||
[testLearningPath.hruid],
|
||||
testLearningPath.language as Language,
|
||||
'the source'
|
||||
);
|
||||
expect(result.success).toBe(true);
|
||||
|
@ -170,7 +87,7 @@ describe('DatabaseLearningPathProvider', () => {
|
|||
|
||||
const learningObjectsOnPath = (
|
||||
await Promise.all(
|
||||
example.learningPath.nodes.map(async (node) =>
|
||||
testLearningPath.nodes.map(async (node) =>
|
||||
learningObjectService.getLearningObjectById({
|
||||
hruid: node.learningObjectHruid,
|
||||
version: node.version,
|
||||
|
@ -180,49 +97,65 @@ describe('DatabaseLearningPathProvider', () => {
|
|||
)
|
||||
).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 () => {
|
||||
// For student A:
|
||||
let result = await databaseLearningPathProvider.fetchLearningPaths(
|
||||
[persTestData.learningContent.learningPath.hruid],
|
||||
persTestData.learningContent.learningPath.language,
|
||||
[testLearningPath.hruid],
|
||||
testLearningPath.language,
|
||||
'the source',
|
||||
{ type: 'student', student: persTestData.studentA }
|
||||
groupA
|
||||
);
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result.data?.length).toBe(1);
|
||||
|
||||
// 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(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe(
|
||||
1
|
||||
); // There should however be a path to the extra exercise object.
|
||||
expect(
|
||||
branchingObject.transitions.filter(
|
||||
it => it.next.hruid === finalLearningObject.hruid
|
||||
).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:
|
||||
result = await databaseLearningPathProvider.fetchLearningPaths(
|
||||
[persTestData.learningContent.learningPath.hruid],
|
||||
persTestData.learningContent.learningPath.language,
|
||||
[testLearningPath.hruid],
|
||||
testLearningPath.language,
|
||||
'the source',
|
||||
{ type: 'student', student: persTestData.studentB }
|
||||
groupB
|
||||
);
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result.data?.length).toBe(1);
|
||||
|
||||
// There should still be exactly one branching object
|
||||
branchingObject = expectBranchingObjectNode(result, persTestData);
|
||||
branchingObject = expectBranchingObjectNode(result);
|
||||
|
||||
// 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(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe(
|
||||
0
|
||||
); // There should not be a path anymore to the extra exercise object.
|
||||
expect(
|
||||
branchingObject.transitions.filter(
|
||||
(it) => it.next.hruid === finalLearningObject.hruid
|
||||
).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 () => {
|
||||
const result = await databaseLearningPathProvider.fetchLearningPaths(
|
||||
[example.learningPath.hruid],
|
||||
[testLearningPath.hruid],
|
||||
Language.Abkhazian, // Wrong language
|
||||
'the source'
|
||||
);
|
||||
|
@ -234,26 +167,26 @@ describe('DatabaseLearningPathProvider', () => {
|
|||
describe('searchLearningPaths', () => {
|
||||
it('returns the correct learning path when queried with a substring of its title', async () => {
|
||||
const result = await databaseLearningPathProvider.searchLearningPaths(
|
||||
example.learningPath.title.substring(2, 6),
|
||||
example.learningPath.language
|
||||
testLearningPath.title.substring(2, 6),
|
||||
testLearningPath.language
|
||||
);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].title).toBe(example.learningPath.title);
|
||||
expect(result[0].description).toBe(example.learningPath.description);
|
||||
expect(result[0].title).toBe(testLearningPath.title);
|
||||
expect(result[0].description).toBe(testLearningPath.description);
|
||||
});
|
||||
it('returns the correct learning path when queried with a substring of the description', async () => {
|
||||
const result = await databaseLearningPathProvider.searchLearningPaths(
|
||||
example.learningPath.description.substring(5, 12),
|
||||
example.learningPath.language
|
||||
testLearningPath.description.substring(5, 12),
|
||||
testLearningPath.language
|
||||
);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].title).toBe(example.learningPath.title);
|
||||
expect(result[0].description).toBe(example.learningPath.description);
|
||||
expect(result[0].title).toBe(testLearningPath.title);
|
||||
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 () => {
|
||||
const result = await databaseLearningPathProvider.searchLearningPaths(
|
||||
'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);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue