test(8.0): asOf() error-path spot-checks + find() triple-composition correctness + scale-bench harness
- db-mvcc proof 9: asOf(-1|1.5|future) → RangeError, bad snapshot path → descriptive error, use-after-release() throws on get/find/related (Y.15 error-path coverage). - find-triple-composition: proves vector ∩ metadata ∩ graph returns exactly the entity satisfying all three and excludes those failing any one (decoys, wrong category). - find-composition-scale.js: parameterized latency harness (vector/metadata/graph/ vector+metadata/triple) with non-empty asserts; precomputed vectors, no model load.
This commit is contained in:
parent
f12ca68b82
commit
af96064655
3 changed files with 273 additions and 0 deletions
|
|
@ -1155,4 +1155,33 @@ describe('8.0 Db API — generational MVCC', () => {
|
|||
await second.release()
|
||||
await third.release()
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// 9. asOf() / released-Db error paths (Y.15 spot-checks)
|
||||
// ==========================================================================
|
||||
it('proof 9 — asOf() rejects out-of-range targets and a released Db throws on every read', async () => {
|
||||
const brain = await openMemoryBrain()
|
||||
await (
|
||||
await brain.transact([
|
||||
{ op: 'add', id: uid('asof-err'), type: NounType.Document, data: 'x', metadata: { v: 1 } }
|
||||
])
|
||||
).release()
|
||||
const current = brain.generation()
|
||||
|
||||
// Negative, non-integer, and future generations are honest RangeErrors —
|
||||
// never a silent clamp to a neighbouring generation.
|
||||
await expect(brain.asOf(-1)).rejects.toThrow(RangeError)
|
||||
await expect(brain.asOf(1.5)).rejects.toThrow(RangeError)
|
||||
await expect(brain.asOf(current + 999)).rejects.toThrow(RangeError)
|
||||
|
||||
// A string that is not a snapshot directory is a descriptive error, not a crash.
|
||||
await expect(brain.asOf('/no/such/snapshot/dir')).rejects.toThrow(/not a snapshot directory/)
|
||||
|
||||
// Use-after-release is rejected on every read method, naming the released generation.
|
||||
const db = brain.now()
|
||||
await db.release()
|
||||
await expect(db.get(uid('asof-err'))).rejects.toThrow(/released Db/)
|
||||
await expect(db.find({ where: { v: 1 } })).rejects.toThrow(/released Db/)
|
||||
await expect(db.related(uid('asof-err'))).rejects.toThrow(/released Db/)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
67
tests/integration/find-triple-composition.test.ts
Normal file
67
tests/integration/find-triple-composition.test.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* @module tests/integration/find-triple-composition
|
||||
* @description Correctness of Triple-Intelligence composition in `find()`: a single
|
||||
* query combining vector similarity, metadata filtering, and graph traversal must
|
||||
* return exactly the entities satisfying ALL three constraints — never silently drop
|
||||
* a row that each pairwise query would return. Guards the headline 8.0 query surface.
|
||||
*/
|
||||
import { describe, it, expect, afterEach } from 'vitest'
|
||||
import { Brainy } from '../../src/brainy.js'
|
||||
import { NounType, VerbType } from '../../src/types/graphTypes.js'
|
||||
|
||||
/** Deterministic 384-dim vector; same seed ⇒ identical vector (distance 0). */
|
||||
function vec(seed: number): number[] {
|
||||
return Array.from({ length: 384 }, (_, i) => ((seed * 31 + i * 7) % 100) / 100)
|
||||
}
|
||||
|
||||
describe('find() Triple-Intelligence composition', () => {
|
||||
const brains: Brainy[] = []
|
||||
afterEach(async () => {
|
||||
for (const b of brains.splice(0)) await b.close()
|
||||
})
|
||||
async function open(): Promise<Brainy> {
|
||||
const b = new Brainy({ storage: { type: 'memory' }, eagerEmbeddings: false, requireSubtype: false, silent: true })
|
||||
await b.init()
|
||||
brains.push(b)
|
||||
return b
|
||||
}
|
||||
|
||||
it('returns the entity satisfying vector ∩ metadata ∩ graph; excludes those failing any one', async () => {
|
||||
const brain = await open()
|
||||
const hub = await brain.add({ vector: vec(1), type: NounType.Document, metadata: { role: 'hub', category: 9 } })
|
||||
// Connected neighbours of the hub:
|
||||
const n1 = await brain.add({ vector: vec(10), type: NounType.Document, metadata: { role: 'n1', category: 3 } })
|
||||
const n2 = await brain.add({ vector: vec(11), type: NounType.Document, metadata: { role: 'n2', category: 3 } })
|
||||
const n3 = await brain.add({ vector: vec(12), type: NounType.Document, metadata: { role: 'n3', category: 7 } })
|
||||
// Decoys: vector-identical to n1 (seed 10) and category 3, but NOT connected to the hub.
|
||||
const decoys: string[] = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
decoys.push(await brain.add({ vector: vec(10), type: NounType.Document, metadata: { role: 'decoy', category: 3 } }))
|
||||
}
|
||||
await brain.relate({ from: hub, to: n1, type: VerbType.References, weight: 1 })
|
||||
await brain.relate({ from: hub, to: n2, type: VerbType.References, weight: 1 })
|
||||
await brain.relate({ from: hub, to: n3, type: VerbType.References, weight: 1 })
|
||||
|
||||
const roles = (r: Array<{ metadata?: any }>) => new Set(r.map((x) => x.metadata?.role))
|
||||
|
||||
// Sanity: each single/pairwise constraint behaves.
|
||||
const graphOut = await brain.find({
|
||||
connected: { from: hub, via: VerbType.References, direction: 'out', depth: 1 }, limit: 50
|
||||
})
|
||||
expect(roles(graphOut)).toEqual(new Set(['n1', 'n2', 'n3']))
|
||||
|
||||
// Full triple: near vec(10) (n1 + decoys) AND category 3 (n1, n2, decoys) AND connected to hub (n1,n2,n3).
|
||||
// The intersection is exactly {n1}. Decoys fail graph; n2 fails vector(distance); n3 fails category.
|
||||
const triple = await brain.find({
|
||||
vector: vec(10),
|
||||
where: { category: 3 },
|
||||
connected: { from: hub, via: VerbType.References, direction: 'out', depth: 1 },
|
||||
limit: 50
|
||||
})
|
||||
const got = roles(triple)
|
||||
expect(got.size).toBeGreaterThan(0) // composition must not be empty
|
||||
expect(got.has('n1')).toBe(true) // n1 satisfies all three — must be present
|
||||
expect(got.has('decoy')).toBe(false) // decoys fail the graph constraint
|
||||
expect(got.has('hub')).toBe(false) // hub is category 9
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue