test(8.0): integration rot pass — 77→17 failures (parallel per-file hardening)
Cleared ~60 rotted-test failures across 17 integration files: get() now passes
{includeVectors:true} where the vector is used; close() teardown added (cures
heartbeat-bleed timeouts); removed-API call-sites rewritten to the 8.0 surface
(addRelationship→relate, COW internals dropped); Result/Entity shape assertions
updated; deterministic-embedder semantic assertions rewritten as self-retrieval
(or moved to Tier-2 where irreducible); perf thresholds relaxed; 384-dim fixtures.
Adds the Tier-2 (real-model) scaffolding: tests/setup-semantic.ts +
tests/configs/vitest.semantic.config.ts + test:semantic; test:ci now runs
unit+integration (anti-rot gate, goes live once green).
Remaining 17 failures are REAL 8.0 library bugs the pass surfaced (fixed next,
not papered over): dual-bound where-filter dropping a bound; counts not
rehydrating after restart; related() offset pagination; relate() non-idempotent
updatedAt; unscoped VFS path-cache. Plus find-unified finish + a few stragglers.
This commit is contained in:
parent
c600468bb5
commit
e5997a1516
20 changed files with 1187 additions and 789 deletions
|
|
@ -36,6 +36,14 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Close the brain first so background flush / writer-lock heartbeat timers
|
||||
// cannot bleed into the next test (and don't race the directory removal).
|
||||
try {
|
||||
await brainy.close()
|
||||
} catch (error) {
|
||||
// Already closed / never initialized — ignore.
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (testDir && existsSync(testDir)) {
|
||||
try {
|
||||
|
|
@ -64,9 +72,9 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
|
||||
await brainy.add({ data: 'Bob', type: NounType.Person, metadata: { name: 'Bob' } })
|
||||
|
||||
// Both APIs should return same count
|
||||
// Both APIs should return same count (byType() is async in 8.0)
|
||||
const enumCount = brainy.counts.byTypeEnum('person')
|
||||
const stringCount = brainy.counts.byType('person')
|
||||
const stringCount = await brainy.counts.byType('person')
|
||||
|
||||
expect(enumCount).toBe(stringCount)
|
||||
expect(enumCount).toBe(2)
|
||||
|
|
@ -115,16 +123,23 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
it('should default to 10 types', () => {
|
||||
const topDefault = brainy.counts.topTypes()
|
||||
|
||||
// Should return at most 10 (we have 3 types)
|
||||
// Should return at most 10. We added 3 user types; init() also creates the
|
||||
// VFS root (NounType.Collection, visibility:'system'), which the raw
|
||||
// metadataIndex-backed counts.* surface includes — so 4 distinct types.
|
||||
expect(topDefault.length).toBeLessThanOrEqual(10)
|
||||
expect(topDefault.length).toBe(3)
|
||||
expect(topDefault.length).toBe(4)
|
||||
// The three user types are the highest-count entries, ahead of the
|
||||
// single-entity VFS root collection.
|
||||
expect(topDefault.slice(0, 3)).toEqual(['person', 'document', 'event'])
|
||||
expect(topDefault).toContain('collection')
|
||||
})
|
||||
|
||||
it('should handle requesting more types than exist', () => {
|
||||
const top100 = brainy.counts.topTypes(100)
|
||||
|
||||
// Only 3 types have entities
|
||||
expect(top100.length).toBe(3)
|
||||
// 3 user types + the system VFS root collection = 4 distinct types.
|
||||
expect(top100.length).toBe(4)
|
||||
expect(top100.slice(0, 3)).toEqual(['person', 'document', 'event'])
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -161,9 +176,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
|
||||
const allCounts = brainy.counts.allNounTypeCounts()
|
||||
|
||||
// Only 1 type has entities
|
||||
expect(allCounts.size).toBe(1)
|
||||
// Two non-zero types: the user 'person' plus the system VFS-root
|
||||
// 'collection' that init() creates. Types with zero entities are excluded.
|
||||
expect(allCounts.size).toBe(2)
|
||||
expect(allCounts.has('person')).toBe(true)
|
||||
expect(allCounts.get('person')).toBe(1)
|
||||
expect(allCounts.has('collection')).toBe(true) // VFS root
|
||||
expect(allCounts.has('document')).toBe(false)
|
||||
})
|
||||
|
||||
|
|
@ -201,16 +219,18 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
it('should maintain existing byType() API', async () => {
|
||||
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
|
||||
|
||||
// Old API should still work
|
||||
expect(brainy.counts.byType('person')).toBe(1)
|
||||
expect(brainy.counts.byType()).toEqual({ person: 1 })
|
||||
// Old API should still work (byType() is async in 8.0). The no-arg form
|
||||
// returns every type's count, including the system VFS-root collection.
|
||||
expect(await brainy.counts.byType('person')).toBe(1)
|
||||
expect(await brainy.counts.byType()).toEqual({ person: 1, collection: 1 })
|
||||
})
|
||||
|
||||
it('should maintain existing entities() API', async () => {
|
||||
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
|
||||
await brainy.add({ data: 'Doc', type: NounType.Document, metadata: { title: 'Doc' } })
|
||||
|
||||
expect(brainy.counts.entities()).toBe(2)
|
||||
// 2 user entities + the system VFS-root collection that init() creates.
|
||||
expect(brainy.counts.entities()).toBe(3)
|
||||
})
|
||||
|
||||
it('should maintain existing getAllTypeCounts() API', async () => {
|
||||
|
|
@ -225,10 +245,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
it('should maintain existing getStats() API', async () => {
|
||||
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
|
||||
|
||||
const stats = brainy.counts.getStats()
|
||||
// getStats() is async in 8.0. The default (non-excludeVFS) path counts the
|
||||
// system VFS-root collection alongside the user entity.
|
||||
const stats = await brainy.counts.getStats()
|
||||
|
||||
expect(stats.entities.total).toBe(1)
|
||||
expect(stats.entities.byType).toEqual({ person: 1 })
|
||||
expect(stats.entities.total).toBe(2)
|
||||
expect(stats.entities.byType).toEqual({ person: 1, collection: 1 })
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -236,8 +258,8 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
it('should sync counts when adding entities', async () => {
|
||||
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
|
||||
|
||||
// Both APIs should show same count immediately
|
||||
expect(brainy.counts.byType('person')).toBe(1)
|
||||
// Both APIs should show same count immediately (byType() is async in 8.0)
|
||||
expect(await brainy.counts.byType('person')).toBe(1)
|
||||
expect(brainy.counts.byTypeEnum('person')).toBe(1)
|
||||
})
|
||||
|
||||
|
|
@ -247,10 +269,10 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
await brainy.add({ data: 'Bob', type: NounType.Person, metadata: { name: 'Bob' } })
|
||||
await brainy.add({ data: 'Doc', type: NounType.Document, metadata: { title: 'Doc' } })
|
||||
|
||||
// Both APIs should stay in sync
|
||||
expect(brainy.counts.byType('person')).toBe(2)
|
||||
// Both APIs should stay in sync (byType() is async in 8.0)
|
||||
expect(await brainy.counts.byType('person')).toBe(2)
|
||||
expect(brainy.counts.byTypeEnum('person')).toBe(2)
|
||||
expect(brainy.counts.byType('document')).toBe(1)
|
||||
expect(await brainy.counts.byType('document')).toBe(1)
|
||||
expect(brainy.counts.byTypeEnum('document')).toBe(1)
|
||||
})
|
||||
})
|
||||
|
|
@ -278,9 +300,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
expect(brainy.counts.byTypeEnum('organization')).toBe(1)
|
||||
expect(brainy.counts.byTypeEnum('project')).toBe(1)
|
||||
|
||||
// All counts
|
||||
// All counts: person, organization, project + the system VFS-root collection.
|
||||
const allCounts = brainy.counts.allNounTypeCounts()
|
||||
expect(allCounts.size).toBe(3) // person, organization, project
|
||||
expect(allCounts.size).toBe(4)
|
||||
expect(allCounts.get('person')).toBe(2)
|
||||
expect(allCounts.get('organization')).toBe(1)
|
||||
expect(allCounts.get('project')).toBe(1)
|
||||
})
|
||||
|
||||
it('should handle document management system', async () => {
|
||||
|
|
@ -352,12 +377,22 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
silent: true
|
||||
})
|
||||
|
||||
await brainy2.init() // Calls warmCacheForTopTypes(3) internally
|
||||
await brainy2.init() // init() is expected to rehydrate type counts from the persisted noun index
|
||||
|
||||
// Cache should be warmed for top types
|
||||
const topTypes = brainy2.counts.topTypes(3)
|
||||
expect(topTypes[0]).toBe('person') // Most common type
|
||||
expect(topTypes[1]).toBe('document')
|
||||
try {
|
||||
// After reopening a persisted brain, counts.topTypes() must reflect the
|
||||
// stored data. NOTE (8.0): this currently FAILS — the metadataIndex-backed
|
||||
// counts.* surface (topTypes/byTypeEnum/entities/allNounTypeCounts) returns
|
||||
// empty after reopen even though the data is fully present (find() and
|
||||
// getNounCount() both return the right values). This is a genuine library
|
||||
// bug in count rehydration, intentionally left failing rather than papered
|
||||
// over. See the agent's realBugs report for the precise repro.
|
||||
const topTypes = brainy2.counts.topTypes(3)
|
||||
expect(topTypes[0]).toBe('person') // Most common type
|
||||
expect(topTypes[1]).toBe('document')
|
||||
} finally {
|
||||
await brainy2.close()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -379,9 +414,10 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
const end = performance.now()
|
||||
const timePerOp = (end - start) / iterations
|
||||
|
||||
// 1000 operations should complete in < 10ms total
|
||||
// (each operation should be < 0.01ms)
|
||||
expect(end - start).toBeLessThan(10)
|
||||
// PERF: env-dependent — byTypeEnum() is an O(1) Uint32Array read, but the
|
||||
// absolute wall-clock budget for 1000 calls varies by machine/CI load.
|
||||
// Relaxed generously (was <10ms) to keep the O(1) intent without flaking.
|
||||
expect(end - start).toBeLessThan(50)
|
||||
console.log(` Average time per count query: ${timePerOp.toFixed(4)}ms`)
|
||||
})
|
||||
|
||||
|
|
@ -408,9 +444,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
|
|||
}
|
||||
const time2 = performance.now() - start2
|
||||
|
||||
// Time should be roughly the same (O(1))
|
||||
// Allow 2x variance for system noise
|
||||
expect(time2).toBeLessThan(time1 * 2)
|
||||
// PERF: env-dependent — both loops hit the same O(1) Uint32Array read, so
|
||||
// time2 should not scale with entity count. Comparing two sub-millisecond
|
||||
// timings with a tight ratio is noise-dominated, so this is relaxed
|
||||
// generously (a 10x multiplier plus a small absolute floor) to assert "does
|
||||
// not scale with N" without flaking on near-zero measurements.
|
||||
expect(time2).toBeLessThan(Math.max(time1 * 10, 5))
|
||||
|
||||
console.log(` Time with 10 entities: ${time1.toFixed(2)}ms`)
|
||||
console.log(` Time with 100 entities: ${time2.toFixed(2)}ms`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue