brainy/tests/integration/vfs-performance-v5.11.1.test.ts
David Snelling 780fb6444b feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1)
Brainy 8.0 makes subtype required by default on every public write path
(`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`,
import). Per the locked C-1 contract, every entity and relation gets a
non-empty subtype string by the time the storage layer sees it.

OPT-OUT REMAINS FULLY SUPPORTED

The runtime flag is still consumer-controlled. Three opt-out paths
cover migration / legacy fixtures / typed escape:

- `new Brainy({ requireSubtype: false })` — last-resort: turn off the
  contract entirely. Recommended only for migration windows or test
  fixtures that legitimately can't supply a subtype.
- `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` —
  per-type allowlist: strict everywhere except the listed types.
- `brain.requireSubtype(type, options)` — per-type registration with
  optional vocabulary. Composes with the brain-wide flag.

Default is now `true`. Opt-out is explicit and documented; nothing
silently degrades.

TEST SWEEP

Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call
site across 120 test files. Three sed patterns covered the shapes:
  - `new Brainy({` → `new Brainy({ requireSubtype: false,`
  - `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,`
  - `new Brainy()` → `new Brainy({ requireSubtype: false })`

tests/helpers/test-factory.ts → createTestConfig() defaults
`requireSubtype: false` so test files using the helper inherit the
opt-out without per-site edits.

The test sites that DO exercise subtype semantics (the
subtype-and-facets suite, the strict-mode-self-test suite, the verb-
subtype-and-enforcement suite, etc.) already pass real subtypes — they
were the 7.30.x acceptance tests for this contract. Those tests
continue to pass unchanged.

CHANGES

src/brainy.ts
- normalizeConfig() — `requireSubtype` default `false` → `true`.
  Comment refreshed to document the three opt-out paths.

