/** * Shared KV-backend conformance suite. Each backend's spec file calls * {@link runKvBackendContract} with a factory bound to its own medium; the * suite asserts every clause of the `src/backend.ts` contract so both * backends are held to identical semantics. * @module */ import { describe, expect, it } from 'vitest' import type { KvUnitDescriptor, StorageBackend } from '../src/backend.ts' /** One conformance run: a fresh backend plus a way to reopen the same medium (crash simulation). */ export interface KvBackendContractHarness { /** The backend under test, freshly created over an empty medium. */ backend: StorageBackend /** Open a NEW backend instance over the SAME medium, as after a process restart. */ reopen(): Promise } const DESCRIPTOR: KvUnitDescriptor = { name: 'contract_unit', version: 3, tables: ['alpha', 'beta'], hasGlobal: true, } /** * Run the shared conformance suite against one backend implementation. * @param label - Suite label, e.g. `json` / `sqlite`. * @param create - Factory producing a fresh harness per test. */ export function runKvBackendContract(label: string, create: () => Promise) { describe(`kv backend contract: ${label}`, () => { it('opens a missing unit as empty and serves loadAll immediately', async () => { const { backend } = await create() const unit = await backend.kv!.open(DESCRIPTOR) const snapshot = await unit.loadAll() expect(snapshot.tables).toEqual({ alpha: {}, beta: {} }) expect(snapshot.global).toBeNull() await backend.close() }) it('round-trips records and global durably across reopen', async () => { const harness = await create() const unit = await harness.backend.kv!.open(DESCRIPTOR) await unit.putRecord('alpha', 'k1', { n: 1 }) await unit.putRecord('alpha', 'k2', { n: 2 }) await unit.putRecord('beta', 'weird key / with:stuff', { ok: true }) await unit.setGlobal({ counter: 7 }) await harness.backend.close() const reopened = await harness.reopen() const unit2 = await reopened.kv!.open(DESCRIPTOR) const snapshot = await unit2.loadAll() expect(snapshot.tables['alpha']).toEqual({ k1: { n: 1 }, k2: { n: 2 } }) expect(snapshot.tables['beta']).toEqual({ 'weird key / with:stuff': { ok: true } }) expect(snapshot.global).toEqual({ counter: 7 }) await reopened.close() }) it('putRecord overwrites and deleteRecord is idempotent', async () => { const { backend } = await create() const unit = await backend.kv!.open(DESCRIPTOR) await unit.putRecord('alpha', 'k', { v: 'old' }) await unit.putRecord('alpha', 'k', { v: 'new' }) await unit.deleteRecord('alpha', 'k') await unit.deleteRecord('alpha', 'k') await unit.deleteRecord('alpha', 'never-existed') const snapshot = await unit.loadAll() expect(snapshot.tables['alpha']).toEqual({}) await backend.close() }) it('rejects a version mismatch on reopen without touching the data', async () => { const harness = await create() const unit = await harness.backend.kv!.open(DESCRIPTOR) await unit.putRecord('alpha', 'k', { v: 1 }) await harness.backend.close() const reopened = await harness.reopen() await expect(reopened.kv!.open({ ...DESCRIPTOR, version: 4 })).rejects.toMatchObject({ name: 'StorageError', code: 'version-mismatch', }) // Original version still opens and still holds the data. const unit2 = await reopened.kv!.open(DESCRIPTOR) expect((await unit2.loadAll()).tables['alpha']).toEqual({ k: { v: 1 } }) await reopened.close() }) it('rejects operations after unit close, and close is idempotent', async () => { const { backend } = await create() const unit = await backend.kv!.open(DESCRIPTOR) await unit.close() await unit.close() await expect(unit.putRecord('alpha', 'k', {})).rejects.toMatchObject({ code: 'closed' }) await expect(unit.loadAll()).rejects.toMatchObject({ code: 'closed' }) await backend.close() await backend.close() }) }) }