Testing
Test patterns and in-memory adapters for RoomKit
Overview
RoomKit provides in-memory storage adapters for testing. These adapters implement all 11 storage interfaces without requiring a database, making tests fast and isolated.
import { createInMemoryStorage } from '@roomkit/nestjs/testing';In-Memory Adapters
The createInMemoryStorage() factory returns a complete set of storage implementations that hold data in memory. All data is reset between test runs when you create a new instance.
import { createInMemoryStorage } from '@roomkit/nestjs/testing';
const storage = createInMemoryStorage();
// storage implements all 11 storage interfaces:
// LocationStorage, RoomStorage, BookingStorage, RecurrenceStorage,
// BlackoutStorage, ConflictStorage, ConfigStorage, AuditStorage,
// PriorityStorage, ExamStorage, BulkOperationStorageTest Setup Pattern
Use the NestJS Test.createTestingModule with in-memory adapters to create an isolated test module.
import { Test } from '@nestjs/testing';
import {
RoomKitModule,
BookingService,
LocationService,
RoomService,
} from '@roomkit/nestjs';
import { createInMemoryStorage } from '@roomkit/nestjs/testing';
describe('BookingService', () => {
let bookingService: BookingService;
let locationService: LocationService;
let roomService: RoomService;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [
RoomKitModule.register({
storage: createInMemoryStorage(),
}),
],
}).compile();
bookingService = module.get(BookingService);
locationService = module.get(LocationService);
roomService = module.get(RoomService);
});
it('should create and confirm a booking', async () => {
const location = await locationService.createNode({
type: 'room',
displayName: 'Room 101',
});
const room = await roomService.create({
locationNodeId: location.id,
seatedCapacity: 30,
examCapacity: 15,
standingCapacity: 50,
});
const booking = await bookingService.create({
roomId: room.id,
requesterId: 'user-1',
title: 'Team Meeting',
startsAt: new Date('2026-03-02T10:00:00Z'),
endsAt: new Date('2026-03-02T11:00:00Z'),
purposeType: 'SEMINAR',
});
expect(booking.status).toBe('requested');
const confirmed = await bookingService.confirm(booking.id, 'admin');
expect(confirmed.status).toBe('confirmed');
});
});Booking Lifecycle Test
Test the full lifecycle of a booking from creation through completion.
it('should complete the full booking lifecycle', async () => {
const location = await locationService.createNode({
type: 'room',
displayName: 'Lecture Hall A',
});
const room = await roomService.create({
locationNodeId: location.id,
seatedCapacity: 100,
examCapacity: 50,
standingCapacity: 150,
});
// Create
const booking = await bookingService.create({
roomId: room.id,
requesterId: 'user-1',
title: 'CS 101 Lecture',
startsAt: new Date('2026-03-02T09:00:00Z'),
endsAt: new Date('2026-03-02T10:00:00Z'),
purposeType: 'LECTURE',
});
expect(booking.status).toBe('requested');
// Confirm
const confirmed = await bookingService.confirm(booking.id, 'admin');
expect(confirmed.status).toBe('confirmed');
// Check in
const started = await bookingService.checkIn(booking.id, 'user-1');
expect(started.status).toBe('in_progress');
// Complete
const completed = await bookingService.complete(booking.id, 'user-1');
expect(completed.status).toBe('completed');
});Conflict Detection Test
Verify that overlapping bookings are detected correctly.
import { BookingConflictError } from '@roomkit/nestjs';
it('should detect conflicts for overlapping bookings', async () => {
const location = await locationService.createNode({
type: 'room',
displayName: 'Room 202',
});
const room = await roomService.create({
locationNodeId: location.id,
seatedCapacity: 20,
examCapacity: 10,
standingCapacity: 30,
});
// Create the first booking
await bookingService.create({
roomId: room.id,
requesterId: 'user-1',
title: 'Morning Meeting',
startsAt: new Date('2026-03-02T09:00:00Z'),
endsAt: new Date('2026-03-02T10:00:00Z'),
purposeType: 'SEMINAR',
});
// Attempt to create an overlapping booking
await expect(
bookingService.create({
roomId: room.id,
requesterId: 'user-2',
title: 'Overlapping Meeting',
startsAt: new Date('2026-03-02T09:30:00Z'),
endsAt: new Date('2026-03-02T10:30:00Z'),
purposeType: 'SEMINAR',
}),
).rejects.toThrow(BookingConflictError);
});Recurrence Test
Verify that recurring bookings expand to the expected dates.
import { RecurrenceService } from '@roomkit/nestjs';
it('should create a recurring weekly booking', async () => {
const module = await Test.createTestingModule({
imports: [
RoomKitModule.register({
storage: createInMemoryStorage(),
}),
],
}).compile();
const recurrenceService = module.get(RecurrenceService);
const locationService = module.get(LocationService);
const roomService = module.get(RoomService);
const location = await locationService.createNode({
type: 'room',
displayName: 'Seminar Room',
});
const room = await roomService.create({
locationNodeId: location.id,
seatedCapacity: 25,
examCapacity: 12,
standingCapacity: 40,
});
const result = await recurrenceService.createRecurringSeries({
roomId: room.id,
requesterId: 'user-1',
title: 'Weekly Standup',
startsAt: new Date('2026-03-02T09:00:00Z'),
endsAt: new Date('2026-03-02T09:30:00Z'),
purposeType: 'SEMINAR',
recurrence: {
frequency: 'weekly',
interval: 1,
until: new Date('2026-03-30T09:30:00Z'),
},
});
// Expect 5 bookings: Mar 2, 9, 16, 23, 30
expect(result.bookings).toHaveLength(5);
expect(result.bookings[0].startsAt).toEqual(new Date('2026-03-02T09:00:00Z'));
expect(result.bookings[4].startsAt).toEqual(new Date('2026-03-30T09:00:00Z'));
});Testing with Events
Subscribe to domain events during tests to verify that the correct events are emitted.
import { EventBus } from '@roomkit/nestjs';
it('should emit BookingRequested event on create', async () => {
const eventBus = module.get(EventBus);
const events: any[] = [];
eventBus.on('BookingRequested', (event) => {
events.push(event);
});
const location = await locationService.createNode({
type: 'room',
displayName: 'Room 303',
});
const room = await roomService.create({
locationNodeId: location.id,
seatedCapacity: 15,
examCapacity: 8,
standingCapacity: 20,
});
const booking = await bookingService.create({
roomId: room.id,
requesterId: 'user-1',
title: 'Event Test',
startsAt: new Date('2026-03-02T14:00:00Z'),
endsAt: new Date('2026-03-02T15:00:00Z'),
purposeType: 'SEMINAR',
});
expect(events).toHaveLength(1);
expect(events[0].payload.id).toBe(booking.id);
expect(events[0].metadata.triggeredBy).toBeDefined();
});