tests/* (120 files)
- Bulk-edited brain construction sites. No functional test changes; the
  opt-out preserves the test author's original intent.

tests/helpers/test-factory.ts
- createTestConfig() base config gains `requireSubtype: false`.

NO-OP for consumers who were already passing subtype on every write.

For consumers who weren't, the upgrade path is one of the three opt-out
forms above. Migration recipe documented in 8.0 release notes (next
commit).

VERIFICATION

- npx tsc --noEmit: clean
- npm test: 1408 / 1409 (same pre-existing race-condition outstanding;
  no other regressions from the flip)
2026-06-09 14:58:25 -07:00

254 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* VFS Performance Integration Tests (v5.11.1)
*
* Verifies that VFS operations are 75%+ faster due to brain.get() optimization.
*
* VFS internally uses brain.get() which now loads metadata-only by default.
* Since VFS operations don't need vectors, they automatically benefit from
* the 76-81% speedup with ZERO code changes.
*
* NO STUBS, NO MOCKS - Real VFS performance testing
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { VirtualFileSystem } from '../../src/vfs/VirtualFileSystem.js'
import { mkdtempSync, rmSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
describe('VFS Performance (v5.11.1 Optimization)', () => {
let brain: Brainy
let vfs: VirtualFileSystem
let testDir: string
beforeEach(async () => {
// Create temporary directory for test
testDir = mkdtempSync(join(tmpdir(), 'brainy-vfs-perf-test-'))
// Initialize Brain with FileSystemStorage
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
options: { path: testDir }
},
silent: true
})
await brain.init()
// Initialize VFS
vfs = new VirtualFileSystem(brain)
await vfs.init()
})
afterEach(async () => {
await brain.close()
rmSync(testDir, { recursive: true, force: true })
})
describe('readFile() Performance', () => {
it('should complete in <20ms per file', async () => {
// Create test file
await vfs.writeFile('/test.txt', Buffer.from('test content for performance'))
// Warm up (populate caches)
await vfs.readFile('/test.txt')
// Measure performance
const iterations = 50
const start = performance.now()
for (let i = 0; i < iterations; i++) {
await vfs.readFile('/test.txt')
}
const avgTime = (performance.now() - start) / iterations
// Expect <20ms per read (was ~53ms in v5.11.0)
expect(avgTime).toBeLessThan(20)
console.log(`[VFS Performance] readFile: ${avgTime.toFixed(2)}ms (target: <20ms, was ~53ms in v5.11.0)`)
})
it('should be 75%+ faster than v5.11.0 baseline', async () => {
// This test verifies the optimization works
// We can't test against actual v5.11.0, but we can verify
// the metadata-only path is being used
await vfs.writeFile('/test.txt', Buffer.from('test content'))
// Warm up
await vfs.readFile('/test.txt')
// Measure current performance
const iterations = 50
const start = performance.now()
for (let i = 0; i < iterations; i++) {
await vfs.readFile('/test.txt')
}
const avgTime = (performance.now() - start) / iterations
// v5.11.0 baseline was ~53ms, v5.11.1 should be ~10-15ms
// That's a 75%+ improvement
const expectedSlowTime = 53 // v5.11.0 baseline
const speedup = ((expectedSlowTime - avgTime) / expectedSlowTime) * 100
// Verify significant speedup
expect(speedup).toBeGreaterThan(70) // Allow some variance
console.log(`[VFS Performance] Speedup vs v5.11.0: ${speedup.toFixed(1)}% (${expectedSlowTime}ms → ${avgTime.toFixed(2)}ms)`)
})
})
describe('readdir() Performance', () => {
it('should complete in <1.5s for 100 files', async () => {
// Create 100 test files
for (let i = 0; i < 100; i++) {
await vfs.writeFile(`/file${i}.txt`, Buffer.from(`content ${i}`))
}
// Warm up
await vfs.readdir('/')
// Measure performance
const start = performance.now()
await vfs.readdir('/')
const time = performance.now() - start
// Expect <1.5s for 100 files (was ~5.3s in v5.11.0)
// 100 files × 15ms = 1500ms
expect(time).toBeLessThan(1500)
console.log(`[VFS Performance] readdir(100 files): ${time.toFixed(0)}ms (target: <1500ms, was ~5300ms in v5.11.0)`)
})
it('should scale linearly with file count', async () => {
// Create 10, 20, 30 files and measure
const measurements = []
for (const count of [10, 20, 30]) {
// Create files
for (let i = 0; i < count; i++) {
await vfs.writeFile(`/test-${count}-${i}.txt`, Buffer.from(`content ${i}`))
}
// Measure
const start = performance.now()
await vfs.readdir('/')
const time = performance.now() - start
measurements.push({ count, time })
}
// Verify linear scaling (not quadratic)
// time(20) / time(10) should be ~2
// time(30) / time(10) should be ~3
// With optimization, scaling may be better than linear due to caching
const ratio20 = measurements[1].time / measurements[0].time
const ratio30 = measurements[2].time / measurements[0].time
expect(ratio20).toBeGreaterThan(1.0) // At least some scaling
expect(ratio20).toBeLessThan(3.0) // But not worse than linear
expect(ratio30).toBeGreaterThan(1.0) // At least some scaling (optimization makes this sub-linear!)
expect(ratio30).toBeLessThan(4.0) // But not worse than linear
console.log(`[VFS Performance] Scaling: 10 files=${measurements[0].time.toFixed(0)}ms, 20 files=${measurements[1].time.toFixed(0)}ms (${ratio20.toFixed(1)}x), 30 files=${measurements[2].time.toFixed(0)}ms (${ratio30.toFixed(1)}x)`)
})
})
describe('stat() Performance', () => {
it('should complete in <20ms per file', async () => {
await vfs.writeFile('/test.txt', Buffer.from('test content'))
// Warm up
await vfs.stat('/test.txt')
// Measure performance
const iterations = 50
const start = performance.now()
for (let i = 0; i < iterations; i++) {
await vfs.stat('/test.txt')
}
const avgTime = (performance.now() - start) / iterations
// Expect <20ms per stat (was ~53ms in v5.11.0)
expect(avgTime).toBeLessThan(20)
console.log(`[VFS Performance] stat: ${avgTime.toFixed(2)}ms (target: <20ms, was ~53ms in v5.11.0)`)
})
})
describe('Zero Code Changes Benefit', () => {
it('VFS should automatically benefit from brain.get() optimization', async () => {
// This test verifies that VFS operations use the fast path
// WITHOUT any code changes to VFS itself
await vfs.writeFile('/test.txt', Buffer.from('test content'))
// Warm up (first read can be slower due to initialization)
await vfs.readFile('/test.txt')
await vfs.stat('/test.txt')
// VFS operations should be fast automatically after warmup
const start1 = performance.now()
await vfs.readFile('/test.txt')
const readTime = performance.now() - start1
const start2 = performance.now()
await vfs.stat('/test.txt')
const statTime = performance.now() - start2
// Should be significantly faster than v5.11.0 baseline (53ms)
// Target: <50ms (with some headroom for FileSystemStorage overhead)
expect(readTime).toBeLessThan(50)
expect(statTime).toBeLessThan(50)
console.log(`[VFS Performance] Zero-config benefit: readFile=${readTime.toFixed(2)}ms, stat=${statTime.toFixed(2)}ms (both <50ms, was ~53ms in v5.11.0)`)
})
})
describe('Real-World Scenario', () => {
it('should handle typical file operations efficiently', async () => {
// Simulate real-world usage:
// 1. Create directory structure
// 2. Write files
// 3. Read files
// 4. Check stats
// 5. List directories
const start = performance.now()
// Create structure
await vfs.mkdir('/documents')
await vfs.mkdir('/images')
// Write files
for (let i = 0; i < 10; i++) {
await vfs.writeFile(`/documents/doc${i}.txt`, Buffer.from(`Document ${i}`))
await vfs.writeFile(`/images/img${i}.png`, Buffer.from(`Image ${i} data`))
}
// Read files
for (let i = 0; i < 10; i++) {
await vfs.readFile(`/documents/doc${i}.txt`)
}
// Stat files
for (let i = 0; i < 10; i++) {
await vfs.stat(`/documents/doc${i}.txt`)
}
// List directories
await vfs.readdir('/documents')
await vfs.readdir('/images')
const totalTime = performance.now() - start
// Entire workflow should complete faster than v5.11.0 baseline
// FileSystemStorage: ~2-3s baseline, target <2s with optimization
// This test does: 2 mkdir + 20 writeFile + 10 readFile + 10 stat + 2 readdir
expect(totalTime).toBeLessThan(2500)
console.log(`[VFS Performance] Real-world scenario: ${totalTime.toFixed(0)}ms (target: <2500ms, was ~2-3s in v5.11.0)`)
})
})
})