brainy/tests/typeAwareQueryPlanner.test.ts
David Snelling 823cd5cf1b fix: update all Stage 2 references to Stage 3 CANONICAL type counts
Comprehensive update of type count references from Stage 2 (31 nouns + 40 verbs)
to Stage 3 CANONICAL (42 nouns + 127 verbs) across entire codebase.

Changes (23 files):
- Core architecture: Memory tracking comments, speedup calculations
- Tests: Type count assertions, enum index expectations, memory benchmarks
- CLI: User-visible type count output
- Augmentations: Type detection comments
- Documentation: Architecture docs, guides, performance docs
- Type embeddings: Regenerated for all 169 types (338KB)

Specific updates:
- 31 → 42 (noun count): 38 occurrences
- 40 → 127 (verb count): 24 occurrences
- 124 → 168 bytes (noun array size): 5 occurrences
- 160 → 508 bytes (verb array size): 5 occurrences
- 284 → 676 bytes (total type tracking): 12 occurrences
- Enum indices updated to match Stage 3 reordering

Type embeddings regenerated:
- 42 noun embeddings (64.5 KB)
- 127 verb embeddings (194.8 KB)
- Total: 338 KB (was 108.8 KB)

All constants, arrays, and tests now consistent with Stage 3 taxonomy.

Fixes #v5.5.1-type-count-migration
2025-11-06 09:40:33 -08:00

248 lines
8.2 KiB
TypeScript

