mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
[Tests][Backend] Fix remaining tests and use better courseCodeToGroupId format
This commit is contained in:
committed by
Aaro Koivukangas
parent
793bf317e8
commit
7b772e188a
+4
-6
@@ -5,9 +5,7 @@ import { yearToYearCode } from '@oodikone/shared/util'
|
||||
import { initTests } from '../../../../utils'
|
||||
import { getCourseYearlyStats, CourseYearlyStats } from '../helpers'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
MAT21003: 'hy-CU-117375829',
|
||||
}
|
||||
const MAT21003 = 'hy-CU-117375829'
|
||||
|
||||
void describe('Course yearly attempt statistics - MAT21003 (hy-CU-117375394, no substitutions)', () => {
|
||||
let app: Express
|
||||
@@ -18,7 +16,7 @@ void describe('Course yearly attempt statistics - MAT21003 (hy-CU-117375394, no
|
||||
describe('duplicate failed attempts across a large timespan are counted separately', () => {
|
||||
let body: CourseYearlyStats
|
||||
beforeAll(async () => {
|
||||
body = await getCourseYearlyStats(app, `courses=${courseCodeToGroupId.MAT21003}&substitutions=false`)
|
||||
body = await getCourseYearlyStats(app, `courses=${MAT21003}&substitutions=false`)
|
||||
})
|
||||
|
||||
it('2017-2018 should include a single failed attempt (539036)', () => {
|
||||
@@ -65,7 +63,7 @@ void describe('Course yearly attempt statistics - MAT21003 (hy-CU-117375394, no
|
||||
it('2017-2018 should include a failed attempt (539036)', async () => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2017)}&toYearCode=${yearToYearCode(2017)}`
|
||||
`courses=${MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2017)}&toYearCode=${yearToYearCode(2017)}`
|
||||
)
|
||||
const year = body.unifyStats.statistics.find(year => year.name === '2017-2018')
|
||||
assert(year && 'enrollments' in year, 'Missing stats for 2017-2018')
|
||||
@@ -79,7 +77,7 @@ void describe('Course yearly attempt statistics - MAT21003 (hy-CU-117375394, no
|
||||
it('2018-2019 should include a failed attempt (539036)', async () => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2018)}&toYearCode=${yearToYearCode(2018)}`
|
||||
`courses=${MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2018)}&toYearCode=${yearToYearCode(2018)}`
|
||||
)
|
||||
const year = body.unifyStats.statistics.find(year => year.name === '2018-2019')
|
||||
assert(year && 'enrollments' in year, 'Missing stats for 2018-2019')
|
||||
|
||||
+2
-4
@@ -4,16 +4,14 @@ import { describe, it, beforeAll, assert } from 'vitest'
|
||||
import { initTests } from '../../../../utils'
|
||||
import { calculatePassedAndFailed, getCourseYearlyStats, CourseYearlyStats } from '../helpers'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
MAT11002: 'hy-CU-117375394',
|
||||
}
|
||||
const MAT11002 = 'hy-CU-117375394'
|
||||
|
||||
void describe('Course yearly statistics - MAT11002 (hy-CU-117375394, no substitutions)', () => {
|
||||
let app: Express
|
||||
let body: CourseYearlyStats
|
||||
beforeAll(async () => {
|
||||
app = await initTests()
|
||||
body = await getCourseYearlyStats(app, `courses=${courseCodeToGroupId.MAT11002}&combineSubstitutions=false`)
|
||||
body = await getCourseYearlyStats(app, `courses=${MAT11002}&combineSubstitutions=false`)
|
||||
})
|
||||
|
||||
it('should not include students for AY code', () => {
|
||||
|
||||
+5
-7
@@ -5,9 +5,7 @@ import { yearToYearCode } from '@oodikone/shared/util'
|
||||
import { initTests } from '../../../../utils'
|
||||
import { calculatePassedAndFailed, getCourseYearlyStats, CourseYearlyStats } from '../helpers'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
MAT21003: 'hy-CU-117375829',
|
||||
}
|
||||
const MAT21003 = 'hy-CU-117375829'
|
||||
|
||||
void describe('Course yearly statistics - MAT21003 (no substitutions)', () => {
|
||||
let app: Express
|
||||
@@ -18,7 +16,7 @@ void describe('Course yearly statistics - MAT21003 (no substitutions)', () => {
|
||||
describe('duplicate failed grades across a large timespan are not double-counted', () => {
|
||||
let body: CourseYearlyStats
|
||||
beforeAll(async () => {
|
||||
body = await getCourseYearlyStats(app, `courses=${courseCodeToGroupId.MAT21003}&substitutions=false`)
|
||||
body = await getCourseYearlyStats(app, `courses=${MAT21003}&substitutions=false`)
|
||||
})
|
||||
|
||||
it('2017-2018 should not include a failed grade', () => {
|
||||
@@ -46,7 +44,7 @@ void describe('Course yearly statistics - MAT21003 (no substitutions)', () => {
|
||||
])('should include correct years for %s', async (name, from, to) => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(from)}&toYearCode=${yearToYearCode(to)}`
|
||||
`courses=${MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(from)}&toYearCode=${yearToYearCode(to)}`
|
||||
)
|
||||
|
||||
const years: string[] = []
|
||||
@@ -65,7 +63,7 @@ void describe('Course yearly statistics - MAT21003 (no substitutions)', () => {
|
||||
it('2017-2018 should include a failed grade', async () => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2017)}&toYearCode=${yearToYearCode(2017)}`
|
||||
`courses=${MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2017)}&toYearCode=${yearToYearCode(2017)}`
|
||||
)
|
||||
const year = body.unifyStats.statistics.find(year => year.name === '2017-2018')
|
||||
assert(year && 'enrollments' in year, 'Missing stats for 2017-2018')
|
||||
@@ -78,7 +76,7 @@ void describe('Course yearly statistics - MAT21003 (no substitutions)', () => {
|
||||
it('2018-2019 should include a failed grade', async () => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2018)}&toYearCode=${yearToYearCode(2018)}`
|
||||
`courses=${MAT21003}&substitutions=false&fromYearCode=${yearToYearCode(2018)}&toYearCode=${yearToYearCode(2018)}`
|
||||
)
|
||||
const year = body.unifyStats.statistics.find(year => year.name === '2018-2019')
|
||||
assert(year && 'enrollments' in year, 'Missing stats for 2018-2019')
|
||||
|
||||
+3
-5
@@ -5,9 +5,7 @@ import { yearToYearCode } from '@oodikone/shared/util'
|
||||
import { initTests } from '../../../../utils'
|
||||
import { calculatePassedAndFailed, getCourseYearlyStats, CourseYearlyStats } from '../helpers'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
TKT10002: 'hy-CU-118023867',
|
||||
}
|
||||
const TKT10002 = 'hy-CU-118023867'
|
||||
|
||||
void describe('Course yearly statistics - TKT10002 (no substitutions)', () => {
|
||||
let app: Express
|
||||
@@ -20,7 +18,7 @@ void describe('Course yearly statistics - TKT10002 (no substitutions)', () => {
|
||||
beforeAll(async () => {
|
||||
body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.TKT10002}&substitutions=false&fromYearCode=${yearToYearCode(2016)}&toYearCode=${yearToYearCode(2023)}`
|
||||
`courses=${TKT10002}&substitutions=false&fromYearCode=${yearToYearCode(2016)}&toYearCode=${yearToYearCode(2023)}`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -236,7 +234,7 @@ void describe('Course yearly statistics - TKT10002 (no substitutions)', () => {
|
||||
])('should include correct stats for one academic year ($0)', async (year, categories, grades) => {
|
||||
const body = await getCourseYearlyStats(
|
||||
app,
|
||||
`courses=${courseCodeToGroupId.TKT10002}&substitutions=false&fromYearCode=${yearToYearCode(year.split('-').at(0))}&toYearCode=${yearToYearCode(parseInt(year.split('-').at(1)!)) - 1}`
|
||||
`courses=${TKT10002}&substitutions=false&fromYearCode=${yearToYearCode(year.split('-').at(0))}&toYearCode=${yearToYearCode(parseInt(year.split('-').at(1)!)) - 1}`
|
||||
)
|
||||
const stats = body.unifyStats.statistics.find(yearStats => yearStats.name === year)!
|
||||
assert('enrollments' in stats, 'Missing field enrollment in statsitics')
|
||||
|
||||
@@ -8,10 +8,8 @@ import { PopulationstatisticsbycourseResBody } from '@oodikone/shared/routes/pop
|
||||
import { CreditModel } from '@/models'
|
||||
import { EnrollmentState } from '@oodikone/shared/types'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
MAT21003: 'hy-CU-117375829',
|
||||
MAT21005: 'hy-CU-117376344',
|
||||
}
|
||||
const MAT21003 = 'hy-CU-117375829'
|
||||
const MAT21005 = 'hy-CU-117376344'
|
||||
|
||||
void describe('Population statistics by course MAT21003 + MAT21005 (hy-CU-117375829 + hy-CU-117376344)', () => {
|
||||
let app: Express
|
||||
@@ -45,9 +43,7 @@ void describe('Population statistics by course MAT21003 + MAT21005 (hy-CU-117375
|
||||
[2023, 21, 1, 0, 36],
|
||||
])('should return correct amount of students for single year ($0)', async (year, total, passed, failed, enrolled) => {
|
||||
const res = await request(app)
|
||||
.get(
|
||||
`/populationstatisticsbycourse?courses=${courseCodeToGroupId.MAT21003}&from=${yearToYearCode(year)}&to=${yearToYearCode(year)}`
|
||||
)
|
||||
.get(`/populationstatisticsbycourse?courses=${MAT21003}&from=${yearToYearCode(year)}&to=${yearToYearCode(year)}`)
|
||||
.set('shib-session-id', 'test')
|
||||
.set('uid', 'basic')
|
||||
.set('hygroupcn', 'grp-oodikone-basic-users')
|
||||
@@ -60,14 +56,14 @@ void describe('Population statistics by course MAT21003 + MAT21005 (hy-CU-117375
|
||||
assert.strictEqual(body.students.length, total, 'Incorrect amount of students')
|
||||
assert.strictEqual(
|
||||
body.coursestatistics.credits.filter(
|
||||
credit => body.idToGroupIdMap[credit.course_id] === courseCodeToGroupId.MAT21003 && CreditModel.passed(credit)
|
||||
credit => body.idToGroupIdMap[credit.course_id] === MAT21003 && CreditModel.passed(credit)
|
||||
).length,
|
||||
passed,
|
||||
'Incorrect amount of passed credits'
|
||||
)
|
||||
assert.strictEqual(
|
||||
body.coursestatistics.credits.filter(
|
||||
credit => body.idToGroupIdMap[credit.course_id] === courseCodeToGroupId.MAT21003 && CreditModel.failed(credit)
|
||||
credit => body.idToGroupIdMap[credit.course_id] === MAT21003 && CreditModel.failed(credit)
|
||||
).length,
|
||||
failed,
|
||||
'Incorrect amount of failed credits'
|
||||
@@ -75,8 +71,7 @@ void describe('Population statistics by course MAT21003 + MAT21005 (hy-CU-117375
|
||||
assert.strictEqual(
|
||||
body.coursestatistics.enrollments.filter(
|
||||
enrollment =>
|
||||
body.idToGroupIdMap[enrollment.course_id] === courseCodeToGroupId.MAT21003 &&
|
||||
enrollment.state === EnrollmentState.ENROLLED
|
||||
body.idToGroupIdMap[enrollment.course_id] === MAT21003 && enrollment.state === EnrollmentState.ENROLLED
|
||||
).length,
|
||||
enrolled,
|
||||
'Incorrect amount of enrollments'
|
||||
@@ -85,7 +80,7 @@ void describe('Population statistics by course MAT21003 + MAT21005 (hy-CU-117375
|
||||
|
||||
it('should return correct amount of students for a course', async () => {
|
||||
const res = await request(app)
|
||||
.get(`/populationstatisticsbycourse?courses=${courseCodeToGroupId.MAT21005}&from=70&to=70`)
|
||||
.get(`/populationstatisticsbycourse?courses=${MAT21005}&from=70&to=70`)
|
||||
.set('shib-session-id', 'test')
|
||||
.set('uid', 'basic')
|
||||
.set('hygroupcn', 'grp-oodikone-basic-users')
|
||||
|
||||
@@ -5,11 +5,8 @@ import { yearToYearCode } from '@oodikone/shared/util'
|
||||
import { Unification } from '@oodikone/shared/types'
|
||||
import { getRelevantCourseIdMap } from '@/services/courses'
|
||||
|
||||
const courseCodeToGroupId = {
|
||||
MAT11001: 'hy-CU-117375151',
|
||||
CSM14204: 'hy-CU-119366777',
|
||||
MAT21018: 'hy-CU-117377764',
|
||||
}
|
||||
const CSM14204 = 'hy-CU-119366777'
|
||||
const MAT21018 = 'hy-CU-117377764'
|
||||
|
||||
void describe.concurrent('Search and complete substitution groups to codes', () => {
|
||||
beforeAll(async () => {
|
||||
@@ -29,7 +26,7 @@ void describe.concurrent('Search and complete substitution groups to codes', ()
|
||||
] as TestList)(
|
||||
'should return correct student numbers for a short course (CSM14204, 2019-2023) by $0',
|
||||
async (separate, to, from) => {
|
||||
const courseIds = await getRelevantCourseIdMap([courseCodeToGroupId.CSM14204], false)
|
||||
const courseIds = await getRelevantCourseIdMap([CSM14204], false)
|
||||
const res = await findByCourseAndSemesters(
|
||||
Object.keys(courseIds),
|
||||
to,
|
||||
@@ -46,7 +43,7 @@ void describe.concurrent('Search and complete substitution groups to codes', ()
|
||||
['semester', semesterToSemesterCode(2020, 'fall'), semesterToSemesterCode(2023, 'spring')],
|
||||
['year', yearToYearCode(2020), yearToYearCode(2023)],
|
||||
] as TestList)('should filter out students outside of 2020-2023 by $0', async (separate, to, from) => {
|
||||
const courseIds = await getRelevantCourseIdMap([courseCodeToGroupId.CSM14204], false)
|
||||
const courseIds = await getRelevantCourseIdMap([CSM14204], false)
|
||||
const res = await findByCourseAndSemesters(
|
||||
Object.keys(courseIds),
|
||||
to,
|
||||
@@ -63,7 +60,7 @@ void describe.concurrent('Search and complete substitution groups to codes', ()
|
||||
] as TestList)(
|
||||
'should return correct student numbers for a longer course (MAT21018, 2017-2021) by $0',
|
||||
async (separate, to, from) => {
|
||||
const courseIds = await getRelevantCourseIdMap([courseCodeToGroupId.MAT21018], false)
|
||||
const courseIds = await getRelevantCourseIdMap([MAT21018], false)
|
||||
const res = await findByCourseAndSemesters(
|
||||
Object.keys(courseIds),
|
||||
to,
|
||||
@@ -94,7 +91,7 @@ void describe.concurrent('Search and complete substitution groups to codes', ()
|
||||
] as TestList)(
|
||||
'should return correct student numbers for a longer course (MAT21018, 2021-2023) by $0',
|
||||
async (separate, to, from) => {
|
||||
const courseIds = await getRelevantCourseIdMap([courseCodeToGroupId.MAT21018], false)
|
||||
const courseIds = await getRelevantCourseIdMap([MAT21018], false)
|
||||
const res = await findByCourseAndSemesters(
|
||||
Object.keys(courseIds),
|
||||
to,
|
||||
@@ -133,7 +130,7 @@ void describe.concurrent('Search and complete substitution groups to codes', ()
|
||||
|
||||
describe('should work with specific cases', () => {
|
||||
it('Student with enrollment in -21 and passed grade in -23 should be included in both stats when querying one year at a time (522321)', async () => {
|
||||
const courseIds = await getRelevantCourseIdMap([courseCodeToGroupId.MAT21018], false)
|
||||
const courseIds = await getRelevantCourseIdMap([MAT21018], false)
|
||||
const res21 = await findByCourseAndSemesters(
|
||||
Object.keys(courseIds),
|
||||
yearToYearCode(2021),
|
||||
|
||||
@@ -2,125 +2,153 @@ import { describe, it, assert, vi, afterEach } from 'vitest'
|
||||
import { Op } from 'sequelize'
|
||||
|
||||
import { CreditTypeCode, Unification } from '@oodikone/shared/types'
|
||||
import { Credit } from '@oodikone/shared/models'
|
||||
import { createCredit as credit } from '@oodikone/shared/test/utils'
|
||||
import { StudentModel } from '@/models'
|
||||
import { CreditModel } 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)
|
||||
const MAT11002 = 'hy-CU-117375394'
|
||||
const MAT21001 = 'hy-CU-117375754'
|
||||
const MAT21002 = 'hy-CU-117375793'
|
||||
const OTHER101 = 'hy-CU-000000000'
|
||||
|
||||
const mockCredits = (credits: Array<Record<string, unknown>>) =>
|
||||
vi.spyOn(CreditModel, 'findAll').mockResolvedValue(credits as any)
|
||||
|
||||
const from = new Date(2000, 0, 1)
|
||||
const to = new Date(2030, 0, 1)
|
||||
|
||||
// Tests share a spy on the same StudentModel.findAll, so they must not run concurrently
|
||||
// Tests share a spy on the same CreditModel.findAll, so they must not run concurrently
|
||||
// (the suite config default is sequence.concurrent: true).
|
||||
void describe('Get credits for courses (unit, mocked db)', { concurrent: false }, () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('should return no groups without any course codes, without querying credits by course', async () => {
|
||||
const findAll = mockStudents([{ studentnumber: '111', credits: [credit()] }])
|
||||
it('should return no groups when there are no groupId groups to match', async () => {
|
||||
const findAll = mockCredits([credit()])
|
||||
|
||||
const res = await getCreditsForCourses([], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses([], [], Unification.UNIFY, from, to)
|
||||
|
||||
assert.deepStrictEqual(res, [])
|
||||
assert.strictEqual(findAll.mock.calls.length, 1)
|
||||
})
|
||||
|
||||
it('should query StudentModel with course codes, date range and openness filters', async () => {
|
||||
const findAll = mockStudents([])
|
||||
it('should query CreditModel with course ids, date range and openness filters', async () => {
|
||||
const findAll = mockCredits([])
|
||||
|
||||
await getCreditsForCourses([['MAT11002'], ['MAT21001', 'MAT21002']], Unification.OPEN, from, to)
|
||||
await getCreditsForCourses([[MAT11002], [MAT21001, MAT21002]], ['course-1', 'course-2'], Unification.OPEN, from, to)
|
||||
|
||||
const call = findAll.mock.calls[0][0] as any
|
||||
const creditInclude = call.include[0]
|
||||
|
||||
assert.deepStrictEqual(creditInclude.where.course_code, { [Op.in]: ['MAT11002', 'MAT21001', 'MAT21002'] })
|
||||
assert.deepStrictEqual(creditInclude.where.attainment_date, { [Op.between]: [from, to] })
|
||||
assert.deepStrictEqual(creditInclude.where.is_open, { [Op.eq]: true })
|
||||
assert.deepStrictEqual(call.where.course_id, { [Op.in]: ['course-1', 'course-2'] })
|
||||
assert.deepStrictEqual(call.where.attainment_date, { [Op.between]: [from, to] })
|
||||
assert.deepStrictEqual(call.where.is_open, { [Op.eq]: true })
|
||||
})
|
||||
|
||||
it('should include both passed and failed credits for a single-code group as one group', async () => {
|
||||
mockStudents([
|
||||
{
|
||||
studentnumber: '111',
|
||||
credits: [credit({ credittypecode: CreditTypeCode.FAILED }), credit({ credittypecode: CreditTypeCode.PASSED })],
|
||||
},
|
||||
])
|
||||
mockCredits([credit({ credittypecode: CreditTypeCode.FAILED }), credit({ credittypecode: CreditTypeCode.PASSED })])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses([[MAT11002]], ['course-1'], Unification.UNIFY, from, to)
|
||||
|
||||
assert.strictEqual(res.length, 1)
|
||||
assert.strictEqual(res.flat().length, 2)
|
||||
})
|
||||
|
||||
it('should create a separate group per student for a single-code group', async () => {
|
||||
mockStudents([
|
||||
{ studentnumber: '111', credits: [credit({ student_studentnumber: '111' })] },
|
||||
{ studentnumber: '222', credits: [credit({ student_studentnumber: '222' })] },
|
||||
])
|
||||
mockCredits([credit({ student_studentnumber: '111' }), credit({ student_studentnumber: '222' })])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses([[MAT11002]], ['course-1'], Unification.UNIFY, from, to)
|
||||
|
||||
assert.strictEqual(res.length, 2)
|
||||
assert.deepStrictEqual(res.map(group => group.map(c => c.student_studentnumber)).sort(), [['111'], ['222']])
|
||||
})
|
||||
|
||||
it('should not create a group for a single course code the student has no credit for', async () => {
|
||||
mockStudents([{ studentnumber: '111', credits: [credit({ course_code: 'OTHER101' })] }])
|
||||
mockCredits([credit({ course_code: 'OTHER101', course: { groupId: OTHER101 } as Credit['course'] })])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses([[MAT11002]], ['course-1'], Unification.UNIFY, from, to)
|
||||
|
||||
assert.deepStrictEqual(res, [])
|
||||
})
|
||||
|
||||
it('should only include the passed credits of a substitution group when every code was passed', async () => {
|
||||
mockStudents([
|
||||
{
|
||||
studentnumber: '111',
|
||||
credits: [
|
||||
credit({ course_code: 'MAT11002', credittypecode: CreditTypeCode.PASSED }),
|
||||
credit({ course_code: 'MAT21001', credittypecode: CreditTypeCode.APPROVED }),
|
||||
],
|
||||
},
|
||||
mockCredits([
|
||||
credit({
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: MAT11002 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
credit({
|
||||
course_code: 'MAT21001',
|
||||
course: { groupId: MAT21001 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.APPROVED,
|
||||
}),
|
||||
])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002', 'MAT21001']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses(
|
||||
[[MAT11002, MAT21001]],
|
||||
['course-1', 'course-2'],
|
||||
Unification.UNIFY,
|
||||
from,
|
||||
to
|
||||
)
|
||||
|
||||
assert.strictEqual(res.length, 1)
|
||||
assert.deepStrictEqual(res[0].map(c => c.course_code).sort(), ['MAT11002', 'MAT21001'])
|
||||
})
|
||||
|
||||
it('should exclude a partially completed substitution group entirely', async () => {
|
||||
mockStudents([
|
||||
{
|
||||
studentnumber: '111',
|
||||
credits: [
|
||||
credit({ course_code: 'MAT11002', credittypecode: CreditTypeCode.PASSED }),
|
||||
credit({ course_code: 'MAT21001', credittypecode: CreditTypeCode.FAILED }),
|
||||
],
|
||||
},
|
||||
mockCredits([
|
||||
credit({
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: MAT11002 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
credit({
|
||||
course_code: 'MAT21001',
|
||||
course: { groupId: MAT21001 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.FAILED,
|
||||
}),
|
||||
])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002', 'MAT21001']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses(
|
||||
[[MAT11002, MAT21001]],
|
||||
['course-1', 'course-2'],
|
||||
Unification.UNIFY,
|
||||
from,
|
||||
to
|
||||
)
|
||||
|
||||
assert.deepStrictEqual(res, [])
|
||||
})
|
||||
|
||||
it('should exclude an earlier failed attempt from a substitution group even when it later passed', async () => {
|
||||
mockStudents([
|
||||
{
|
||||
studentnumber: '111',
|
||||
credits: [
|
||||
credit({ course_code: 'MAT11002', credittypecode: CreditTypeCode.FAILED }),
|
||||
credit({ course_code: 'MAT11002', credittypecode: CreditTypeCode.PASSED }),
|
||||
credit({ course_code: 'MAT21001', credittypecode: CreditTypeCode.PASSED }),
|
||||
],
|
||||
},
|
||||
mockCredits([
|
||||
credit({
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: MAT11002 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.FAILED,
|
||||
}),
|
||||
credit({
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: MAT11002 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
credit({
|
||||
course_code: 'MAT21001',
|
||||
course: { groupId: MAT21001 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002', 'MAT21001']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses(
|
||||
[[MAT11002, MAT21001]],
|
||||
['course-1', 'course-2'],
|
||||
Unification.UNIFY,
|
||||
from,
|
||||
to
|
||||
)
|
||||
|
||||
assert.strictEqual(res.length, 1)
|
||||
assert.strictEqual(res.flat().length, 2)
|
||||
@@ -131,18 +159,33 @@ void describe('Get credits for courses (unit, mocked db)', { concurrent: false }
|
||||
})
|
||||
|
||||
it('should handle independent groups and students together', async () => {
|
||||
mockStudents([
|
||||
{ studentnumber: '111', credits: [credit({ student_studentnumber: '111', course_code: 'MAT11002' })] },
|
||||
{
|
||||
studentnumber: '222',
|
||||
credits: [
|
||||
credit({ student_studentnumber: '222', course_code: 'MAT21001', credittypecode: CreditTypeCode.PASSED }),
|
||||
credit({ student_studentnumber: '222', course_code: 'MAT21002', credittypecode: CreditTypeCode.PASSED }),
|
||||
],
|
||||
},
|
||||
mockCredits([
|
||||
credit({
|
||||
student_studentnumber: '111',
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: MAT11002 } as Credit['course'],
|
||||
}),
|
||||
credit({
|
||||
student_studentnumber: '222',
|
||||
course_code: 'MAT21001',
|
||||
course: { groupId: MAT21001 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
credit({
|
||||
student_studentnumber: '222',
|
||||
course_code: 'MAT21002',
|
||||
course: { groupId: MAT21002 } as Credit['course'],
|
||||
credittypecode: CreditTypeCode.PASSED,
|
||||
}),
|
||||
])
|
||||
|
||||
const res = await getCreditsForCourses([['MAT11002'], ['MAT21001', 'MAT21002']], Unification.UNIFY, from, to)
|
||||
const res = await getCreditsForCourses(
|
||||
[[MAT11002], [MAT21001, MAT21002]],
|
||||
['course-1', 'course-2', 'course-3'],
|
||||
Unification.UNIFY,
|
||||
from,
|
||||
to
|
||||
)
|
||||
|
||||
assert.strictEqual(res.length, 2)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, assert } from 'vitest'
|
||||
|
||||
import { CreditTypeCode } from '@oodikone/shared/types'
|
||||
import { Credit } from '@oodikone/shared/models'
|
||||
import { createCredit as credit, createStudyRightElementModel } from '@oodikone/shared/test/utils'
|
||||
import { parseCredit } from '@/services/courses'
|
||||
import { SISStudyRightElementModel } from '@/models'
|
||||
@@ -30,7 +31,10 @@ void describe('parseCredit', () => {
|
||||
|
||||
it('should combine a substitution group into one credit with summed credits', () => {
|
||||
const res = parseCredit(
|
||||
[credit({ course_code: 'MAT11002', credits: 5 }), credit({ course_code: 'MAT21001', credits: 5 })],
|
||||
[
|
||||
credit({ course_code: 'MAT11002', course: { groupId: 'hy-CU-117375394' } as Credit['course'], credits: 5 }),
|
||||
credit({ course_code: 'MAT21001', course: { groupId: 'hy-CU-117375754' } as Credit['course'], credits: 5 }),
|
||||
],
|
||||
null,
|
||||
'MAT11002',
|
||||
[studyRightElement()]
|
||||
|
||||
@@ -69,8 +69,8 @@ export const createStudyRight = (overrides: Partial<StudentStudyRight> = {}): St
|
||||
})
|
||||
|
||||
export const createCourse = (overrides: Partial<StudentCourse> = {}): StudentCourse => ({
|
||||
course_code: 'MAT001',
|
||||
date: new Date('2024-09-01'),
|
||||
course_id: 'hy-CU-000000001-2024-08-01',
|
||||
date: new Date('2024-08-01'),
|
||||
passed: true,
|
||||
grade: '5',
|
||||
credits: 5,
|
||||
@@ -94,9 +94,9 @@ export const createStudyPlan = (overrides: Partial<StudentStudyPlan> = {}): Stud
|
||||
export const createEnrollment = (
|
||||
overrides: Partial<FormattedStudent['enrollments'][number]> = {}
|
||||
): FormattedStudent['enrollments'][number] => ({
|
||||
course_code: 'MAT001',
|
||||
course_id: 'hy-CU-000000001-2024-08-01',
|
||||
state: EnrollmentState.ENROLLED,
|
||||
enrollment_date_time: new Date('2024-09-01'),
|
||||
enrollment_date_time: new Date('2024-08-01'),
|
||||
semestercode: 149,
|
||||
studyright_id: 'sr-1',
|
||||
...overrides,
|
||||
@@ -113,6 +113,7 @@ export const createCredit = (overrides: Partial<Credit> = {}) =>
|
||||
({
|
||||
grade: '5',
|
||||
course_code: 'MAT11002',
|
||||
course: { groupId: 'hy-CU-117375394' } as Credit['course'],
|
||||
credits: 5,
|
||||
attainment_date: new Date(2020, 0, 1),
|
||||
student_studentnumber: '111',
|
||||
|
||||
Reference in New Issue
Block a user