From ba912c3ef035b9dc4762bbeba52830d3b8cfff08 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Fri, 18 Apr 2025 23:47:56 +0200 Subject: [PATCH] fix: Test- en linting-errors opgelost --- backend/src/controllers/submissions.ts | 3 +- .../data/assignments/submission-repository.ts | 32 +++++---- .../data/content/learning-path-repository.ts | 8 +-- .../content/learning-object.entity.ts | 2 +- .../learning-paths/learning-path-service.ts | 4 +- backend/src/services/submissions.ts | 17 +++-- .../src/sqlite-autoincrement-workaround.ts | 5 +- backend/src/util/base64-buffer-conversion.ts | 8 +-- .../data/assignments/assignments.test.ts | 5 +- .../data/assignments/submissions.test.ts | 8 ++- .../learning-object-repository.test.ts | 2 +- .../tests/data/content/learning-paths.test.ts | 9 ++- .../database-learning-path-provider.test.ts | 21 +----- backend/tests/test-utils/expectations.ts | 69 ++++++++----------- backend/tests/test-utils/load-test-asset.ts | 6 +- .../assignments/groups.testdata.ts | 12 ++-- .../test_essay_question/rendering.txt | 2 +- .../test_multiple_choice/rendering.txt | 2 +- .../content/learning-paths.testdata.ts | 2 +- .../test_assets/users/students.testdata.ts | 2 +- 20 files changed, 103 insertions(+), 116 deletions(-) diff --git a/backend/src/controllers/submissions.ts b/backend/src/controllers/submissions.ts index 68851164..a117d7bf 100644 --- a/backend/src/controllers/submissions.ts +++ b/backend/src/controllers/submissions.ts @@ -19,8 +19,7 @@ export async function getSubmissionsHandler(req: Request, res: Response): Promis const forGroup = req.query.forGroup as string | undefined; - let submissions: SubmissionDTO[] - submissions = await getSubmissionsForLearningObjectAndAssignment( + const submissions: SubmissionDTO[] = await getSubmissionsForLearningObjectAndAssignment( loHruid, lang, version, diff --git a/backend/src/data/assignments/submission-repository.ts b/backend/src/data/assignments/submission-repository.ts index e028c7cd..743409bd 100644 --- a/backend/src/data/assignments/submission-repository.ts +++ b/backend/src/data/assignments/submission-repository.ts @@ -61,28 +61,36 @@ export class SubmissionRepository extends DwengoEntityRepository { /** * Looks up all submissions for the given learning object which were submitted as part of the given assignment. - * When forGroup is set, only the submissions of the given group are shown. */ public async findAllSubmissionsForLearningObjectAndAssignment( loId: LearningObjectIdentifier, assignment: Assignment, - forGroup?: number ): Promise { - const onBehalfOf = forGroup - ? { - assignment, - groupNumber: forGroup - } - : { - assignment, - }; - return this.findAll({ where: { learningObjectHruid: loId.hruid, learningObjectLanguage: loId.language, learningObjectVersion: loId.version, - onBehalfOf, + onBehalfOf: { + assignment + } + }, + }); + } + + /** + * Looks up all submissions for the given learning object which were submitted by the given group + */ + public async findAllSubmissionsForLearningObjectAndGroup( + loId: LearningObjectIdentifier, + group: Group, + ): Promise { + return this.findAll({ + where: { + learningObjectHruid: loId.hruid, + learningObjectLanguage: loId.language, + learningObjectVersion: loId.version, + onBehalfOf: group }, }); } diff --git a/backend/src/data/content/learning-path-repository.ts b/backend/src/data/content/learning-path-repository.ts index 9db7c361..c405cd1f 100644 --- a/backend/src/data/content/learning-path-repository.ts +++ b/backend/src/data/content/learning-path-repository.ts @@ -29,13 +29,13 @@ export class LearningPathRepository extends DwengoEntityRepository } public createNode( - nodeData: RequiredEntityData + nodeData: RequiredEntityData ): LearningPathNode { return this.em.create(LearningPathNode, nodeData); } public createTransition( - transitionData: RequiredEntityData + transitionData: RequiredEntityData ): LearningPathTransition { return this.em.create(LearningPathTransition, transitionData) } @@ -53,7 +53,7 @@ export class LearningPathRepository extends DwengoEntityRepository } const em = this.getEntityManager(); await em.persistAndFlush(path); - await Promise.all(nodes.map(it => em.persistAndFlush(it))); - await Promise.all(transitions.map(it => em.persistAndFlush(it))); + await Promise.all(nodes.map(async it => em.persistAndFlush(it))); + await Promise.all(transitions.map(async it => em.persistAndFlush(it))); } } diff --git a/backend/src/entities/content/learning-object.entity.ts b/backend/src/entities/content/learning-object.entity.ts index ac111906..3fe2a884 100644 --- a/backend/src/entities/content/learning-object.entity.ts +++ b/backend/src/entities/content/learning-object.entity.ts @@ -42,7 +42,7 @@ export class LearningObject { @Property({ type: 'array' }) keywords: string[] = []; - @Property({ type: new ArrayType(i => +i), nullable: true }) + @Property({ type: new ArrayType(i => Number(i)), nullable: true }) targetAges?: number[] = []; @Property({ type: 'bool' }) diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index 3b8a257c..3a0109ec 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -61,9 +61,9 @@ export function mapToLearningPath( next: toNode, condition: transDto.condition ?? "true" }); - } else { + } return undefined; - } + }).filter(it => it).map(it => it!); fromNode.transitions = new Collection(fromNode, transitions); diff --git a/backend/src/services/submissions.ts b/backend/src/services/submissions.ts index 530be7a0..bd217ea0 100644 --- a/backend/src/services/submissions.ts +++ b/backend/src/services/submissions.ts @@ -1,4 +1,4 @@ -import {getAssignmentRepository, getSubmissionRepository} from '../data/repositories.js'; +import {getAssignmentRepository, getGroupRepository, getSubmissionRepository} from '../data/repositories.js'; import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; import { NotFoundException } from '../exceptions/not-found-exception.js'; import { mapToSubmission, mapToSubmissionDTO } from '../interfaces/submission.js'; @@ -37,11 +37,8 @@ export async function createSubmission(submissionDTO: SubmissionDTO): Promise mapToSubmissionDTO(s)); } diff --git a/backend/src/sqlite-autoincrement-workaround.ts b/backend/src/sqlite-autoincrement-workaround.ts index e45a5141..c50488a9 100644 --- a/backend/src/sqlite-autoincrement-workaround.ts +++ b/backend/src/sqlite-autoincrement-workaround.ts @@ -27,7 +27,10 @@ export class SqliteAutoincrementSubscriber implements EventSubscriber { for (const prop of Object.values(args.meta.properties)) { const property = prop as EntityProperty; - if (property.primary && property.autoincrement && !(args.entity as Record)[property.name]) { + if ( + property.primary && property.autoincrement + && (args.entity as Record)[property.name] === undefined + ) { // Obtain and increment sequence number of this entity. const propertyKey = args.meta.class.name + '.' + property.name; const nextSeqNumber = this.sequenceNumbersForEntityType.get(propertyKey) || 0; diff --git a/backend/src/util/base64-buffer-conversion.ts b/backend/src/util/base64-buffer-conversion.ts index 0453b604..a2b23002 100644 --- a/backend/src/util/base64-buffer-conversion.ts +++ b/backend/src/util/base64-buffer-conversion.ts @@ -2,10 +2,10 @@ * Convert a Base64-encoded string into a buffer with the same data. * @param base64 The Base64 encoded string. */ -export function base64ToArrayBuffer(base64: string) { - var binaryString = atob(base64); - var bytes = new Uint8Array(binaryString.length); - for (var i = 0; i < binaryString.length; i++) { +export function base64ToArrayBuffer(base64: string): ArrayBuffer { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; diff --git a/backend/tests/data/assignments/assignments.test.ts b/backend/tests/data/assignments/assignments.test.ts index 1fe52523..6ac15d09 100644 --- a/backend/tests/data/assignments/assignments.test.ts +++ b/backend/tests/data/assignments/assignments.test.ts @@ -33,9 +33,10 @@ describe('AssignmentRepository', () => { it('should find all by username of the responsible teacher', async () => { const result = await assignmentRepository.findAllByResponsibleTeacher('testleerkracht1'); - const resultIds = result.map((it) => it.id).sort((a, b) => (a ?? 0) - (b ?? 0)); + const resultIds = result.map((it) => it.id) + .sort((a, b) => (a ?? 0) - (b ?? 0)); - expect(resultIds).toEqual([1, 3, 4]); + expect(resultIds).toEqual([1, 1, 3, 4]); }); it('should not find removed assignment', async () => { diff --git a/backend/tests/data/assignments/submissions.test.ts b/backend/tests/data/assignments/submissions.test.ts index 31aafc1d..e5d656da 100644 --- a/backend/tests/data/assignments/submissions.test.ts +++ b/backend/tests/data/assignments/submissions.test.ts @@ -91,9 +91,11 @@ describe('SubmissionRepository', () => { expect(result[2].submissionNumber).toBe(3); }); - it("should find only the submissions for a certain learning object and assignment made for the user's group", async () => { - const result = await submissionRepository.findAllSubmissionsForLearningObjectAndAssignment(loId, assignment!, 'Tool'); - // (student Tool is in group #2) + it("should find only the submissions for a certain learning object and assignment made for the given group", async () => { + const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 2); + const result = await submissionRepository.findAllSubmissionsForLearningObjectAndGroup( + loId, group! + ); expect(result).toHaveLength(1); diff --git a/backend/tests/data/content/learning-object-repository.test.ts b/backend/tests/data/content/learning-object-repository.test.ts index 75d46796..3400c913 100644 --- a/backend/tests/data/content/learning-object-repository.test.ts +++ b/backend/tests/data/content/learning-object-repository.test.ts @@ -41,7 +41,7 @@ describe('LearningObjectRepository', () => { let newerExample: LearningObject; it('should allow a learning object with the same id except a different version to be added', async () => { - let testLearningObject01Newer = structuredClone(testLearningObject01); + const testLearningObject01Newer = structuredClone(testLearningObject01); testLearningObject01Newer.version = 10; testLearningObject01Newer.title += " (nieuw)"; testLearningObject01Newer.uuid = v4(); diff --git a/backend/tests/data/content/learning-paths.test.ts b/backend/tests/data/content/learning-paths.test.ts index 683e1d40..c8620a31 100644 --- a/backend/tests/data/content/learning-paths.test.ts +++ b/backend/tests/data/content/learning-paths.test.ts @@ -3,6 +3,7 @@ import { getLearningPathRepository } from '../../../src/data/repositories'; import { LearningPathRepository } from '../../../src/data/content/learning-path-repository'; import { setupTestApp } from '../../setup-tests'; import { Language } from '@dwengo-1/common/util/language'; +import {testLearningPath01} from "../../test_assets/content/learning-paths.testdata"; describe('LearningPathRepository', () => { let learningPathRepository: LearningPathRepository; @@ -19,10 +20,12 @@ describe('LearningPathRepository', () => { }); it('should return requested learning path', async () => { - const learningPath = await learningPathRepository.findByHruidAndLanguage('id01', Language.English); + const learningPath = await learningPathRepository.findByHruidAndLanguage( + testLearningPath01.hruid, testLearningPath01.language as Language + ); expect(learningPath).toBeTruthy(); - expect(learningPath?.title).toBe('repertoire Tool'); - expect(learningPath?.description).toBe('all about Tool'); + expect(learningPath?.title).toBe(testLearningPath01.title); + expect(learningPath?.description).toBe(testLearningPath01.description); }); }); diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index b48f8c3e..69d88288 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -6,7 +6,6 @@ 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 { @@ -79,29 +78,13 @@ describe('DatabaseLearningPathProvider', () => { it('returns the learning path correctly', async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [testLearningPath.hruid], - testLearningPath.language as Language, + testLearningPath.language, 'the source' ); expect(result.success).toBe(true); expect(result.data?.length).toBe(1); - const learningObjectsOnPath = ( - await Promise.all( - testLearningPath.nodes.map(async (node) => - learningObjectService.getLearningObjectById({ - hruid: node.learningObjectHruid, - version: node.version, - language: node.language, - }) - ) - ) - ).filter((it) => it !== null); - - expectToBeCorrectLearningPath( - result.data![0], - mapToLearningPath(testLearningPathWithConditions, []), - learningObjectsOnPath - ); + expectToBeCorrectLearningPath(result.data![0], testLearningPathWithConditions); }); it('returns the correct personalized learning path', async () => { // For student A: diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index 2ca10d20..c6c49b70 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -1,6 +1,5 @@ import { AssertionError } from 'node:assert'; import { LearningObject } from '../../src/entities/content/learning-object.entity'; -import { LearningPath as LearningPathEntity } from '../../src/entities/content/learning-path.entity'; import { expect } from 'vitest'; import { FilteredLearningObject, LearningPath } from '@dwengo-1/common/interfaces/learning-content'; import {RequiredEntityData} from "@mikro-orm/core"; @@ -17,7 +16,7 @@ const IGNORE_PROPERTIES = ['parent']; export function expectToBeCorrectEntity( actual: T, expected: T, - propertyPrefix: string = "" + propertyPrefix = "" ): void { for (const property in expected) { const prefixedProperty = propertyPrefix + property; @@ -91,55 +90,41 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni * is a correct representation of the given learning path entity. * * @param learningPath The learning path returned by the retriever, service or endpoint - * @param expectedEntity The expected entity - * @param learningObjectsOnPath The learning objects on LearningPath. Necessary since some information in - * the learning path returned from the API endpoint + * @param expected The learning path that should have been returned. */ export function expectToBeCorrectLearningPath( learningPath: LearningPath, - expectedEntity: LearningPathEntity, - learningObjectsOnPath: FilteredLearningObject[] + expected: LearningPath ): void { - expect(learningPath.hruid).toEqual(expectedEntity.hruid); - expect(learningPath.language).toEqual(expectedEntity.language); - expect(learningPath.description).toEqual(expectedEntity.description); - expect(learningPath.title).toEqual(expectedEntity.title); + expect(learningPath.hruid).toEqual(expected.hruid); + expect(learningPath.language).toEqual(expected.language); + expect(learningPath.description).toEqual(expected.description); + expect(learningPath.title).toEqual(expected.title); - const keywords = new Set(learningObjectsOnPath.flatMap((it) => it.keywords || [])); - expect(new Set(learningPath.keywords.split(' '))).toEqual(keywords); + expect(new Set(learningPath.keywords.split(' '))).toEqual(new Set(learningPath.keywords.split(' '))); - const targetAges = new Set(learningObjectsOnPath.flatMap((it) => it.targetAges || [])); - expect(new Set(learningPath.target_ages)).toEqual(targetAges); - expect(learningPath.min_age).toEqual(Math.min(...targetAges)); - expect(learningPath.max_age).toEqual(Math.max(...targetAges)); + expect(new Set(learningPath.target_ages)).toEqual(new Set(expected.target_ages)); + expect(learningPath.min_age).toEqual(Math.min(...expected.target_ages)); + expect(learningPath.max_age).toEqual(Math.max(...expected.target_ages)); - expect(learningPath.num_nodes).toEqual(expectedEntity.nodes.length); - expect(learningPath.image || null).toEqual(expectedEntity.image); + expect(learningPath.num_nodes).toEqual(expected.nodes.length); + expect(learningPath.image ?? null).toEqual(expected.image ?? null); - const expectedLearningPathNodes = new Map( - expectedEntity.nodes.map((node) => [ - { learningObjectHruid: node.learningObjectHruid, language: node.language, version: node.version }, - { startNode: node.startNode, transitions: node.transitions }, - ]) - ); - - for (const node of learningPath.nodes) { - const nodeKey = { - learningObjectHruid: node.learningobject_hruid, - language: node.language, - version: node.version, - }; - expect(expectedLearningPathNodes.keys()).toContainEqual(nodeKey); - const expectedNode = [...expectedLearningPathNodes.entries()].find( - ([key, _]) => key.learningObjectHruid === nodeKey.learningObjectHruid && key.language === node.language && key.version === node.version - )![1]; - expect(Boolean(node.start_node)).toEqual(Boolean(expectedNode.startNode)); - - expect(new Set(node.transitions.map((it) => it.next.hruid))).toEqual( - new Set(expectedNode.transitions.map((it) => it.next.learningObjectHruid)) + for (const node of expected.nodes) { + const correspondingNode = learningPath.nodes.find(it => + node.learningobject_hruid === it.learningobject_hruid && node.language === it.language + && node.version === it.version ); - expect(new Set(node.transitions.map((it) => it.next.language))).toEqual(new Set(expectedNode.transitions.map((it) => it.next.language))); - expect(new Set(node.transitions.map((it) => it.next.version))).toEqual(new Set(expectedNode.transitions.map((it) => it.next.version))); + expect(correspondingNode).toBeTruthy(); + expect(Boolean(correspondingNode!.start_node)).toEqual(Boolean(node.start_node)); + + for (const transition of node.transitions) { + const correspondingTransition = correspondingNode!.transitions.find(it => + it.next.hruid === transition.next.hruid && it.next.language === transition.next.language + && it.next.version === transition.next.version + ); + expect(correspondingTransition).toBeTruthy(); + } } } diff --git a/backend/tests/test-utils/load-test-asset.ts b/backend/tests/test-utils/load-test-asset.ts index c3879d12..b7107a76 100644 --- a/backend/tests/test-utils/load-test-asset.ts +++ b/backend/tests/test-utils/load-test-asset.ts @@ -2,13 +2,13 @@ import fs from 'fs'; import path from 'node:path'; import {fileURLToPath} from "node:url"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +const fileName = fileURLToPath(import.meta.url); +const dirName = path.dirname(fileName); /** * Load the asset at the given path. * @param relPath Path of the asset relative to the test-assets folder. */ export function loadTestAsset(relPath: string): Buffer { - return fs.readFileSync(path.resolve(__dirname, `../test_assets/${relPath}`)); + return fs.readFileSync(path.resolve(dirName, `../test_assets/${relPath}`)); } diff --git a/backend/tests/test_assets/assignments/groups.testdata.ts b/backend/tests/test_assets/assignments/groups.testdata.ts index ae0241b8..3240b8d0 100644 --- a/backend/tests/test_assets/assignments/groups.testdata.ts +++ b/backend/tests/test_assets/assignments/groups.testdata.ts @@ -75,26 +75,26 @@ let group04: Group; let group05: Group; let group1ConditionalLearningPath: Group; -export function getTestGroup01() { +export function getTestGroup01(): Group { return group01; } -export function getTestGroup02() { +export function getTestGroup02(): Group { return group02; } -export function getTestGroup03() { +export function getTestGroup03(): Group { return group03; } -export function getTestGroup04() { +export function getTestGroup04(): Group { return group04; } -export function getTestGroup05() { +export function getTestGroup05(): Group { return group05; } -export function getGroup1ConditionalLearningPath() { +export function getGroup1ConditionalLearningPath(): Group { return group1ConditionalLearningPath; } diff --git a/backend/tests/test_assets/content/learning-object-resources/test_essay_question/rendering.txt b/backend/tests/test_assets/content/learning-object-resources/test_essay_question/rendering.txt index 46f605bd..53c58584 100644 --- a/backend/tests/test_assets/content/learning-object-resources/test_essay_question/rendering.txt +++ b/backend/tests/test_assets/content/learning-object-resources/test_essay_question/rendering.txt @@ -1,5 +1,5 @@
-
+

Reflection

Reflect on this learning path. What have you learned today?

diff --git a/backend/tests/test_assets/content/learning-object-resources/test_multiple_choice/rendering.txt b/backend/tests/test_assets/content/learning-object-resources/test_multiple_choice/rendering.txt index 8c94b4f2..982f40b4 100644 --- a/backend/tests/test_assets/content/learning-object-resources/test_multiple_choice/rendering.txt +++ b/backend/tests/test_assets/content/learning-object-resources/test_multiple_choice/rendering.txt @@ -1,5 +1,5 @@
-
+

Self-evaluation

Are you following along well?

diff --git a/backend/tests/test_assets/content/learning-paths.testdata.ts b/backend/tests/test_assets/content/learning-paths.testdata.ts index 17dc2c30..4d9f09c8 100644 --- a/backend/tests/test_assets/content/learning-paths.testdata.ts +++ b/backend/tests/test_assets/content/learning-paths.testdata.ts @@ -176,7 +176,7 @@ export const testLearningPathWithConditions: LearningPathDTO = { title: 'Example learning path with conditional transitions', description: 'This learning path was made for the purpose of testing conditional transitions', keywords: "test", - target_ages: [18, 19, 20, 21], + target_ages: [10, 11, 12, 13, 14, 15, 16, 17, 18], nodes: [ { learningobject_hruid: testLearningObjectMultipleChoice.hruid, diff --git a/backend/tests/test_assets/users/students.testdata.ts b/backend/tests/test_assets/users/students.testdata.ts index 19a5dfaf..4080766f 100644 --- a/backend/tests/test_assets/users/students.testdata.ts +++ b/backend/tests/test_assets/users/students.testdata.ts @@ -24,5 +24,5 @@ export function makeTestStudents(em: EntityManager): Student[] { } export function getTestleerling1(): Student { - return testStudents.find(it => it.username == "testleerling1"); + return testStudents.find(it => it.username === "testleerling1"); }