[UnitTests] Move helpers to shared and change paths to use @ in backend tests

This commit is contained in:
Aaro Koivukangas
2026-08-21 17:47:14 +03:00
parent 9202c5d32c
commit 1b8eb0dbf5
40 changed files with 139 additions and 149 deletions
@@ -4,7 +4,7 @@ import { describe, it, beforeAll, assert } from 'vitest'
import { Unarray } from '@oodikone/shared/types'
import { yearToYearCode } from '@oodikone/shared/util'
import { CourseYearlyStatsResBody } from '../../../src/routes/courses'
import { CourseYearlyStatsResBody } from '@/routes/courses'
import { initTests, ResponseWithBody } from '../../utils'
void describe('Course yearly statistics (2016-2023)', () => {
@@ -5,7 +5,7 @@ import { describe, it, beforeAll, assert } from 'vitest'
import { CanError } from '@oodikone/shared/routes'
import { PopulationstatisticsResBody } from '@oodikone/shared/routes/populations'
import { dbConnections } from '../../../src/database/connection'
import { dbConnections } from '@/database/connection'
import { initTests, ResponseWithBody } from '../../utils'
const populationUrl = (
@@ -5,7 +5,7 @@ import { describe, it, beforeAll, assert } from 'vitest'
import { initTests } from '../../utils'
import { yearToYearCode } from '@oodikone/shared/util'
import { PopulationstatisticsbycourseResBody } from '@oodikone/shared/routes/populations'
import { CreditModel } from '../../../src/models'
import { CreditModel } from '@/models'
import { EnrollmentState } from '@oodikone/shared/types'
void describe('Population statistics by course', () => {
@@ -2,8 +2,8 @@ import express from 'express'
import request from 'supertest'
import { describe, it, assert } from 'vitest'
import { testTimeMiddleware } from '../../src/middleware/testTime'
import { now } from '../../src/util/clock'
import { testTimeMiddleware } from '@/middleware/testTime'
import { now } from '@/util/clock'
describe('test time middleware', () => {
it('uses the date from the x-test-now header', async () => {
@@ -1,11 +1,8 @@
import { describe, it, assert, beforeAll, vi } from 'vitest'
import { Unification } from '@oodikone/shared/types'
import { initializeDatabaseConnection } from '../../../src/database/connection'
import {
maxYearsToCreatePopulationFrom,
searchAndCombineSubstitutionGroupsToCodes,
} from '../../../src/services/courses'
import { findByCourseAndSemesters } from '../../../src/services/students'
import { initializeDatabaseConnection } from '@/database/connection'
import { maxYearsToCreatePopulationFrom, searchAndCombineSubstitutionGroupsToCodes } from '@/services/courses'
import { findByCourseAndSemesters } from '@/services/students'
import { yearToYearCode } from '@oodikone/shared/util'
void describe.concurrent('Search and complete substitution groups to codes', () => {
@@ -2,19 +2,9 @@ import { describe, it, assert, vi, afterEach } from 'vitest'
import { Op } from 'sequelize'
import { CreditTypeCode, Unification } from '@oodikone/shared/types'
import { StudentModel } from '../../../../src/models'
import { getCreditsForCourses } from '../../../../src/services/courses/creditsAndEnrollmentsOfCourse'
const credit = (overrides: Partial<Record<string, unknown>> = {}) => ({
grade: '5',
course_code: 'MAT11002',
credits: 5,
attainment_date: new Date(2020, 0, 1),
student_studentnumber: '111',
studyright_id: 'sr-1',
credittypecode: CreditTypeCode.PASSED,
...overrides,
})
import { createCredit as credit } from '@oodikone/shared/test/utils'
import { StudentModel } from '@/models'
import { getCreditsForCourses } from '@/services/courses/creditsAndEnrollmentsOfCourse'
const mockStudents = (students: Array<{ studentnumber: string; credits: Array<Record<string, unknown>> }>) =>
vi.spyOn(StudentModel, 'findAll').mockResolvedValue(students as any)
@@ -1,5 +1,5 @@
import dayjs from 'dayjs'
import { parseDateRangeFromParams } from '../../../../../src/services/populations/shared'
import { parseDateRangeFromParams } from '@/services/populations/shared'
import { describe, it, assert } from 'vitest'
void describe('parseDateRangeFromParams', () => {
@@ -1,9 +1,5 @@
import { describe, it, assert, vi, beforeAll } from 'vitest'
import {
getPercentage,
getYearsArray,
getYearsObject,
} from '../../../../src/services/studyProgramme/studyProgrammeHelpers'
import { getPercentage, getYearsArray, getYearsObject } from '@/services/studyProgramme/studyProgrammeHelpers'
void describe('Get years object', () => {
it('should return nothing with empty years', () => {
@@ -1,6 +1,6 @@
import { describe, it, assert } from 'vitest'
import { combineStats } from '../../../../../src/services/studyProgramme/studyTrackStats'
import { getPercentage } from '../../../../../src/services/studyProgramme/studyProgrammeHelpers'
import { combineStats } from '@/services/studyProgramme/studyTrackStats'
import { getPercentage } from '@/services/studyProgramme/studyProgrammeHelpers'
const constructStats = (mainStatsByYear = {}, mainStatsByTrack = {}, otherCountriesCount = {}) => {
return {
@@ -1,4 +1,4 @@
import { hasFullAccessToStudentData } from '../../../../src/util'
import { hasFullAccessToStudentData } from '@/util'
import { describe, it, assert } from 'vitest'
void describe('hasFullAccessToStudentData', () => {
+4 -4
View File
@@ -1,10 +1,10 @@
import express from 'express'
import { Response } from 'supertest'
import { baseUrl, silentTesting } from '../src/config'
import { initializeDatabaseConnection, dbConnections } from '../src/database/connection'
import routes from '../src/routes'
import logger from '../src/util/logger'
import { baseUrl, silentTesting } from '@/config'
import { initializeDatabaseConnection, dbConnections } from '@/database/connection'
import routes from '@/routes'
import logger from '@/util/logger'
/** Override Supertest's Response body with our own type */
export type ResponseWithBody<T> = Omit<Response, 'body'> & { body: T }
+1
View File
@@ -9,6 +9,7 @@
"emitDecoratorMetadata": true,
"outDir": "dist",
"paths": {
"@/*": ["./src/*"],
"*": ["./*"]
},
"esModuleInterop": true,
+6
View File
@@ -1,6 +1,12 @@
import path from 'path'
import { defineConfig } from 'vitest/config'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
test: {
reporters: ['tree'],
exclude: ['./dist', 'node_modules'],
@@ -2,7 +2,8 @@ import { assert, describe, it } from 'vitest'
import { calculateExcelData, getHopsCourses } from '@/components/CustomPopulation/courseMatrix'
import { CreditTypeCode } from '@oodikone/shared/types'
import { FormattedStudent, StudentCourse, StudentStudyPlan } from '@oodikone/shared/types/studentData'
import { createCourse as createBaseCourse, createStudent, createStudyPlan } from '@oodikone/shared/test/utils'
const createCourse = (
course_code: string,
@@ -10,57 +11,9 @@ const createCourse = (
passed: boolean,
date: Date,
credittypecode: CreditTypeCode = passed ? CreditTypeCode.PASSED : CreditTypeCode.FAILED
): StudentCourse => ({
course_code,
date,
passed,
grade: passed ? '5' : 'Hyl.',
credits,
isStudyModuleCredit: false,
credittypecode,
language: 'fi',
studyright_id: 'sr-1',
})
) => createBaseCourse({ course_code, date, passed, grade: passed ? '5' : 'Hyl.', credits, credittypecode })
const createHops = (includedCourses: string[]): StudentStudyPlan => ({
included_courses: includedCourses,
programme_code: 'TEST',
includedModules: [],
completed_credits: 0,
curriculum_period_id: 'cp-1',
sis_study_right_id: 'sr-1',
})
const createStudent = (overrides: Partial<FormattedStudent>): FormattedStudent => ({
firstnames: 'Testi',
lastname: 'Opiskelija',
started: new Date('2024-08-01'),
studentNumber: '123456789',
credits: 0,
hopsCredits: 0,
name: 'Testi Opiskelija',
gender_code: '1',
email: 'testi@example.com',
secondaryEmail: '',
phoneNumber: '',
updatedAt: new Date(),
tags: [],
studyrightStart: '2024-08-01',
option: null,
birthdate: new Date('2000-01-01'),
sis_person_id: '1',
citizenships: [],
criteriaProgress: {},
curriculumVersion: null,
hasPersonalIdentityCode: false,
transferredStudyright: false,
transferSource: undefined,
studyRights: [],
studyplans: [],
courses: [],
enrollments: [],
...overrides,
})
const createHops = (includedCourses: string[]) => createStudyPlan({ included_courses: includedCourses })
void describe('getHopsCourses', () => {
void it('returns only passed courses included in the student HOPS', () => {
@@ -1,8 +1,8 @@
import { getProgressCriteria } from '@/redux/populations/criteriaProgress'
import { CreditTypeCode, ProgressCriteria } from '@oodikone/shared/types'
import { StudentStudyPlan } from '@oodikone/shared/types/studentData'
import { PopulationCourseStatsCredit } from '@/redux/populations/util'
import { createCredit as createBaseCredit, createStudyPlan } from '@oodikone/shared/test/utils'
import { describe, it, assert } from 'vitest'
const createCriteria = (overrides: Partial<ProgressCriteria> = {}): ProgressCriteria => ({
@@ -29,30 +29,15 @@ const createCriteria = (overrides: Partial<ProgressCriteria> = {}): ProgressCrit
...overrides,
})
const createHops = (includedCourses: string[]): StudentStudyPlan => ({
included_courses: includedCourses,
programme_code: 'TEST',
includedModules: [],
completed_credits: 0,
curriculum_period_id: 'cp-1',
sis_study_right_id: 'sr-1',
})
const createHops = (includedCourses: string[]) => createStudyPlan({ included_courses: includedCourses })
const createCredit = (
course_code: string,
credits: number,
credittypecode: CreditTypeCode,
date: Date
): PopulationCourseStatsCredit => ({
grade: '',
credits,
credittypecode,
attainment_date: date,
isStudyModule: false,
course_code,
language: 'fi',
studyright_id: 'sr-1',
})
): PopulationCourseStatsCredit =>
createBaseCredit({ grade: '', credits, credittypecode, attainment_date: date, course_code })
const STUDY_RIGHT_START = new Date('2024-08-01')
@@ -2,7 +2,7 @@ import { assert, describe, it } from 'vitest'
import { admissionTypeFilter, filter } from '@/components/FilterView/filters/admissionType'
import { createStudent, createStudyRight, createStudyRightElement } from './helpers'
import { createStudent, createStudyRight, createStudyRightElement } from '@oodikone/shared/test/utils'
const ARGS = { programme: 'KH50_001' }
@@ -4,7 +4,7 @@ import { ageFilter } from '@/components/FilterView/filters/age'
import { getAge } from '@/util/timeAndDate'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, options: { min: number | null; max: number | null }) =>
ageFilter().filter(student, { args: undefined, options, precomputed: { min: null, max: null } })
@@ -2,7 +2,7 @@ import { assert, describe, it } from 'vitest'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const { citizenshipFilter } = await import('@/components/FilterView/filters/citizenship')
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { FilterType } from '@/components/FilterView/filters/courses/filterType'
import { CreditTypeCode } from '@oodikone/shared/types'
import { createCourse, createStudent } from './helpers'
import { createCourse, createStudent } from '@oodikone/shared/test/utils'
const { courseFilter } = await import('@/components/FilterView/filters/courses')
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { creditsEarnedFilter } from '@/components/FilterView/filters/creditsEarned'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createCourse, createStudent } from './helpers'
import { createCourse, createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, options: { min: number | null; max: number | null }) =>
creditsEarnedFilter().filter(student, { args: undefined, options, precomputed: { min: undefined, max: undefined } })
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { curriculumPeriodFilter } from '@/components/FilterView/filters/curriculumPeriod'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, selected: string) =>
curriculumPeriodFilter().filter(student, { args: undefined, options: { selected }, precomputed: null })
@@ -1,22 +1,10 @@
import dayjs from 'dayjs'
import { assert, describe, it } from 'vitest'
import { EnrollmentState } from '@oodikone/shared/types'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createCourse, createStudent } from './helpers'
import { createCourse, createEnrollment, createStudent } from '@oodikone/shared/test/utils'
const { creditDateFilter } = await import('@/components/FilterView/filters/date')
const createEnrollment = (overrides: Partial<FormattedStudent['enrollments'][number]> = {}) => ({
course_code: 'TKT002',
state: EnrollmentState.ENROLLED,
enrollment_date_time: new Date('2024-09-01'),
semestercode: 100,
studyright_id: 'sr-1',
...overrides,
})
void describe('creditDateFilter', () => {
void it('should not filter students out directly', () => {
const student = createStudent()
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { DegreeProgrammeType, EnrollmentType, Phase } from '@oodikone/shared/types'
import { FormattedStudent, StudentStudyRight } from '@oodikone/shared/types/studentData'
import { createStudent, createStudyRight as createBaseStudyRight } from './helpers'
import { createStudent, createStudyRight as createBaseStudyRight } from '@oodikone/shared/test/utils'
const { enrollmentStatusFilter } = await import('@/components/FilterView/filters/enrollmentStatus')
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { genderFilter } from '@/components/FilterView/filters/gender'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
import { GenderCode } from '@oodikone/shared/types'
const filterStudent = (student: FormattedStudent, selected: string) =>
@@ -2,7 +2,7 @@ import { assert, describe, it } from 'vitest'
import { gradeFilter } from '@/components/FilterView/filters/grade'
import { createCourse, createStudent } from './helpers'
import { createCourse, createStudent } from '@oodikone/shared/test/utils'
const ARGS = { courseCodes: ['TKT002'], from: '2024-01-01', to: '2024-12-31' }
@@ -7,7 +7,11 @@ import {
} from '@/components/FilterView/filters/graduatedFromProgramme'
import { FormattedStudent, StudentStudyRight, StudentStudyRightElement } from '@oodikone/shared/types/studentData'
import { createStudent, createStudyRight as createBaseStudyRight, createStudyRightElement } from './helpers'
import {
createStudent,
createStudyRight as createBaseStudyRight,
createStudyRightElement,
} from '@oodikone/shared/test/utils'
const createElement = (overrides: Partial<StudentStudyRightElement> = {}): StudentStudyRightElement =>
createStudyRightElement({ code: 'KH50_001', endDate: new Date('2024-08-01'), ...overrides })
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { hetuFilter } from '@/components/FilterView/filters/hetu'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, mode: number) =>
hetuFilter().filter(student, { args: undefined, options: { mode }, precomputed: undefined })
@@ -4,7 +4,7 @@ import { hopsFilter } from '@/components/FilterView/filters/hops'
import { DegreeProgrammeType, Phase } from '@oodikone/shared/types'
import { FormattedStudent, StudentStudyPlan } from '@oodikone/shared/types/studentData'
import { createCourse, createStudent, createStudyRight } from './helpers'
import { createCourse, createStudent, createStudyRight } from '@oodikone/shared/test/utils'
const createHops = (programmeCode: string, sisStudyRightId: string, includedCourses: string[]): StudentStudyPlan => ({
included_courses: includedCourses,
@@ -1,6 +1,6 @@
import { assert, describe, it } from 'vitest'
import { createStudent, createStudyRight, createStudyRightElement } from './helpers'
import { createStudent, createStudyRight, createStudyRightElement } from '@oodikone/shared/test/utils'
const { programmeFilter } = await import('@/components/FilterView/filters/programmes')
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { startYearAtUniFilter } from '@/components/FilterView/filters/startYearAtUni'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, selected: number | string) =>
startYearAtUniFilter().filter(student, { args: undefined, options: { selected }, precomputed: undefined })
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { studentNumberFilter } from '@/components/FilterView/filters/studentNumber'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, options: { allowlist: string[]; blocklist: string[] }) =>
studentNumberFilter().filter(student, { args: undefined, options, precomputed: undefined })
@@ -4,7 +4,7 @@ import { studyRightStatusFilter } from '@/components/FilterView/filters/studyRig
import { DegreeProgrammeType, EnrollmentType, Phase } from '@oodikone/shared/types'
import { StudentStudyRight } from '@oodikone/shared/types/studentData'
import { createStudent, createStudyRight as createBaseStudyRight } from './helpers'
import { createStudent, createStudyRight as createBaseStudyRight } from '@oodikone/shared/test/utils'
const createStudyRight = (overrides: Partial<StudentStudyRight> = {}): StudentStudyRight =>
createBaseStudyRight({ semesterEnrollments: [{ type: EnrollmentType.PRESENT, semester: 1 }], ...overrides })
@@ -4,7 +4,11 @@ import { studyRightTypeFilter } from '@/components/FilterView/filters/studyRight
import { ExtentCode } from '@oodikone/shared/types'
import { StudentStudyRight } from '@oodikone/shared/types/studentData'
import { createStudent, createStudyRight as createBaseStudyRight, createStudyRightElement } from './helpers'
import {
createStudent,
createStudyRight as createBaseStudyRight,
createStudyRightElement,
} from '@oodikone/shared/test/utils'
const createStudyRight = (overrides: Partial<StudentStudyRight> = {}): StudentStudyRight =>
createBaseStudyRight({ studyRightElements: [createStudyRightElement({ code: 'KH50' })], ...overrides })
@@ -2,7 +2,11 @@ import { assert, describe, it } from 'vitest'
import { StudentStudyRight } from '@oodikone/shared/types/studentData'
import { createStudent, createStudyRight as createBaseStudyRight, createStudyRightElement } from './helpers'
import {
createStudent,
createStudyRight as createBaseStudyRight,
createStudyRightElement,
} from '@oodikone/shared/test/utils'
const { studyTrackFilter } = await import('@/components/FilterView/filters/studyTrack')
@@ -2,15 +2,15 @@ import { assert, describe, it } from 'vitest'
import { tagsFilter } from '@/components/FilterView/filters/tags'
import type { Tag } from '@oodikone/shared/models/kone'
import { FormattedStudent, StudentTags } from '@oodikone/shared/types/studentData'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent, createTag as createBaseTag } from '@oodikone/shared/test/utils'
const createTag = (tagId: string): StudentTags => ({
tag_id: tagId,
studentnumber: '123456789',
tag: { tag_id: tagId, tagname: `Tag ${tagId}`, personal_user_id: null } as unknown as Tag,
})
const createTag = (tagId: string) =>
createBaseTag({
tag_id: tagId,
tag: { tag_id: tagId, tagname: `Tag ${tagId}`, personal_user_id: null } as unknown as Tag,
})
const filterStudent = (student: FormattedStudent, includedTags: string[], excludedTags: string[]) =>
tagsFilter().filter(student, { args: undefined, options: { includedTags, excludedTags }, precomputed: undefined })
@@ -3,7 +3,7 @@ import { assert, describe, it } from 'vitest'
import { transferredToProgrammeFilter } from '@/components/FilterView/filters/transferredToProgramme'
import { FormattedStudent } from '@oodikone/shared/types/studentData'
import { createStudent } from './helpers'
import { createStudent } from '@oodikone/shared/test/utils'
const filterStudent = (student: FormattedStudent, transferred: string) =>
transferredToProgrammeFilter().filter(student, { args: undefined, options: { transferred }, precomputed: undefined })
@@ -1,10 +1,13 @@
import { CreditTypeCode, DegreeProgrammeType, ExtentCode, Phase } from '@oodikone/shared/types'
import type { Tag } from '../../models/kone'
import { CreditTypeCode, DegreeProgrammeType, EnrollmentState, ExtentCode, Phase } from '../../types'
import {
FormattedStudent,
StudentCourse,
StudentStudyRight,
StudentStudyRightElement,
} from '@oodikone/shared/types/studentData'
StudentStudyPlan,
StudentTags,
} from '../../types/studentData'
export const createStudent = (overrides: Partial<FormattedStudent> = {}): FormattedStudent => ({
firstnames: 'Testi',
@@ -76,3 +79,50 @@ export const createCourse = (overrides: Partial<StudentCourse> = {}): StudentCou
studyright_id: 'sr-1',
...overrides,
})
export const createStudyPlan = (overrides: Partial<StudentStudyPlan> = {}): StudentStudyPlan => ({
included_courses: [],
programme_code: 'TEST',
includedModules: [],
completed_credits: 0,
curriculum_period_id: 'cp-1',
sis_study_right_id: 'sr-1',
...overrides,
})
export const createEnrollment = (
overrides: Partial<FormattedStudent['enrollments'][number]> = {}
): FormattedStudent['enrollments'][number] => ({
course_code: 'MAT001',
state: EnrollmentState.ENROLLED,
enrollment_date_time: new Date('2024-09-01'),
semestercode: 149,
studyright_id: 'sr-1',
...overrides,
})
export const createTag = (overrides: Partial<StudentTags> = {}): StudentTags => ({
tag_id: '1',
studentnumber: '123456789',
tag: { tag_id: '1', tagname: 'Tag 1', personal_user_id: null } as unknown as Tag,
...overrides,
})
export const createCredit = (overrides: Partial<Record<string, unknown>> = {}) => ({
grade: '5',
course_code: 'MAT11002',
credits: 5,
attainment_date: new Date(2020, 0, 1),
student_studentnumber: '111',
studyright_id: 'sr-1',
credittypecode: CreditTypeCode.PASSED,
isStudyModule: false,
language: 'fi',
semester: {
yearcode: 2020,
yearname: '2019-2020',
semestercode: 140,
name: { en: 'Spring 2020', fi: 'Kevät 2020', sv: 'Våren 2020' },
},
...overrides,
})
+2
View File
@@ -0,0 +1,2 @@
export * from './formatted'
export * from './models'
+9
View File
@@ -0,0 +1,9 @@
export const createStudyRightElementModel = (overrides: Partial<Record<string, unknown>> = {}) => ({
studyRightId: 'sr-1',
code: 'KH50_005',
name: { en: 'Bachelor', fi: 'Kandi', sv: 'Kandi sv' },
startDate: new Date(2019, 0, 1),
endDate: new Date(2025, 0, 1),
studyRight: { facultyCode: 'H50', organization: { name: { en: 'Faculty', fi: 'Tiedekunta', sv: 'Fakultet' } } },
...overrides,
})
+2 -1
View File
@@ -24,5 +24,6 @@
"sourceRoot": "/",
"inlineSources": true,
"resolveJsonModule": true
}
},
"exclude": ["tests", "test", "dist"]
}