Atomic whole-file replacement (same-dir temp + fsync + rename + parent fsync); the in-memory unit state is authoritative and the file is always the current net state, pretty-printed. Missing files open as empty units and materialize on first write; foreign or unparsable files reject with malformed-medium, stored-version drift with version-mismatch.
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterAll, describe, expect, it } from 'vitest'
|
|
import { runKvBackendContract } from '../../storage/tests/contract.ts'
|
|
import { JsonStorageBackend } from '../src/index.ts'
|
|
|
|
const roots: string[] = []
|
|
|
|
async function freshRoot(): Promise<string> {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-storage-json-'))
|
|
roots.push(root)
|
|
return root
|
|
}
|
|
|
|
afterAll(async () => {
|
|
for (const root of roots) await rm(root, { recursive: true, force: true })
|
|
})
|
|
|
|
runKvBackendContract('json', async () => {
|
|
const root = await freshRoot()
|
|
return {
|
|
backend: new JsonStorageBackend(root),
|
|
reopen: async () => new JsonStorageBackend(root),
|
|
}
|
|
})
|
|
|
|
describe('json backend specifics', () => {
|
|
const descriptor = { name: 'shape', version: 1, tables: ['t'], hasGlobal: true }
|
|
|
|
it('publishes a human-readable pretty-printed file', async () => {
|
|
const root = await freshRoot()
|
|
const backend = new JsonStorageBackend(root)
|
|
const unit = await backend.kv.open(descriptor)
|
|
await unit.putRecord('t', 'k', { hello: 'world' })
|
|
const text = await readFile(join(root, 'shape.json'), 'utf8')
|
|
expect(text).toBe(`${JSON.stringify(
|
|
{ unit: { name: 'shape', version: 1 }, global: null, tables: { t: { k: { hello: 'world' } } } },
|
|
null,
|
|
2,
|
|
)}\n`)
|
|
await backend.close()
|
|
})
|
|
|
|
it('defers materialization until the first write', async () => {
|
|
const root = await freshRoot()
|
|
const backend = new JsonStorageBackend(root)
|
|
await backend.kv.open(descriptor)
|
|
await expect(readFile(join(root, 'shape.json'), 'utf8')).rejects.toMatchObject({ code: 'ENOENT' })
|
|
await backend.close()
|
|
})
|
|
|
|
it('rejects a malformed medium', async () => {
|
|
const root = await freshRoot()
|
|
await writeFile(join(root, 'shape.json'), 'not json at all', 'utf8')
|
|
const backend = new JsonStorageBackend(root)
|
|
await expect(backend.kv.open(descriptor)).rejects.toMatchObject({ code: 'malformed-medium' })
|
|
await backend.close()
|
|
})
|
|
|
|
it('rejects a foreign unit header', async () => {
|
|
const root = await freshRoot()
|
|
await writeFile(
|
|
join(root, 'shape.json'),
|
|
JSON.stringify({ unit: { name: 'other', version: 1 }, global: null, tables: {} }),
|
|
'utf8',
|
|
)
|
|
const backend = new JsonStorageBackend(root)
|
|
await expect(backend.kv.open(descriptor)).rejects.toMatchObject({ code: 'malformed-medium' })
|
|
await backend.close()
|
|
})
|
|
|
|
it('rejects double-open of one unit', async () => {
|
|
const root = await freshRoot()
|
|
const backend = new JsonStorageBackend(root)
|
|
await backend.kv.open(descriptor)
|
|
await expect(backend.kv.open(descriptor)).rejects.toMatchObject({ code: 'malformed-medium' })
|
|
await backend.close()
|
|
})
|
|
})
|