/**
* TypeAwareQueryPlanner Tests - Phase 3
*
* Comprehensive tests for query planning and routing strategy selection
* Target: 10 tests covering all planning scenarios
*/
import { describe, it, expect, beforeEach } from 'vitest'
import {
TypeAwareQueryPlanner,
planQuery,
getQueryPlanner
} from '../src/query/typeAwareQueryPlanner.js'
import { TypeInferenceSystem } from '../src/query/typeInference.js'
import { NounType } from '../src/types/graphTypes.js'
describe('TypeAwareQueryPlanner', () => {
let planner: TypeAwareQueryPlanner
beforeEach(() => {
planner = new TypeAwareQueryPlanner()
})
// ========== Routing Tests (4 tests) ==========
describe('Routing Strategy Selection', () => {
it('should use single-type routing for high confidence queries', () => {
const plan = planner.planQuery('Find engineers')
expect(plan.routing).toBe('single-type')
expect(plan.targetTypes.length).toBe(1)
expect(plan.targetTypes[0]).toBe(NounType.Person)
expect(plan.confidence).toBeGreaterThanOrEqual(0.8)
expect(plan.estimatedSpeedup).toBeGreaterThan(10) // 42/1 types
})
it('should use multi-type routing for multiple high-confidence types', () => {
const plan = planner.planQuery('employees at tech companies')
expect(plan.routing).toBe('multi-type')
expect(plan.targetTypes.length).toBeGreaterThanOrEqual(2)
expect(plan.targetTypes.length).toBeLessThanOrEqual(5)
expect(plan.targetTypes).toContain(NounType.Person)
expect(plan.targetTypes).toContain(NounType.Organization)
expect(plan.estimatedSpeedup).toBeGreaterThan(1)
expect(plan.estimatedSpeedup).toBeLessThanOrEqual(42)
})
it('should use all-types routing for low confidence queries', () => {
const plan = planner.planQuery('show me stuff')
expect(plan.routing).toBe('all-types')
expect(plan.targetTypes.length).toBe(42) // All noun types
expect(plan.estimatedSpeedup).toBe(1.0) // No speedup
expect(plan.confidence).toBeLessThan(0.6)
})
it('should use all-types routing for empty queries', () => {
const plan = planner.planQuery('')
expect(plan.routing).toBe('all-types')
expect(plan.targetTypes.length).toBe(42)
expect(plan.estimatedSpeedup).toBe(1.0)
expect(plan.reasoning).toContain('Empty query')
})
})
// ========== Plan Generation Tests (3 tests) ==========
describe('Query Plan Generation', () => {
it('should generate complete query plan with all fields', () => {
const plan = planner.planQuery('Find software engineers')
expect(plan).toHaveProperty('originalQuery')
expect(plan).toHaveProperty('inferredTypes')
expect(plan).toHaveProperty('routing')
expect(plan).toHaveProperty('targetTypes')
expect(plan).toHaveProperty('estimatedSpeedup')
expect(plan).toHaveProperty('confidence')
expect(plan).toHaveProperty('reasoning')
expect(plan.originalQuery).toBe('Find software engineers')
expect(Array.isArray(plan.inferredTypes)).toBe(true)
expect(Array.isArray(plan.targetTypes)).toBe(true)
})
it('should calculate accurate speedup estimates', () => {
const singleType = planner.planQuery('Find engineers')
const multiType = planner.planQuery('engineers at companies')
const allTypes = planner.planQuery('show everything')
// Single-type: 42/1 = 42x
expect(singleType.estimatedSpeedup).toBeCloseTo(42, 0)
// Multi-type: 42/N where N = 2-5
expect(multiType.estimatedSpeedup).toBeGreaterThan(1)
expect(multiType.estimatedSpeedup).toBeLessThan(42)
// All-types: 42/42 = 1x
expect(allTypes.estimatedSpeedup).toBe(1.0)
})
it('should sort types by confidence in inference results', () => {
const plan = planner.planQuery('Find engineers and documents')
expect(plan.inferredTypes.length).toBeGreaterThan(0)
// Verify sorted by confidence (descending)
for (let i = 1; i < plan.inferredTypes.length; i++) {
expect(plan.inferredTypes[i - 1].confidence).toBeGreaterThanOrEqual(
plan.inferredTypes[i].confidence
)
}
})
})
// ========== Performance Tests (1 test) ==========
describe('Performance', () => {
it('should plan queries in < 5ms', () => {
const start = performance.now()
planner.planQuery('Find software engineers in San Francisco')
const elapsed = performance.now() - start
expect(elapsed).toBeLessThan(5) // Target: < 1ms, allow 5ms for CI
})
})
// ========== Statistics Tests (2 tests) ==========
describe('Query Statistics', () => {
it('should track query statistics', () => {
planner.planQuery('Find engineers') // single-type
planner.planQuery('engineers at companies') // multi-type
planner.planQuery('show everything') // all-types
const stats = planner.getStats()
expect(stats.totalQueries).toBe(3)
expect(stats.singleTypeQueries).toBeGreaterThan(0)
expect(stats.multiTypeQueries).toBeGreaterThan(0)
expect(stats.allTypesQueries).toBeGreaterThan(0)
expect(stats.avgConfidence).toBeGreaterThan(0)
})
it('should generate statistics report', () => {
planner.planQuery('Find engineers')
planner.planQuery('Find documents')
planner.planQuery('Show everything')
const report = planner.getStatsReport()
expect(report).toContain('Query Statistics')
expect(report).toContain('Single-type')
expect(report).toContain('Multi-type')
expect(report).toContain('All-types')
expect(report).toContain('Avg confidence')
expect(report).toContain('Avg speedup')
})
})
// ========== Batch Analysis Tests (1 test) ==========
describe('Batch Query Analysis', () => {
it('should analyze query distribution and provide recommendations', () => {
const queries = [
'Find engineers',
'Find developers',
'Show documents',
'List reports',
'Find companies',
'engineers at Tesla',
'documents about AI',
'show me everything',
'all the things',
'stuff'
]
const analysis = planner.analyzeQueries(queries)
expect(analysis).toHaveProperty('distribution')
expect(analysis).toHaveProperty('avgSpeedup')
expect(analysis).toHaveProperty('recommendations')
expect(analysis.distribution['single-type']).toBeGreaterThanOrEqual(0)
expect(analysis.distribution['multi-type']).toBeGreaterThanOrEqual(0)
expect(analysis.distribution['all-types']).toBeGreaterThanOrEqual(0)
const total =
analysis.distribution['single-type'] +
analysis.distribution['multi-type'] +
analysis.distribution['all-types']
expect(total).toBe(queries.length)
expect(analysis.avgSpeedup).toBeGreaterThan(0)
expect(Array.isArray(analysis.recommendations)).toBe(true)
})
})
// ========== Configuration Tests (2 tests) ==========
describe('Configuration', () => {
it('should respect custom confidence thresholds', () => {
const strictPlanner = new TypeAwareQueryPlanner(undefined, {
singleTypeThreshold: 0.95,
multiTypeThreshold: 0.85
})
// Query with moderate confidence should fall back to all-types
const plan = strictPlanner.planQuery('maybe find some engineers')
// With strict thresholds, this should use all-types or multi-type
expect(plan.routing).not.toBe('single-type')
})
it('should respect maxMultiTypes limit', () => {
const limitedPlanner = new TypeAwareQueryPlanner(undefined, {
maxMultiTypes: 2
})
const plan = limitedPlanner.planQuery(
'engineers at companies in cities working on projects with tools'
)
if (plan.routing === 'multi-type') {
expect(plan.targetTypes.length).toBeLessThanOrEqual(2)
}
})
})
// ========== Convenience Functions (1 test) ==========
describe('Global Convenience Functions', () => {
it('should provide planQuery() and getQueryPlanner() functions', () => {
const plan = planQuery('Find engineers')
expect(plan).toHaveProperty('routing')
expect(plan).toHaveProperty('targetTypes')
const globalPlanner = getQueryPlanner()
expect(globalPlanner).toBeInstanceOf(TypeAwareQueryPlanner)
})
})
})