diff --git a/tests/configs/vitest.perf.config.ts b/tests/configs/vitest.perf.config.ts index 6936a71c..6c0f2d1b 100644 --- a/tests/configs/vitest.perf.config.ts +++ b/tests/configs/vitest.perf.config.ts @@ -57,14 +57,7 @@ export default defineConfig({ // otherwise-correctness integration suite (self-skipped everywhere // else via BRAINY_PERF_LANE). Stays in the integration gate's // include too, so every OTHER test in the file keeps running there. - 'tests/integration/storage-batch-operations.test.ts', - // Same pattern: one wall-clock budget case (100-file write + readdir, - // 5.5s budget) inside an otherwise-correctness VFS unit suite - // (self-skipped everywhere else via BRAINY_PERF_LANE — see - // tests/vfs/vfs.unit.test.ts's 'Performance > should handle many - // files efficiently'). Stays in the unit gate's *.unit.test.ts match - // too, so every OTHER test in the file keeps running there. - 'tests/vfs/vfs.unit.test.ts' + 'tests/integration/storage-batch-operations.test.ts' ], reporters: process.env.CI ? ['dot'] : ['basic'], diff --git a/tests/integration/graphIndex-pagination.test.ts b/tests/integration/graphIndex-pagination.test.ts index 8ad4d6d8..32a7673c 100644 --- a/tests/integration/graphIndex-pagination.test.ts +++ b/tests/integration/graphIndex-pagination.test.ts @@ -9,34 +9,9 @@ * 8.0 BigInt boundary: entity ints in (resolved via the metadata index's * idMapper), entity/verb ints out (`bigint[]`). Entity ints map back to UUIDs * via `idMapper.getUuid(Number(int))`; verb ints via `verbIntsToIds()`. - * - * COST NOTE (2026-09): this file's `beforeEach` used to recreate a fresh - * FileSystemStorage-backed Brainy plus 51 real-embedded entities before - * EVERY one of the 18 tests below (~950 add()/relate() calls total, each - * paying the real ONNX embedder — the whole file walled ~328s). Fixed - * without touching a single assertion: - * - * (1) `vector: []` on every add() below — these tests exercise graph - * pagination, never similarity, so a pre-supplied vector is honest, not - * a shortcut: `add()`'s `params.vector || (await this.embed(...))` never - * calls the embedder once `vector` is present, even the sanctioned - * unvectored `[]` shape (see brainy.ts's add(), the zero-norm-law - * comment) — and the `vector.length > 0` gate on dimension-pinning means - * `[]` never poisons `this.dimensions` for later real embeds. - * (2) `storage: { type: 'memory' }` instead of the 'auto' default - * (FileSystemStorage at ./brainy-data) — real disk I/O the pagination - * assertions never needed, and it sidesteps tests/setup.ts's global - * per-test `rm -rf brainy-data`, which would otherwise corrupt a brain - * shared across a describe's beforeAll out from under it. - * (3) the base fixture (one central hub + 50 outgoing-edge neighbors) now - * builds ONCE per describe (`beforeAll`) instead of once per test — safe - * because no test in a given describe block mutates the shared fixture - * in a way an earlier sibling test's assertion depends on (the one - * mutating case, the incoming-direction test, is the LAST test in its - * describe). */ -import { describe, it, expect, beforeAll, afterAll } from 'vitest' +import { describe, it, expect, beforeEach } from 'vitest' import { Brainy } from '../../src/brainy.js' import { NounType, VerbType } from '../../src/types/graphTypes.js' @@ -64,21 +39,14 @@ describe('GraphAdjacencyIndex Pagination', () => { .map((i) => idMapper().getUuid(Number(i))) .filter((u: string | undefined): u is string => u !== undefined) - /** - * Builds one central hub + 50 neighbor entities (all outgoing edges from - * the hub), unvectored and on in-memory storage (see the file header). - * Assigns the describe-scoped `brain`/`centralId`/`neighborIds` above; - * called once per describe via `beforeAll`, not once per test. - */ - async function buildFixture(): Promise { + beforeEach(async () => { brain = new Brainy({ requireSubtype: false }) - await brain.init({ storage: { type: 'memory' } }) + await brain.init() // Create central entity centralId = await brain.add({ data: { name: 'Central Hub' }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) // Create 50 neighbor entities with relationships @@ -86,8 +54,7 @@ describe('GraphAdjacencyIndex Pagination', () => { for (let i = 0; i < 50; i++) { const neighborId = await brain.add({ data: { name: `Neighbor ${i}`, index: i }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) neighborIds.push(neighborId) @@ -98,14 +65,9 @@ describe('GraphAdjacencyIndex Pagination', () => { type: VerbType.RelatesTo }) } - } + }) describe('getNeighbors() Pagination', () => { - beforeAll(buildFixture) - afterAll(async () => { - await brain?.close() - }) - it('should return all neighbors without pagination', async () => { const neighborInts = await graphIndex().getNeighbors(entityInt(centralId)) const neighbors = intsToUuids(neighborInts) @@ -187,8 +149,7 @@ describe('GraphAdjacencyIndex Pagination', () => { // Create some incoming relationships const sourceId = await brain.add({ data: { name: 'Source' }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) await brain.relate({ @@ -208,11 +169,6 @@ describe('GraphAdjacencyIndex Pagination', () => { }) describe('getVerbIdsBySource() Pagination', () => { - beforeAll(buildFixture) - afterAll(async () => { - await brain?.close() - }) - it('should return all verb ints without pagination and resolve them back to ids', async () => { const verbInts: bigint[] = await graphIndex().getVerbIdsBySource(entityInt(centralId)) @@ -267,11 +223,6 @@ describe('GraphAdjacencyIndex Pagination', () => { }) describe('getVerbIdsByTarget() Pagination', () => { - beforeAll(buildFixture) - afterAll(async () => { - await brain?.close() - }) - it('should return all verb ints targeting an entity', async () => { // Pick a neighbor that's a target of relationships const targetId = neighborIds[0] @@ -285,16 +236,14 @@ describe('GraphAdjacencyIndex Pagination', () => { // Create entity with many incoming relationships const popularTarget = await brain.add({ data: { name: 'Popular Target' }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) // Create 30 relationships pointing to it for (let i = 0; i < 30; i++) { const sourceId = await brain.add({ data: { name: `Source ${i}` }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) await brain.relate({ from: sourceId, @@ -318,11 +267,6 @@ describe('GraphAdjacencyIndex Pagination', () => { }) describe('Performance with Pagination', () => { - beforeAll(buildFixture) - afterAll(async () => { - await brain?.close() - }) - it('should maintain sub-5ms performance with pagination', async () => { const central = entityInt(centralId) @@ -341,17 +285,11 @@ describe('GraphAdjacencyIndex Pagination', () => { }) describe('Real-World Use Cases', () => { - beforeAll(buildFixture) - afterAll(async () => { - await brain?.close() - }) - it('should efficiently paginate through high-degree node', async () => { // Simulate popular entity with 100+ relationships const hub = await brain.add({ data: { name: 'Popular Hub' }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) // Create 100 relationships @@ -359,8 +297,7 @@ describe('GraphAdjacencyIndex Pagination', () => { for (let i = 0; i < 100; i++) { const targetId = await brain.add({ data: { name: `Target ${i}` }, - type: NounType.Thing, - vector: [] + type: NounType.Thing }) targetIds.push(targetId) await brain.relate({ diff --git a/tests/integration/triple-intelligence-correctness.test.ts b/tests/integration/triple-intelligence-correctness.test.ts deleted file mode 100644 index 53848d1a..00000000 --- a/tests/integration/triple-intelligence-correctness.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Triple Intelligence Correctness Tests - * - * Moved out of tests/performance/triple-intelligence-scale.test.ts (the - * perf-lane split excludes the whole `tests/performance/**` directory from - * the correctness gate — see vitest.config.ts's exclude list — which left - * this describe's 4 tests running nowhere by default). Every `expect(...)` - * below is byte-for-byte what the original file asserted — nothing here - * changes an assertion. - * - * Fixture-only fixes were required to make this run at all against the - * current engine — exactly the kind of drift that running nowhere hides - * (tsconfig.json excludes `**\/*.test.ts`, so tsc never typechecked this file - * either, and nothing else exercised it since the perf-lane split): - * `addMany()` now takes `{ items }`, not a bare array; `relate()`'s `type` is - * a `VerbType` enum value, not the string `'related'`; `add()`'s `type` is - * required at runtime (`type: NounType.Document` added — no test asserts on - * it); the `where` filter spells its operators bare (`gte`, not `$gte`); - * `storage: { type: 'memory' }` avoids tests/setup.ts's global per-test - * `rm -rf brainy-data` tearing the writer lock out from under this describe's - * shared (beforeAll) brain between tests. - * - * Two of the four tests are `it.skip` with a defect filed in a comment above - * each, not patched: `graphTraversal()` bypasses the 8.0 id-normalization law - * (a natural-key `connected.from` never resolves), and `vectorSearch()` - * throws a hardcoded O(log n) wall-time guard that a 6-row fixture's cold - * WASM/JIT cost blows through by 6-15x — both genuine TripleIntelligenceSystem - * defects the original file never surfaced because it ran (when it ran at - * all, in-process) after a 1M-item warm-up suite. See each skip's comment. - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest' -import { Brainy } from '../../src/brainy.js' -import { TripleIntelligenceSystem } from '../../src/triple/TripleIntelligenceSystem.js' -import { NounType, VerbType } from '../../src/types/graphTypes.js' - -describe('Triple Intelligence Correctness', () => { - let brain: Brainy - let triple: TripleIntelligenceSystem - - beforeAll(async () => { - brain = new Brainy({ requireSubtype: false }) - await brain.init({ - enableMetadataIndex: true, - enableGraphIndex: true, - // Memory, not the 'auto' default's FileSystemStorage at ./brainy-data: - // tests/setup.ts's global per-test `rm -rf brainy-data` was ripping the - // writer lock out from under this describe's shared (beforeAll) brain - // between tests ("Writer fence lost" on close) — a store this test - // never needed to touch disk for. - storage: { type: 'memory' } - }) - - // Add test data with known patterns - const testData = [ - { id: 'doc1', data: 'Machine learning algorithms', type: NounType.Document, metadata: { topic: 'AI', year: 2023 } }, - { id: 'doc2', data: 'Deep learning neural networks', type: NounType.Document, metadata: { topic: 'AI', year: 2024 } }, - { id: 'doc3', data: 'Natural language processing', type: NounType.Document, metadata: { topic: 'AI', year: 2023 } }, - { id: 'doc4', data: 'Computer vision applications', type: NounType.Document, metadata: { topic: 'AI', year: 2024 } }, - { id: 'doc5', data: 'Quantum computing basics', type: NounType.Document, metadata: { topic: 'Physics', year: 2023 } }, - { id: 'doc6', data: 'Blockchain technology', type: NounType.Document, metadata: { topic: 'Crypto', year: 2024 } } - ] - - await brain.addMany({ items: testData }) - - // Add relationships - await brain.relate({ from: 'doc1', to: 'doc2', type: VerbType.RelatedTo }) - await brain.relate({ from: 'doc2', to: 'doc3', type: VerbType.RelatedTo }) - await brain.relate({ from: 'doc3', to: 'doc4', type: VerbType.RelatedTo }) - - triple = brain.getTripleIntelligence() - }) - - afterAll(async () => { - await brain?.close() - }) - - it('should return exact matches for field queries', async () => { - const results = await triple.find({ - where: { topic: 'AI' }, - limit: 10 - }) - - expect(results).toHaveLength(4) - for (const result of results) { - expect(result.metadata.topic).toBe('AI') - } - }) - - it('should handle range queries correctly', async () => { - const results = await triple.find({ - where: { year: { gte: 2024 } }, - limit: 10 - }) - - expect(results).toHaveLength(3) - for (const result of results) { - expect(result.metadata.year).toBeGreaterThanOrEqual(2024) - } - }) - - // SKIPPED — genuine TripleIntelligenceSystem defect, out of test-hygiene - // scope, filed rather than patched: graphTraversal() (TripleIntelligenceSystem.ts) - // calls storage.getNoun(id) / graphIndex.getNeighbors(id) directly with the - // caller's raw `connected.from` string, bypassing the 8.0 id-normalization - // law (Brainy.add() coerces a natural-key id like 'doc1' to a stable v5 - // UUID and stores the original only for translation at the public API - // surface — see coerceNewEntityId in brainy.ts). A caller passing a - // natural-key id here gets storage.getNoun('doc1') → undefined; every - // result's `id` is whatever raw string seeded the BFS queue, so results - // can never match by natural key either. Reproduces identically against - // the pre-move fixture and code — not introduced by this file's move, just - // never exercised (this describe ran nowhere since the perf-lane split). - it.skip('should traverse graph relationships', async () => { - const results = await triple.find({ - connected: { from: 'doc1', depth: 2 }, - limit: 10 - }) - - // Should find doc1, doc2 (depth 1), and doc3 (depth 2) - const ids = results.map(r => r.id) - expect(ids).toContain('doc1') - expect(ids).toContain('doc2') - expect(ids).toContain('doc3') - - // Check depth values - const doc1Result = results.find(r => r.id === 'doc1') - const doc2Result = results.find(r => r.id === 'doc2') - const doc3Result = results.find(r => r.id === 'doc3') - - expect(doc1Result?.depth).toBe(0) - expect(doc2Result?.depth).toBe(1) - expect(doc3Result?.depth).toBe(2) - }) - - // SKIPPED — genuine TripleIntelligenceSystem defect, out of test-hygiene - // scope, filed rather than patched: vectorSearch() (TripleIntelligenceSystem.ts) - // throws `Vector search O(log n) violation` when elapsed wall time exceeds - // `log2(hnswIndex.size()) * 5 * 2` — on a 6-row fixture that bound is - // ~25.8ms, which the real cost of a WASM/Candle embed call plus first-call - // JIT/cache warmup blows through by 6-15x (measured 166-375ms across - // repeated runs) — a hardcoded constant that assumes an already-warm, - // presumably-native runtime, not this environment. The ORIGINAL file never - // hit this: it ran after 'Triple Intelligence Performance at Scale', whose - // 1M-item setup + many queries left the embedder/HNSW thoroughly warm by - // the time this describe's tests ran in the same process — an accidental - // dependency on a sibling suite, not a property of this test. Standalone, - // cold, it is inherently flaky by the SUT's own design, not fixable by - // fixture changes (enlarging the fixture only pushes elapsed time up - // alongside the threshold's log-scaled — not linear — growth). - it.skip('should combine signals with proper fusion', async () => { - const results = await triple.find({ - similar: 'deep learning', - where: { topic: 'AI' }, - limit: 3 - }, { - fusion: { - strategy: 'rrf', - weights: { vector: 0.7, field: 0.3 } - } - }) - - // doc2 should rank highest (matches both signals) - expect(results[0].id).toBe('doc2') - expect(results[0].fusionScore).toBeGreaterThan(0) - - // All results should have AI topic - for (const result of results) { - expect(result.metadata.topic).toBe('AI') - } - }) -}) diff --git a/tests/performance/triple-intelligence-scale.test.ts b/tests/performance/triple-intelligence-scale.test.ts index 1db7fc80..6687decd 100644 --- a/tests/performance/triple-intelligence-scale.test.ts +++ b/tests/performance/triple-intelligence-scale.test.ts @@ -352,8 +352,106 @@ describe('Triple Intelligence Performance at Scale', () => { }) }) -// The former 'Triple Intelligence Correctness' describe (4 tests, no timing -// assertions) moved to tests/integration/triple-intelligence-correctness.test.ts -// so it runs in the default correctness gate — this whole directory -// (tests/performance/**) is excluded from that gate (see vitest.config.ts), -// which had silently stopped running those 4 tests after the perf-lane split. \ No newline at end of file +describe('Triple Intelligence Correctness', () => { + let brain: Brainy + let triple: TripleIntelligenceSystem + + beforeAll(async () => { + brain = new Brainy({ requireSubtype: false }) + await brain.init({ + enableMetadataIndex: true, + enableGraphIndex: true + }) + + // Add test data with known patterns + const testData = [ + { id: 'doc1', data: 'Machine learning algorithms', metadata: { topic: 'AI', year: 2023 } }, + { id: 'doc2', data: 'Deep learning neural networks', metadata: { topic: 'AI', year: 2024 } }, + { id: 'doc3', data: 'Natural language processing', metadata: { topic: 'AI', year: 2023 } }, + { id: 'doc4', data: 'Computer vision applications', metadata: { topic: 'AI', year: 2024 } }, + { id: 'doc5', data: 'Quantum computing basics', metadata: { topic: 'Physics', year: 2023 } }, + { id: 'doc6', data: 'Blockchain technology', metadata: { topic: 'Crypto', year: 2024 } } + ] + + await brain.addMany(testData) + + // Add relationships + await brain.relate({ from: 'doc1', to: 'doc2', type: 'related' }) + await brain.relate({ from: 'doc2', to: 'doc3', type: 'related' }) + await brain.relate({ from: 'doc3', to: 'doc4', type: 'related' }) + + triple = brain.getTripleIntelligence() + }) + + afterAll(async () => { + await brain?.close() + }) + + it('should return exact matches for field queries', async () => { + const results = await triple.find({ + where: { topic: 'AI' }, + limit: 10 + }) + + expect(results).toHaveLength(4) + for (const result of results) { + expect(result.metadata.topic).toBe('AI') + } + }) + + it('should handle range queries correctly', async () => { + const results = await triple.find({ + where: { year: { $gte: 2024 } }, + limit: 10 + }) + + expect(results).toHaveLength(3) + for (const result of results) { + expect(result.metadata.year).toBeGreaterThanOrEqual(2024) + } + }) + + it('should traverse graph relationships', async () => { + const results = await triple.find({ + connected: { from: 'doc1', depth: 2 }, + limit: 10 + }) + + // Should find doc1, doc2 (depth 1), and doc3 (depth 2) + const ids = results.map(r => r.id) + expect(ids).toContain('doc1') + expect(ids).toContain('doc2') + expect(ids).toContain('doc3') + + // Check depth values + const doc1Result = results.find(r => r.id === 'doc1') + const doc2Result = results.find(r => r.id === 'doc2') + const doc3Result = results.find(r => r.id === 'doc3') + + expect(doc1Result?.depth).toBe(0) + expect(doc2Result?.depth).toBe(1) + expect(doc3Result?.depth).toBe(2) + }) + + it('should combine signals with proper fusion', async () => { + const results = await triple.find({ + similar: 'deep learning', + where: { topic: 'AI' }, + limit: 3 + }, { + fusion: { + strategy: 'rrf', + weights: { vector: 0.7, field: 0.3 } + } + }) + + // doc2 should rank highest (matches both signals) + expect(results[0].id).toBe('doc2') + expect(results[0].fusionScore).toBeGreaterThan(0) + + // All results should have AI topic + for (const result of results) { + expect(result.metadata.topic).toBe('AI') + } + }) +}) \ No newline at end of file diff --git a/tests/vfs/vfs.unit.test.ts b/tests/vfs/vfs.unit.test.ts index b4024155..4b4ba8d2 100644 --- a/tests/vfs/vfs.unit.test.ts +++ b/tests/vfs/vfs.unit.test.ts @@ -389,14 +389,7 @@ describe('VirtualFileSystem - Production Tests', () => { }) describe('Performance', () => { - it('should handle many files efficiently', async (ctx) => { - // Wall-clock budget assertion — belongs to the perf lane (npm run - // test:perf), not the correctness gate: 121ms alone but 16.5s under - // the gate's sibling-file contention, a flake the code never caused - // (same pattern as storage-batch-operations.test.ts's batch-vs- - // individual timing case). - ctx.skip(!process.env.BRAINY_PERF_LANE, 'wall-clock budget assertion — runs only under the perf lane (npm run test:perf)') - + it('should handle many files efficiently', async () => { const dir = '/performance-test' await vfs.mkdir(dir)