Files
adam-acs/tests/property/schedule_window.property.spec.ts
T
Kevin L'HereecandCopilot d1c65dbf1e chore: initial commit — AdonisJS v7 TR-069 ACS
- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim)
- AdonisJS v7 app with Inertia.js + Vue 3
- MongoDB integration
- CWMP/TR-069 state machine engine
- Configuration engine with compliance tracking
- Branding: ADAM ACS (favicon, navbar, locales)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 16:46:37 +02:00

193 lines
6.3 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import fc from 'fast-check'
import { isWithinSchedule } from '../../app/utils/schedule_window.js'
import type { ScheduleWindow } from '../../app/domain/configuration/configuration.js'
/**
* Property 12: Schedule window evaluation
* Validates: Requirements 8.1, 8.3
*
* Verifies midnight-spanning logic and day-of-week filtering using
* randomly generated schedules and timestamps.
*/
describe('Property 12: Schedule window evaluation', () => {
// --- Arbitraries ---
/** Generate a valid hour (0-23) */
const arbHour = fc.integer({ min: 0, max: 23 })
/** Generate a valid day-of-week subset (0=Sunday ... 6=Saturday) */
const arbDaysOfWeek = fc.subarray([0, 1, 2, 3, 4, 5, 6], { minLength: 1 })
/** Generate a Date within a reasonable range */
const arbDate = fc.date({
min: new Date('2020-01-01T00:00:00Z'),
max: new Date('2030-12-31T23:59:59Z'),
})
/** Generate a non-midnight-spanning schedule (startHour < endHour) */
const arbNormalSchedule: fc.Arbitrary<ScheduleWindow> = fc
.tuple(arbHour, arbHour, arbDaysOfWeek)
.filter(([s, e]) => s < e)
.map(([startHour, endHour, daysOfWeek]) => ({
startHour,
endHour,
daysOfWeek,
timezone: 'UTC',
}))
/** Generate a midnight-spanning schedule (startHour > endHour) */
const arbMidnightSchedule: fc.Arbitrary<ScheduleWindow> = fc
.tuple(arbHour, arbHour, arbDaysOfWeek)
.filter(([s, e]) => s > e)
.map(([startHour, endHour, daysOfWeek]) => ({
startHour,
endHour,
daysOfWeek,
timezone: 'UTC',
}))
/** Generate any valid schedule (startHour !== endHour) */
const arbSchedule: fc.Arbitrary<ScheduleWindow> = fc
.tuple(arbHour, arbHour, arbDaysOfWeek)
.filter(([s, e]) => s !== e)
.map(([startHour, endHour, daysOfWeek]) => ({
startHour,
endHour,
daysOfWeek,
timezone: 'UTC',
}))
/**
* Helper: create a UTC Date at a specific hour on a specific day-of-week.
* Uses a known reference week (2025-01-05 is Sunday).
*/
function makeDateAtHourAndDay(hour: number, dayOfWeek: number): Date {
// 2025-01-05 is a Sunday (dayOfWeek=0)
const baseDate = new Date('2025-01-05T00:00:00Z')
baseDate.setUTCDate(baseDate.getUTCDate() + dayOfWeek)
baseDate.setUTCHours(hour, 0, 0, 0)
return baseDate
}
// --- Property 1: undefined schedule always returns true ---
it('isWithinSchedule(undefined, anyDate) always returns true', () => {
fc.assert(
fc.property(arbDate, (date) => {
expect(isWithinSchedule(undefined, date)).toBe(true)
})
)
})
// --- Property 2: Non-midnight-spanning window boundary behavior ---
/**
* **Validates: Requirements 8.1, 8.3**
*
* For a non-midnight-spanning window (startHour < endHour):
* - A time at startHour is within the window
* - A time at endHour is NOT within the window (exclusive end)
*/
it('non-midnight-spanning: startHour is within, endHour is NOT within', () => {
fc.assert(
fc.property(arbNormalSchedule, (schedule) => {
// Pick a day that IS in daysOfWeek
const day = schedule.daysOfWeek[0]
const atStart = makeDateAtHourAndDay(schedule.startHour, day)
const atEnd = makeDateAtHourAndDay(schedule.endHour, day)
expect(isWithinSchedule(schedule, atStart)).toBe(true)
expect(isWithinSchedule(schedule, atEnd)).toBe(false)
})
)
})
// --- Property 3: Midnight-spanning window behavior ---
/**
* **Validates: Requirements 8.1, 8.3**
*
* For a midnight-spanning window (startHour > endHour):
* - A time at startHour is within the window
* - A time between endHour and startHour (exclusive) is NOT within the window
*/
it('midnight-spanning: startHour is within, hour between endHour and startHour is NOT within', () => {
fc.assert(
fc.property(
arbMidnightSchedule,
fc.integer({ min: 0, max: 23 }),
(schedule, randomHour) => {
// Pick a day that IS in daysOfWeek
const day = schedule.daysOfWeek[0]
// startHour should be within
const atStart = makeDateAtHourAndDay(schedule.startHour, day)
expect(isWithinSchedule(schedule, atStart)).toBe(true)
// An hour strictly between endHour and startHour should be outside
// For midnight-spanning: outside range is [endHour, startHour)
const outsideHour = schedule.endHour + (randomHour % (schedule.startHour - schedule.endHour))
if (outsideHour >= schedule.endHour && outsideHour < schedule.startHour) {
const outsideDate = makeDateAtHourAndDay(outsideHour, day)
expect(isWithinSchedule(schedule, outsideDate)).toBe(false)
}
}
)
)
})
// --- Property 4: Day-of-week filtering ---
/**
* **Validates: Requirements 8.1, 8.3**
*
* If the day is NOT in daysOfWeek, returns false regardless of hour.
*/
it('day-of-week filtering: excluded day always returns false', () => {
fc.assert(
fc.property(arbSchedule, arbHour, (schedule, hour) => {
const allDays = [0, 1, 2, 3, 4, 5, 6]
const excludedDays = allDays.filter((d) => !schedule.daysOfWeek.includes(d))
// Only test if there are excluded days
if (excludedDays.length > 0) {
const excludedDay = excludedDays[0]
const date = makeDateAtHourAndDay(hour, excludedDay)
expect(isWithinSchedule(schedule, date)).toBe(false)
}
})
)
})
// --- Property 5: Included day + within hour range returns true ---
/**
* **Validates: Requirements 8.1, 8.3**
*
* If daysOfWeek includes the current day and hour is within range, returns true.
*/
it('included day + hour within range returns true', () => {
fc.assert(
fc.property(arbSchedule, (schedule) => {
const day = schedule.daysOfWeek[0]
const { startHour, endHour } = schedule
// Pick an hour that is definitely within the window
let withinHour: number
if (startHour < endHour) {
// Normal window: any hour in [startHour, endHour)
withinHour = startHour
} else {
// Midnight-spanning: startHour is always within
withinHour = startHour
}
const date = makeDateAtHourAndDay(withinHour, day)
expect(isWithinSchedule(schedule, date)).toBe(true)
})
)
})
})