import type { DashboardConfiguration, DashboardGroupRecord, DashboardPlacementRecord, DashboardStore, SetDashboardPlacementInput, UpsertDashboardGroupInput, } from "@ekklesia/core/dashboard"; import { DashboardStoreError } from "@ekklesia/core/dashboard"; /** * An in-memory `DashboardStore` for tests that exercise **routing and resolution**, not persistence. * * It reproduces only the two behaviours those tests depend on — a placement may not name a group * that does not exist, and deleting a group ungroups its modules (the schema's * `on delete set null`) — and deliberately reproduces nothing else. Everything the real store owns * that this double would only be guessing at (identifier grammar, label trimming and bounds, * ordering bounds, transactional behaviour, and the actual `on delete set null` constraint) is * proven against real PostgreSQL in `core/tests/dashboard-store.integration.ts` instead, so this * double can never quietly stand in for a persistence guarantee nobody checked. */ export function createInMemoryDashboardStore( seed: Partial = {}, ): DashboardStore { const groups = new Map( (seed.groups ?? []).map((group) => [group.id, { ...group }] as const), ); const placements = new Map( (seed.placements ?? []).map((placement) => [placement.moduleId, { ...placement }] as const), ); const sorted = ( records: readonly Record[], key: (record: Record) => string, ): readonly Record[] => Object.freeze( [...records].sort( (left, right) => left.sortOrder - right.sortOrder || (key(left) < key(right) ? -1 : key(left) > key(right) ? 1 : 0), ), ); return Object.freeze({ async deleteGroup(groupId: string): Promise { if (!groups.delete(groupId)) { throw new DashboardStoreError("DASHBOARD_GROUP_NOT_FOUND"); } for (const [moduleId, placement] of placements) { if (placement.groupId === groupId) { placements.set(moduleId, { ...placement, groupId: null }); } } }, async readConfiguration(): Promise { return Object.freeze({ groups: sorted([...groups.values()], (group) => group.id), placements: sorted([...placements.values()], (placement) => placement.moduleId), }); }, async setPlacement(input: SetDashboardPlacementInput): Promise { if (input.groupId !== null && !groups.has(input.groupId)) { throw new DashboardStoreError("DASHBOARD_GROUP_NOT_FOUND"); } const record: DashboardPlacementRecord = Object.freeze({ groupId: input.groupId, moduleId: input.moduleId, sortOrder: input.sortOrder, }); placements.set(input.moduleId, record); return record; }, async upsertGroup(input: UpsertDashboardGroupInput): Promise { const record: DashboardGroupRecord = Object.freeze({ id: input.id, label: input.label.trim(), sortOrder: input.sortOrder, }); groups.set(input.id, record); return record; }, }); }