CourseKitBackend
Testing
In-memory adapters, factories, fixtures, and assertion helpers for testing
Overview
The @hfu.digital/coursekit-nestjs/testing subpath provides everything you need to test CourseKit integrations without a real database.
import {
InMemoryTimetableEventStorage,
InMemoryRoomStorage,
InMemoryInstructorStorage,
InMemoryGroupStorage,
InMemoryAvailabilityStorage,
InMemoryAcademicPeriodStorage,
InMemoryCourseStorage,
InMemoryLocationDistanceStorage,
createTestEvent,
createTestRoom,
expectNoConflicts,
expectConflict,
} from '@hfu.digital/coursekit-nestjs/testing';In-Memory Adapters
Drop-in replacements for Prisma adapters that store data in memory:
| Adapter | Replaces |
|---|---|
InMemoryTimetableEventStorage | PrismaTimetableEventAdapter |
InMemoryRoomStorage | PrismaRoomAdapter |
InMemoryInstructorStorage | PrismaInstructorAdapter |
InMemoryGroupStorage | PrismaGroupAdapter |
InMemoryAvailabilityStorage | PrismaAvailabilityAdapter |
InMemoryAcademicPeriodStorage | PrismaAcademicPeriodAdapter |
InMemoryCourseStorage | PrismaCourseAdapter |
InMemoryLocationDistanceStorage | PrismaLocationDistanceAdapter |
Test Module Setup
import { Test } from '@nestjs/testing';
import { CourseKitModule } from '@hfu.digital/coursekit-nestjs';
import {
InMemoryTimetableEventStorage,
InMemoryRoomStorage,
InMemoryInstructorStorage,
InMemoryGroupStorage,
InMemoryAvailabilityStorage,
InMemoryAcademicPeriodStorage,
InMemoryCourseStorage,
} from '@hfu.digital/coursekit-nestjs/testing';
const module = await Test.createTestingModule({
imports: [
CourseKitModule.register({
eventStorage: new InMemoryTimetableEventStorage(),
roomStorage: new InMemoryRoomStorage(),
instructorStorage: new InMemoryInstructorStorage(),
groupStorage: new InMemoryGroupStorage(),
availabilityStorage: new InMemoryAvailabilityStorage(),
periodStorage: new InMemoryAcademicPeriodStorage(),
courseStorage: new InMemoryCourseStorage(),
}),
],
}).compile();Factories
Factory functions create test entities with sensible defaults. Pass overrides for specific fields:
import {
createTestEvent,
createTestException,
createTestRoom,
createTestInstructor,
createTestGroup,
createTestCourse,
createTestAvailability,
createTestPeriod,
} from '@hfu.digital/coursekit-nestjs/testing';
const event = createTestEvent({
title: 'Math 101',
recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO',
});
const room = createTestRoom({
name: 'Lecture Hall A',
capacity: 200,
building: 'Main Building',
});
const instructor = createTestInstructor({
name: 'Prof. Smith',
email: 'smith@university.edu',
});
const exception = createTestException({
eventId: event.id,
type: 'cancelled',
originalDate: new Date('2026-03-09'),
});Fixtures
Pre-built schedule scenarios for common test cases:
import {
simpleSchoolWeek,
universitySemester,
edgeCaseSchedule,
} from '@hfu.digital/coursekit-nestjs/testing';| Fixture | Description |
|---|---|
simpleSchoolWeek | A basic Mon-Fri school schedule with 3 rooms and 5 instructors |
universitySemester | A full semester with recurring lectures, labs, and exam periods |
edgeCaseSchedule | Edge cases: back-to-back events, midnight crossings, DST transitions |
EventSpy
Capture domain events emitted during tests:
import { EventSpy } from '@hfu.digital/coursekit-nestjs/testing';
import { DOMAIN_EVENTS } from '@hfu.digital/coursekit-nestjs';
const spy = new EventSpy(eventEmitter);
// ... perform operations ...
const createdEvents = spy.getEvents(DOMAIN_EVENTS.EVENT_CREATED);
expect(createdEvents).toHaveLength(1);
expect(createdEvents[0].event.title).toBe('Math 101');Assertion Helpers
Fluent assertion helpers for conflict check results:
import {
expectNoConflicts,
expectConflict,
expectConflictCount,
expectConflictInvolves,
} from '@hfu.digital/coursekit-nestjs/testing';
// Assert no conflicts
expectNoConflicts(result);
// Assert a specific conflict type exists
expectConflict(result, 'instructor-double-book');
// Assert exact conflict count
expectConflictCount(result, 2);
// Assert a conflict involves specific entities
expectConflictInvolves(result, 'instructor-double-book', ['instructor-1']);Full Example
import { Test } from '@nestjs/testing';
import { CourseKitModule, ConflictService, type TimetableEvent } from '@hfu.digital/coursekit-nestjs';
import {
InMemoryTimetableEventStorage,
InMemoryRoomStorage,
InMemoryInstructorStorage,
InMemoryGroupStorage,
InMemoryAvailabilityStorage,
InMemoryAcademicPeriodStorage,
InMemoryCourseStorage,
createTestEvent,
createTestRoom,
expectConflict,
} from '@hfu.digital/coursekit-nestjs/testing';
describe('Conflict Detection', () => {
let conflicts: ConflictService;
let eventStorage: InMemoryTimetableEventStorage;
beforeEach(async () => {
eventStorage = new InMemoryTimetableEventStorage();
const module = await Test.createTestingModule({
imports: [
CourseKitModule.register({
eventStorage,
roomStorage: new InMemoryRoomStorage(),
instructorStorage: new InMemoryInstructorStorage(),
groupStorage: new InMemoryGroupStorage(),
availabilityStorage: new InMemoryAvailabilityStorage(),
periodStorage: new InMemoryAcademicPeriodStorage(),
courseStorage: new InMemoryCourseStorage(),
}),
],
}).compile();
conflicts = module.get(ConflictService);
});
it('should detect room double-booking', async () => {
const room = createTestRoom({ name: 'Room A' });
const event1 = createTestEvent({ title: 'Event 1', roomId: room.id });
const event2 = createTestEvent({ title: 'Event 2', roomId: room.id });
await eventStorage.create(event1);
await eventStorage.create(event2);
const result = await conflicts.check(event1, {
start: new Date('2026-03-01'),
end: new Date('2026-03-31'),
});
expectConflict(result, 'room-overlap');
});
});