import type { EkklesiaUserId } from "@ekklesia/contracts"; import { type AccountPreferences, type AccountPreferencesUpdate, type AccountStore, AccountStoreError, type AccountSummary, DEFAULT_ACCOUNT_PREFERENCES, parseAccountPreferencesUpdate, } from "@ekklesia/core/account"; export interface InMemoryAccountPerson { readonly userId: string; readonly name: string; readonly email: string; readonly preferences?: Partial; } export interface InMemoryAccountStore extends AccountStore { /** Every user id a read or write was asked for, in order, so a route test can prove which account * the route touched — the session's, and never one named by the request. */ readonly requestedUserIds: readonly string[]; /** Makes the next operations fail the way an unreachable database would. */ failWith(code: "ACCOUNT_READ_FAILED" | "ACCOUNT_WRITE_FAILED" | undefined): void; } /** * An in-memory `AccountStore` for tests that exercise **routing and authorization**, not * persistence. It reproduces the store's contract — strict update parsing, partial updates, an * unknown person is not found — and nothing a database owns; persistence, the `CHECK` * constraints, isolation across connections, and the cascade are proven against real PostgreSQL in * `core/tests/account-store.integration.ts`. */ export function createInMemoryAccountStore( people: readonly InMemoryAccountPerson[] = [], ): InMemoryAccountStore { const records = new Map( people.map((person) => [ person.userId, { email: person.email, name: person.name, preferences: { ...DEFAULT_ACCOUNT_PREFERENCES, ...person.preferences }, }, ]), ); const requestedUserIds: string[] = []; let failure: "ACCOUNT_READ_FAILED" | "ACCOUNT_WRITE_FAILED" | undefined; return { failWith(code) { failure = code; }, async readAccount(userId: EkklesiaUserId): Promise { requestedUserIds.push(userId); if (failure) { throw new AccountStoreError("ACCOUNT_READ_FAILED"); } const record = records.get(userId); return record ? { email: record.email, name: record.name, preferences: { ...record.preferences } } : undefined; }, requestedUserIds, async updatePreferences( userId: EkklesiaUserId, update: AccountPreferencesUpdate, ): Promise { requestedUserIds.push(userId); const parsed = parseAccountPreferencesUpdate(update); if (!parsed) { throw new AccountStoreError("ACCOUNT_PREFERENCES_INVALID"); } if (failure) { throw new AccountStoreError("ACCOUNT_WRITE_FAILED"); } const record = records.get(userId); if (!record) { throw new AccountStoreError("ACCOUNT_NOT_FOUND"); } record.preferences = { ...record.preferences, ...parsed }; return { ...record.preferences }; }, }; }