/** * In-memory {@link StorageBackend} test double implementing the full KvUnit * primitive set. Shared test infrastructure: the domain suite uses it to * exercise open/route/write semantics without touching disk, and the * workspace package's tests import it by relative path (it lives under * `tests/`, never `src/`, so it stays out of the published surface). * * Fidelity to the backend contract (`dsh-storage` `src/backend.ts`): version * stamping and `version-mismatch` on reopen, `malformed` never (memory cannot * corrupt), per-call atomicity trivially, `closed` after close, delete * idempotence. Media survive across backends through the shared `media` map * passed into the constructor, which simulates process restarts; stamp * `versions` directly to fabricate an on-medium version and force a * `version-mismatch` without a prior open. * @module */ import { StorageError } from '@deepseek-ai/dsh-storage' import type { KvFacet, KvUnit, KvUnitDescriptor, StorageBackend } from '@deepseek-ai/dsh-storage' /** One unit's medium: tables of records plus the global slot (`null` = never written). */ export interface MemoryMedium { tables: Map> global: unknown } /** * Shared media pool. Construct one and hand it to several * {@link MemoryStorageBackend} instances to simulate reopening the same * medium after a restart; `versions` holds the stamped unit versions and is * writable by tests to inject a mismatching on-medium version, and * `failNextWrites` injects write-primitive failures. */ export class MemoryMediaPool { /** Unit name → its records; a missing entry is a never-materialized unit. */ readonly media = new Map() /** Unit name → stamped version; tests may pre-stamp to force `version-mismatch`. */ readonly versions = new Map() /** * When positive, that many subsequent write primitives (putRecord / * deleteRecord / setGlobal) reject without touching the medium, decrementing * per rejection. Negative path: callers assert their state is * untouched after a durability failure. */ failNextWrites = 0 /** Consume one injected failure, throwing in a rejected write's place. */ consumeInjectedFailure(): void { if (this.failNextWrites > 0) { this.failNextWrites -= 1 throw new Error('injected write failure') } } } /** In-memory KV unit over one pooled medium. */ class MemoryKvUnit implements KvUnit { private closed = false constructor( private readonly pool: MemoryMediaPool, private readonly medium: MemoryMedium, private readonly descriptor: KvUnitDescriptor, private readonly onClose: () => void, ) {} private assertOpen(): void { if (this.closed) { throw new StorageError('closed', `memory unit '${this.descriptor.name}' is closed`) } } async loadAll(): Promise<{ tables: Record>; global: unknown }> { this.assertOpen() const tables: Record> = {} for (const table of this.descriptor.tables) { tables[table] = Object.fromEntries(this.medium.tables.get(table) ?? []) } return { tables, global: this.medium.global } } async putRecord(table: string, key: string, value: unknown): Promise { this.assertOpen() this.pool.consumeInjectedFailure() let records = this.medium.tables.get(table) if (records === undefined) { records = new Map() this.medium.tables.set(table, records) } records.set(key, value) } async deleteRecord(table: string, key: string): Promise { this.assertOpen() this.pool.consumeInjectedFailure() this.medium.tables.get(table)?.delete(key) } async setGlobal(value: unknown): Promise { this.assertOpen() this.pool.consumeInjectedFailure() this.medium.global = value } async close(): Promise { if (this.closed) return this.closed = true this.onClose() } } /** * In-memory storage backend with a `kv` facet. Pass a shared * {@link MemoryMediaPool} to let a second instance reopen the same media; * omit it for a throwaway isolated pool. */ export class MemoryStorageBackend implements StorageBackend { readonly kv: KvFacet private readonly openUnits = new Set() private closed = false /** * @param pool - Media shared across instances; a fresh private pool when omitted. */ constructor(readonly pool: MemoryMediaPool = new MemoryMediaPool()) { this.kv = { open: async (descriptor: KvUnitDescriptor): Promise => { if (this.closed) { throw new StorageError('closed', 'memory backend is closed') } // Double-open is a caller bug per the backend contract; no dedicated // StorageError code exists for it, so a plain Error is correct. if (this.openUnits.has(descriptor.name)) { throw new Error(`memory unit '${descriptor.name}' is already open (double-open is a caller bug)`) } const stamped = this.pool.versions.get(descriptor.name) if (stamped === undefined) { this.pool.versions.set(descriptor.name, descriptor.version) } else if (stamped !== descriptor.version) { throw new StorageError( 'version-mismatch', `memory unit '${descriptor.name}' is stamped v${stamped}, descriptor wants v${descriptor.version}`, ) } let medium = this.pool.media.get(descriptor.name) if (medium === undefined) { medium = { tables: new Map(), global: null } this.pool.media.set(descriptor.name, medium) } this.openUnits.add(descriptor.name) return new MemoryKvUnit(this.pool, medium, descriptor, () => this.openUnits.delete(descriptor.name)) }, } } async close(): Promise { this.closed = true this.openUnits.clear() } }