brainy/tests/unit/storage/registered-blob-contract.test.ts

130 lines
5.5 KiB
TypeScript

/**
* @module tests/unit/storage/registered-blob-contract
* @description Declared derived-index blob FAMILIES are
* undeletable through the storage layer — an in-process GC/sweeper cannot remove
* a load-bearing index file (the lost-main.dkann class). Covers declare →
* protected-delete-throws; unregister → delete-ok; transient passthrough;
* namespace prefix protects children; removeRawPrefix refuses a protected
* intersection; persistence across reopen; missing-on-open detection.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import * as fs from 'node:fs'
import * as os from 'node:os'
import * as path from 'node:path'
import { MemoryStorage } from '../../../src/storage/adapters/memoryStorage.js'
import { FileSystemStorage } from '../../../src/storage/adapters/fileSystemStorage.js'
import { ProtectedArtifactError } from '../../../src/index.js'
describe('registered-blob family contract', () => {
let storage: any
beforeEach(async () => {
storage = new MemoryStorage()
await storage.init()
})
const seedVectorFamily = async () => {
await storage.saveBinaryBlob('_system/vector-index/main.dkann', Buffer.from([1]))
await storage.saveBinaryBlob('_system/vector-index/main.slotmap', Buffer.from([2]))
await storage.saveBinaryBlob('_system/vector-index/main.slotrev', Buffer.from([3]))
await storage.registerDerivedFamily({
name: 'vector-base',
members: [
'_system/vector-index/main.dkann',
'_system/vector-index/main.slotmap',
'_system/vector-index/main.slotrev'
]
})
}
it('a declared member is undeletable (throws ProtectedArtifactError)', async () => {
await seedVectorFamily()
await expect(storage.deleteBinaryBlob('_system/vector-index/main.dkann')).rejects.toBeInstanceOf(
ProtectedArtifactError
)
// The blob is still there.
expect(await storage.loadBinaryBlob('_system/vector-index/main.dkann')).not.toBeNull()
})
it('unregistering the family makes its members deletable again', async () => {
await seedVectorFamily()
await storage.unregisterDerivedFamily('vector-base')
await expect(storage.deleteBinaryBlob('_system/vector-index/main.dkann')).resolves.toBeUndefined()
expect(await storage.loadBinaryBlob('_system/vector-index/main.dkann')).toBeNull()
})
it('an undeclared blob is still deletable (contract does not lock everything)', async () => {
await seedVectorFamily()
await storage.saveBinaryBlob('graph-lsm/source/sstable-1', Buffer.from([9]))
await expect(storage.deleteBinaryBlob('graph-lsm/source/sstable-1')).resolves.toBeUndefined()
})
it('a transient (*.tmp.*) is deletable even if it sits under a protected namespace', async () => {
await storage.registerDerivedFamily({
name: 'vector-ns',
members: ['_system/vector-index/'],
namespace: true
})
await storage.saveBinaryBlob('_system/vector-index/main.dkann.tmp.123', Buffer.from([1]))
await expect(
storage.deleteBinaryBlob('_system/vector-index/main.dkann.tmp.123')
).resolves.toBeUndefined()
})
it('a namespace family protects a growing child key', async () => {
await storage.registerDerivedFamily({
name: 'vector-segments',
members: ['_system/vector-index/'],
namespace: true
})
await storage.saveBinaryBlob('_system/vector-index/seg-42', Buffer.from([7]))
await expect(storage.deleteBinaryBlob('_system/vector-index/seg-42')).rejects.toBeInstanceOf(
ProtectedArtifactError
)
})
it('checkDerivedFamiliesPresent() names a member deleted outside the write path', async () => {
await seedVectorFamily()
// Simulate an EXTERNAL deleter (bypasses the in-process refusal): drop a
// member straight from the underlying store.
;(storage as any).blobStore.delete('_system/vector-index/main.slotmap')
const incomplete = await storage.checkDerivedFamiliesPresent()
expect(incomplete).toHaveLength(1)
expect(incomplete[0].name).toBe('vector-base')
expect(incomplete[0].missing).toContain('_system/vector-index/main.slotmap')
expect(incomplete[0].rebuildable).toBe(true)
})
it('no families registered → deleteBinaryBlob behaves exactly as before (no enforcement)', async () => {
await storage.saveBinaryBlob('some/blob', Buffer.from([1]))
await expect(storage.deleteBinaryBlob('some/blob')).resolves.toBeUndefined()
})
})
describe('registered-blob family protection survives a reopen (FileSystemStorage)', () => {
let dir: string
beforeEach(() => {
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'brainy-regblob-'))
})
afterEach(() => fs.rmSync(dir, { recursive: true, force: true }))
it('a family declared in one session protects members after reopen', async () => {
const s1: any = new FileSystemStorage(dir)
await s1.init()
await s1.saveBinaryBlob('_system/vector-index/main.dkann', Buffer.from([1]))
await s1.registerDerivedFamily({ name: 'vector-base', members: ['_system/vector-index/main.dkann'] })
// Reopen — the registry is loaded from _system/derived-artifacts.json.
const s2: any = new FileSystemStorage(dir)
await s2.init()
expect(await s2.listDerivedFamilies()).toHaveLength(1)
await expect(s2.deleteBinaryBlob('_system/vector-index/main.dkann')).rejects.toBeInstanceOf(
ProtectedArtifactError
)
// removeRawPrefix nuking the vector dir is also refused.
await expect(s2.removeRawPrefix('_blobs/_system/vector-index')).rejects.toBeInstanceOf(
ProtectedArtifactError
)
})
})