[Monthly credit accml] fix crash caused by date change mid-computation

uses months to compute keys rather than dates
This commit is contained in:
joonas-a
2026-10-07 11:07:50 +03:00
parent caa7739f37
commit 41d027e054
2 changed files with 49 additions and 7 deletions
@@ -139,17 +139,19 @@ export const getYearlyMonthlyCreditsObj = () => {
return yearlyMonthlyCredits
}
const getMonthlyCreditsObj = (academicStartYear?: number) => {
const getMonthlyCreditsObj = (academicStartYear = 2017) => {
const today = now()
const time = getStartDate(true, academicStartYear)
/* Compare whole months instead of dates, so the result does not depend on the time of day.
Academic year starts in August (month index 7). */
const firstMonth = academicStartYear * 12 + 7
const lastMonth = today.getFullYear() * 12 + today.getMonth()
const monthlyCredits: Record<string, number[]> = {}
today.setDate(1)
/* NB: JS months start at 0. We need them to start at 1. */
while (time < today) {
monthlyCredits[`${time.getFullYear()}-${time.getMonth() + 1}`] = []
time.setMonth(time.getMonth() + 1)
for (let month = firstMonth; month <= lastMonth; month++) {
monthlyCredits[`${Math.floor(month / 12)}-${(month % 12) + 1}`] = []
}
return monthlyCredits
@@ -1,5 +1,12 @@
import { describe, it, assert, vi, beforeAll } from 'vitest'
import { getPercentage, getYearsArray, getYearsObject } from '@/services/studyProgramme/studyProgrammeHelpers'
import {
getMonthlyCredits,
getPercentage,
getYearlyMonthlyCreditsObj,
getYearsArray,
getYearsObject,
} from '@/services/studyProgramme/studyProgrammeHelpers'
import { clockStorage } from '@/util/clock'
void describe('Get years object', () => {
it('should return nothing with empty years', () => {
@@ -95,3 +102,36 @@ void describe('Get percentage', () => {
assert.strictEqual(getPercentage(-5, 10), '-50.0 %')
})
})
void describe('Get monthly credits', () => {
const atTime = <T>(date: string, fn: () => T) => clockStorage.run({ now: new Date(date) }, fn)
it('should include the current month regardless of the time of day', () => {
const lastKey = (date: string) => Object.keys(atTime(date, () => getYearlyMonthlyCreditsObj())[2024]).at(-1)
assert.strictEqual(lastKey('2026-10-07T00:05:00'), '2026-10')
assert.strictEqual(lastKey('2026-10-07T23:40:00'), '2026-10')
})
it('should not throw when computation continues past midnight', () => {
const monthlyCreditsByStartingYear = atTime('2026-10-06T23:40:00', () => getYearlyMonthlyCreditsObj())
assert.doesNotThrow(() =>
atTime('2026-10-07T00:05:00', () =>
getMonthlyCredits([], new Date('2024-08-01'), 2024, monthlyCreditsByStartingYear[2024])
)
)
})
it('should accumulate credits cumulatively by month', () => {
const monthlyCredits = atTime('2024-10-15T12:00:00', () => getYearlyMonthlyCreditsObj())[2024]
const credits = [
{ attainment_date: new Date('2024-08-20'), credits: 5 },
{ attainment_date: new Date('2024-10-01'), credits: 3 },
] as Parameters<typeof getMonthlyCredits>[0]
atTime('2024-10-15T12:00:00', () => getMonthlyCredits(credits, new Date('2024-08-01'), 2024, monthlyCredits))
assert.deepStrictEqual(monthlyCredits, { '2024-8': [5], '2024-9': [5], '2024-10': [8] })
})
})