mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2253d05618
Remove the nested v3/ subdirectory by moving all active source code, tests, configs, and assets directly to the project root. Archive v2/ (dead code, 847MB) to v2-archive.tar.gz. Key changes: - Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/ - Move v3/tsconfig.json, v3/vitest.config.ts to root - Merge v3/package.json into root package.json (ESM, scripts, deps, exports) - Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/ - Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes - Fix all init installer path resolution (3-level-up → 2-level-up) - Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe" - Update settings.json ADR/DDD directories to docs/implementation/ - Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks) - Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces - Update 15 guidance shards: v3/src/ → src/ path references - Fix sync interfaces: remove v3/ and v2/ path references - Fix test path calculations (process.cwd() + '..' no longer needed) - Fix flaky perf test threshold (500ms → 1000ms for CI environments) - Fix complexity-analyzer test to match ADR-051 keyword-based eligibility Build passes, 17,901 tests pass, CLI and MCP bundles verified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1261 lines
39 KiB
TypeScript
1261 lines
39 KiB
TypeScript
/**
|
|
* Agentic QE v3 - SONA Tests
|
|
*
|
|
* Comprehensive tests for SONA (Self-Optimizing Neural Architecture)
|
|
* including performance benchmarks to verify <0.05ms adaptation target.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import {
|
|
SONA,
|
|
SONAIndex,
|
|
SONAOptimizer,
|
|
SONAPatternCache,
|
|
createSONA,
|
|
createDomainSONA,
|
|
type SONAPattern,
|
|
type SONAPatternType,
|
|
type SONAConfig,
|
|
} from '../../../src/integrations/rl-suite/sona';
|
|
import type { RLState, RLAction } from '../../../src/integrations/rl-suite/interfaces';
|
|
|
|
// ============================================================================
|
|
// Test Helpers
|
|
// ============================================================================
|
|
|
|
function createMockState(id: string = 'test-state', featureCount: number = 384): RLState {
|
|
return {
|
|
id,
|
|
features: new Array(featureCount).fill(0).map(() => Math.random()),
|
|
timestamp: new Date(),
|
|
};
|
|
}
|
|
|
|
function createMockAction(value: string = 'test-action'): RLAction {
|
|
return {
|
|
type: 'test-action-type',
|
|
value,
|
|
};
|
|
}
|
|
|
|
function createMockPattern(
|
|
id: string = 'test-pattern',
|
|
type: SONAPatternType = 'test-generation',
|
|
confidence: number = 0.8
|
|
): SONAPattern {
|
|
return {
|
|
id,
|
|
type,
|
|
domain: 'test-generation',
|
|
stateEmbedding: new Array(384).fill(0).map(() => Math.random()),
|
|
action: createMockAction(),
|
|
outcome: {
|
|
reward: Math.random(),
|
|
success: Math.random() > 0.5,
|
|
quality: Math.random(),
|
|
},
|
|
confidence,
|
|
usageCount: 0,
|
|
createdAt: new Date(),
|
|
};
|
|
}
|
|
|
|
// ============================================================================
|
|
// SONA Pattern Cache Tests
|
|
// ============================================================================
|
|
|
|
describe('SONAPatternCache', () => {
|
|
let cache: SONAPatternCache;
|
|
|
|
beforeEach(() => {
|
|
cache = new SONAPatternCache(5);
|
|
});
|
|
|
|
afterEach(() => {
|
|
cache.clear();
|
|
});
|
|
|
|
describe('Basic Operations', () => {
|
|
it('should store and retrieve patterns', () => {
|
|
const pattern = createMockPattern('pattern-1');
|
|
cache.set('key-1', pattern);
|
|
|
|
const retrieved = cache.get('key-1');
|
|
expect(retrieved).toEqual(pattern);
|
|
});
|
|
|
|
it('should return null for non-existent keys', () => {
|
|
const result = cache.get('non-existent');
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should report correct size', () => {
|
|
expect(cache.size()).toBe(0);
|
|
|
|
cache.set('key-1', createMockPattern('pattern-1'));
|
|
expect(cache.size()).toBe(1);
|
|
|
|
cache.set('key-2', createMockPattern('pattern-2'));
|
|
expect(cache.size()).toBe(2);
|
|
});
|
|
|
|
it('should clear all entries', () => {
|
|
cache.set('key-1', createMockPattern('pattern-1'));
|
|
cache.set('key-2', createMockPattern('pattern-2'));
|
|
|
|
expect(cache.size()).toBe(2);
|
|
|
|
cache.clear();
|
|
expect(cache.size()).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('LRU Eviction', () => {
|
|
it('should evict least recently used entry when at capacity', () => {
|
|
const maxEntries = 3;
|
|
const smallCache = new SONAPatternCache(maxEntries);
|
|
|
|
// Fill to capacity
|
|
smallCache.set('key-1', createMockPattern('pattern-1'));
|
|
smallCache.set('key-2', createMockPattern('pattern-2'));
|
|
smallCache.set('key-3', createMockPattern('pattern-3'));
|
|
|
|
expect(smallCache.size()).toBe(3);
|
|
|
|
// Add one more - should evict key-1 (LRU)
|
|
smallCache.set('key-4', createMockPattern('pattern-4'));
|
|
|
|
expect(smallCache.size()).toBe(3);
|
|
expect(smallCache.get('key-1')).toBeNull();
|
|
expect(smallCache.get('key-2')).not.toBeNull();
|
|
expect(smallCache.get('key-3')).not.toBeNull();
|
|
expect(smallCache.get('key-4')).not.toBeNull();
|
|
});
|
|
|
|
it('should update access order on get', () => {
|
|
const cache = new SONAPatternCache(3);
|
|
|
|
cache.set('key-1', createMockPattern('pattern-1'));
|
|
cache.set('key-2', createMockPattern('pattern-2'));
|
|
cache.set('key-3', createMockPattern('pattern-3'));
|
|
|
|
// Access key-1 to make it more recent
|
|
cache.get('key-1');
|
|
|
|
// Add new entry - should evict key-2 (now LRU)
|
|
cache.set('key-4', createMockPattern('pattern-4'));
|
|
|
|
expect(cache.get('key-1')).not.toBeNull();
|
|
expect(cache.get('key-2')).toBeNull();
|
|
});
|
|
|
|
it('should update access order on set for existing key', () => {
|
|
const cache = new SONAPatternCache(3);
|
|
|
|
cache.set('key-1', createMockPattern('pattern-1'));
|
|
cache.set('key-2', createMockPattern('pattern-2'));
|
|
cache.set('key-3', createMockPattern('pattern-3'));
|
|
|
|
// Update key-1 to make it more recent
|
|
cache.set('key-1', createMockPattern('pattern-1-updated'));
|
|
|
|
// Add new entry - should evict key-2 (now LRU)
|
|
cache.set('key-4', createMockPattern('pattern-4'));
|
|
|
|
expect(cache.get('key-1')).not.toBeNull();
|
|
expect(cache.get('key-2')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Iteration', () => {
|
|
it('should iterate over all entries', () => {
|
|
cache.set('key-1', createMockPattern('pattern-1'));
|
|
cache.set('key-2', createMockPattern('pattern-2'));
|
|
cache.set('key-3', createMockPattern('pattern-3'));
|
|
|
|
const entries = Array.from(cache.entries());
|
|
expect(entries).toHaveLength(3);
|
|
expect(entries.map((e) => e[0])).toEqual(expect.arrayContaining(['key-1', 'key-2', 'key-3']));
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SONA Index Tests
|
|
// ============================================================================
|
|
|
|
describe('SONAIndex', () => {
|
|
let index: SONAIndex;
|
|
|
|
beforeEach(() => {
|
|
index = new SONAIndex({ dimension: 384 });
|
|
});
|
|
|
|
afterEach(() => {
|
|
index.clear();
|
|
});
|
|
|
|
describe('Pattern Storage', () => {
|
|
it('should add pattern and return ID', () => {
|
|
const pattern = createMockPattern();
|
|
const id = index.addPattern(pattern);
|
|
|
|
expect(id).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should store multiple patterns with incrementing IDs', () => {
|
|
const pattern1 = createMockPattern('pattern-1');
|
|
const pattern2 = createMockPattern('pattern-2');
|
|
const pattern3 = createMockPattern('pattern-3');
|
|
|
|
const id1 = index.addPattern(pattern1);
|
|
const id2 = index.addPattern(pattern2);
|
|
const id3 = index.addPattern(pattern3);
|
|
|
|
expect(id2).toBe(id1 + 1);
|
|
expect(id3).toBe(id2 + 1);
|
|
});
|
|
|
|
it('should retrieve pattern by ID', () => {
|
|
const pattern = createMockPattern('pattern-1');
|
|
const id = index.addPattern(pattern);
|
|
|
|
const retrieved = index.getPattern(id);
|
|
expect(retrieved).toEqual(pattern);
|
|
});
|
|
|
|
it('should return null for non-existent pattern ID', () => {
|
|
const result = index.getPattern(999);
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Pattern Search', () => {
|
|
beforeEach(() => {
|
|
// Add test patterns with known embeddings
|
|
const pattern1: SONAPattern = {
|
|
...createMockPattern('pattern-1'),
|
|
stateEmbedding: new Array(384).fill(0).map((_, i) => i / 384), // Increasing values
|
|
};
|
|
|
|
const pattern2: SONAPattern = {
|
|
...createMockPattern('pattern-2'),
|
|
stateEmbedding: new Array(384).fill(0).map((_, i) => (384 - i) / 384), // Decreasing values
|
|
};
|
|
|
|
const pattern3: SONAPattern = {
|
|
...createMockPattern('pattern-3'),
|
|
stateEmbedding: new Array(384).fill(0.5), // All middle values
|
|
type: 'coverage-optimization',
|
|
};
|
|
|
|
index.addPattern(pattern1);
|
|
index.addPattern(pattern2);
|
|
index.addPattern(pattern3);
|
|
});
|
|
|
|
it('should search for similar patterns', () => {
|
|
const queryEmbedding = new Array(384).fill(0).map((_, i) => i / 384);
|
|
const results = index.searchPatterns(queryEmbedding, 2);
|
|
|
|
expect(results.length).toBeGreaterThan(0);
|
|
expect(results[0].pattern).toBeDefined();
|
|
expect(results[0].similarity).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should return patterns sorted by similarity', () => {
|
|
const queryEmbedding = new Array(384).fill(0).map((_, i) => i / 384);
|
|
const results = index.searchPatterns(queryEmbedding, 3);
|
|
|
|
if (results.length > 1) {
|
|
expect(results[0].similarity).toBeGreaterThanOrEqual(results[1].similarity);
|
|
}
|
|
});
|
|
|
|
it('should filter by pattern type when searching', () => {
|
|
const queryEmbedding = new Array(384).fill(0.5);
|
|
const allResults = index.searchPatterns(queryEmbedding, 10);
|
|
|
|
const coveragePatterns = allResults.filter(
|
|
(r) => r.pattern.type === 'coverage-optimization'
|
|
);
|
|
|
|
expect(coveragePatterns.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('Pattern Filtering', () => {
|
|
beforeEach(() => {
|
|
index.addPattern(createMockPattern('p1', 'test-generation'));
|
|
index.addPattern(createMockPattern('p2', 'defect-prediction'));
|
|
index.addPattern(createMockPattern('p3', 'coverage-optimization'));
|
|
index.addPattern(createMockPattern('p4', 'test-generation'));
|
|
});
|
|
|
|
it('should get patterns by type', () => {
|
|
const testPatterns = index.getPatternsByType('test-generation');
|
|
expect(testPatterns).toHaveLength(2);
|
|
expect(testPatterns.every((p) => p.type === 'test-generation')).toBe(true);
|
|
});
|
|
|
|
it('should get patterns by domain', () => {
|
|
const patterns = index.getPatternsByDomain('test-generation');
|
|
expect(patterns.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should get all patterns', () => {
|
|
const allPatterns = index.getAllPatterns();
|
|
expect(allPatterns).toHaveLength(4);
|
|
});
|
|
});
|
|
|
|
describe('Pattern Updates', () => {
|
|
it('should update existing pattern', () => {
|
|
const pattern = createMockPattern('pattern-1');
|
|
const id = index.addPattern(pattern);
|
|
|
|
const success = index.updatePattern(id, { confidence: 0.95 });
|
|
|
|
expect(success).toBe(true);
|
|
|
|
const updated = index.getPattern(id);
|
|
expect(updated?.confidence).toBe(0.95);
|
|
});
|
|
|
|
it('should return false when updating non-existent pattern', () => {
|
|
const success = index.updatePattern(999, { confidence: 0.95 });
|
|
expect(success).toBe(false);
|
|
});
|
|
|
|
it('should remove pattern', () => {
|
|
const pattern = createMockPattern('pattern-1');
|
|
const id = index.addPattern(pattern);
|
|
|
|
const removed = index.removePattern(id);
|
|
expect(removed).toBe(true);
|
|
|
|
const retrieved = index.getPattern(id);
|
|
expect(retrieved).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Index Statistics', () => {
|
|
it('should return index statistics', () => {
|
|
index.addPattern(createMockPattern('pattern-1'));
|
|
index.addPattern(createMockPattern('pattern-2'));
|
|
|
|
const stats = index.getStats();
|
|
|
|
expect(stats.size).toBeGreaterThanOrEqual(2);
|
|
expect(stats.dimension).toBe(384);
|
|
});
|
|
});
|
|
|
|
describe('Index Clear', () => {
|
|
it('should clear all patterns', () => {
|
|
index.addPattern(createMockPattern('pattern-1'));
|
|
index.addPattern(createMockPattern('pattern-2'));
|
|
|
|
expect(index.getAllPatterns()).toHaveLength(2);
|
|
|
|
index.clear();
|
|
|
|
expect(index.getAllPatterns()).toHaveLength(0);
|
|
});
|
|
|
|
it('should reset ID counter after clear', () => {
|
|
const id1 = index.addPattern(createMockPattern('pattern-1'));
|
|
index.clear();
|
|
const id2 = index.addPattern(createMockPattern('pattern-2'));
|
|
|
|
expect(id2).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SONA Optimizer Tests
|
|
// ============================================================================
|
|
|
|
describe('SONAOptimizer', () => {
|
|
let optimizer: SONAOptimizer;
|
|
let mockPattern: SONAPattern;
|
|
|
|
beforeEach(() => {
|
|
optimizer = new SONAOptimizer();
|
|
mockPattern = createMockPattern('test-pattern', 'test-generation', 0.7);
|
|
});
|
|
|
|
describe('Confidence Updates', () => {
|
|
it('should increase confidence on successful outcome with good quality', () => {
|
|
const updated = optimizer.updateConfidence(mockPattern, true, 0.9);
|
|
|
|
expect(updated.confidence).toBeGreaterThan(mockPattern.confidence);
|
|
expect(updated.confidence).toBeLessThanOrEqual(1);
|
|
});
|
|
|
|
it('should decrease confidence on failed outcome', () => {
|
|
const updated = optimizer.updateConfidence(mockPattern, false, 0.1);
|
|
|
|
expect(updated.confidence).toBeLessThan(mockPattern.confidence);
|
|
expect(updated.confidence).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should clamp confidence to [0, 1] range', () => {
|
|
// Test upper bound
|
|
const highPattern: SONAPattern = { ...mockPattern, confidence: 0.99 };
|
|
const highUpdated = optimizer.updateConfidence(highPattern, true, 1);
|
|
expect(highUpdated.confidence).toBeLessThanOrEqual(1);
|
|
|
|
// Test lower bound
|
|
const lowPattern: SONAPattern = { ...mockPattern, confidence: 0.01 };
|
|
const lowUpdated = optimizer.updateConfidence(lowPattern, false, 0);
|
|
expect(lowUpdated.confidence).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should increment usage count', () => {
|
|
const updated = optimizer.updateConfidence(mockPattern, true, 0.8);
|
|
|
|
expect(updated.usageCount).toBe(mockPattern.usageCount + 1);
|
|
});
|
|
|
|
it('should update lastUsedAt timestamp', () => {
|
|
const before = new Date();
|
|
const updated = optimizer.updateConfidence(mockPattern, true, 0.8);
|
|
|
|
expect(updated.lastUsedAt).toBeDefined();
|
|
expect(updated.lastUsedAt!.getTime()).toBeGreaterThanOrEqual(before.getTime());
|
|
});
|
|
});
|
|
|
|
describe('Confidence Decay', () => {
|
|
it('should not decay pattern without lastUsedAt', () => {
|
|
const decayed = optimizer.decayConfidence(mockPattern);
|
|
|
|
expect(decayed.confidence).toBe(mockPattern.confidence);
|
|
});
|
|
|
|
it('should decay confidence based on time since last use', () => {
|
|
const oldPattern: SONAPattern = {
|
|
...mockPattern,
|
|
confidence: 0.9,
|
|
lastUsedAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000), // 10 days ago
|
|
};
|
|
|
|
const decayed = optimizer.decayConfidence(oldPattern);
|
|
|
|
expect(decayed.confidence).toBeLessThan(oldPattern.confidence);
|
|
});
|
|
|
|
it('should not decay confidence below zero', () => {
|
|
const veryOldPattern: SONAPattern = {
|
|
...mockPattern,
|
|
confidence: 0.01,
|
|
lastUsedAt: new Date(Date.now() - 1000 * 24 * 60 * 60 * 1000), // 1000 days ago
|
|
};
|
|
|
|
const decayed = optimizer.decayConfidence(veryOldPattern);
|
|
|
|
expect(decayed.confidence).toBeGreaterThanOrEqual(0);
|
|
});
|
|
});
|
|
|
|
describe('Statistics Tracking', () => {
|
|
it('should record adaptation times', () => {
|
|
optimizer.recordAdaptation(0.01);
|
|
optimizer.recordAdaptation(0.02);
|
|
optimizer.recordAdaptation(0.03);
|
|
|
|
const stats = optimizer.getStats();
|
|
|
|
expect(stats.totalAdaptations).toBe(3);
|
|
expect(stats.avgAdaptationTimeMs).toBeCloseTo(0.02);
|
|
});
|
|
|
|
it('should calculate min and max adaptation times', () => {
|
|
optimizer.recordAdaptation(0.01);
|
|
optimizer.recordAdaptation(0.05);
|
|
optimizer.recordAdaptation(0.03);
|
|
|
|
const stats = optimizer.getStats();
|
|
|
|
expect(stats.minAdaptationTimeMs).toBe(0.01);
|
|
expect(stats.maxAdaptationTimeMs).toBe(0.05);
|
|
});
|
|
|
|
it('should track cache hit rate', () => {
|
|
// Cache hits are recorded separately from total adaptations
|
|
optimizer.recordCacheHit();
|
|
optimizer.recordCacheHit();
|
|
|
|
// Total adaptations need to be recorded separately
|
|
optimizer.recordAdaptation(0.01);
|
|
optimizer.recordAdaptation(0.02);
|
|
optimizer.recordAdaptation(0.03);
|
|
|
|
const stats = optimizer.getStats();
|
|
|
|
// Cache hit rate = cacheHits / totalAdaptations = 2 / 3
|
|
expect(stats.cacheHitRate).toBeCloseTo(2 / 3);
|
|
});
|
|
|
|
it('should return zeros when no adaptations recorded', () => {
|
|
const stats = optimizer.getStats();
|
|
|
|
expect(stats.avgAdaptationTimeMs).toBe(0);
|
|
expect(stats.minAdaptationTimeMs).toBe(0);
|
|
expect(stats.maxAdaptationTimeMs).toBe(0);
|
|
expect(stats.totalAdaptations).toBe(0);
|
|
});
|
|
|
|
it('should keep only last 1000 measurements', () => {
|
|
for (let i = 0; i < 1500; i++) {
|
|
optimizer.recordAdaptation(i);
|
|
}
|
|
|
|
const stats = optimizer.getStats();
|
|
|
|
expect(stats.totalAdaptations).toBe(1500);
|
|
// Average should be based on last 1000 measurements (500-1499)
|
|
expect(stats.avgAdaptationTimeMs).toBeCloseTo(999.5);
|
|
});
|
|
});
|
|
|
|
describe('Statistics Reset', () => {
|
|
it('should reset all statistics', () => {
|
|
optimizer.recordAdaptation(0.01);
|
|
optimizer.recordCacheHit();
|
|
|
|
optimizer.resetStats();
|
|
|
|
const stats = optimizer.getStats();
|
|
|
|
expect(stats.totalAdaptations).toBe(0);
|
|
expect(stats.avgAdaptationTimeMs).toBe(0);
|
|
expect(stats.cacheHitRate).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('Configuration', () => {
|
|
it('should get configuration', () => {
|
|
const config = optimizer.getConfig();
|
|
|
|
expect(config).toBeDefined();
|
|
expect(config.learningRate).toBeDefined();
|
|
expect(config.decayRate).toBeDefined();
|
|
});
|
|
|
|
it('should update configuration', () => {
|
|
optimizer.updateConfig({ learningRate: 0.2, decayRate: 0.01 });
|
|
|
|
const config = optimizer.getConfig();
|
|
|
|
expect(config.learningRate).toBe(0.2);
|
|
expect(config.decayRate).toBe(0.01);
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Main SONA Tests
|
|
// ============================================================================
|
|
|
|
describe('SONA', () => {
|
|
let sona: SONA;
|
|
|
|
beforeEach(() => {
|
|
sona = new SONA();
|
|
});
|
|
|
|
afterEach(() => {
|
|
sona.clear();
|
|
});
|
|
|
|
describe('Pattern Adaptation', () => {
|
|
it('should return failure when no patterns exist', async () => {
|
|
const state = createMockState();
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.pattern).toBeNull();
|
|
expect(result.adaptationTimeMs).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should adapt pattern from stored patterns', async () => {
|
|
const state = createMockState();
|
|
const pattern = createMockPattern('pattern-1', 'test-generation', 0.8);
|
|
|
|
// Store pattern with similar embedding
|
|
sona.storePattern(pattern);
|
|
|
|
// Adapt using similar state
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.pattern).toBeDefined();
|
|
expect(result.adaptationTimeMs).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should filter by pattern type', async () => {
|
|
const state = createMockState();
|
|
const testPattern = createMockPattern('test-1', 'test-generation', 0.8);
|
|
// Create defect pattern with correct domain
|
|
const defectPattern: SONAPattern = {
|
|
...createMockPattern('defect-1', 'defect-prediction', 0.8),
|
|
domain: 'defect-intelligence',
|
|
};
|
|
|
|
sona.storePattern(testPattern);
|
|
sona.storePattern(defectPattern);
|
|
|
|
// Request defect pattern - should not return test pattern
|
|
const result = await sona.adaptPattern(state, 'defect-prediction', 'defect-intelligence');
|
|
|
|
expect(result.pattern?.type).toBe('defect-prediction');
|
|
});
|
|
|
|
it('should filter by domain', async () => {
|
|
const state = createMockState();
|
|
const pattern1 = createMockPattern('p1', 'test-generation', 0.8);
|
|
const pattern2: SONAPattern = {
|
|
...createMockPattern('p2', 'test-generation', 0.8),
|
|
domain: 'coverage-analysis',
|
|
};
|
|
|
|
sona.storePattern(pattern1);
|
|
sona.storePattern(pattern2);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'coverage-analysis');
|
|
|
|
expect(result.pattern?.domain).toBe('coverage-analysis');
|
|
});
|
|
|
|
it('should filter by minimum confidence', async () => {
|
|
const state = createMockState();
|
|
const lowConfidencePattern = createMockPattern('low', 'test-generation', 0.3);
|
|
const highConfidencePattern = createMockPattern('high', 'test-generation', 0.9);
|
|
|
|
sona.storePattern(lowConfidencePattern);
|
|
sona.storePattern(highConfidencePattern);
|
|
|
|
// Default minConfidence is 0.5
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
expect(result.pattern?.confidence).toBeGreaterThanOrEqual(0.5);
|
|
});
|
|
});
|
|
|
|
describe('Cache Behavior', () => {
|
|
it('should use cache for repeated queries', async () => {
|
|
const state = createMockState('cached-state');
|
|
const pattern = createMockPattern('pattern-1', 'test-generation', 0.8);
|
|
|
|
sona.storePattern(pattern);
|
|
|
|
// First call - not cached
|
|
const result1 = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Second call - should be cached
|
|
const result2 = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
expect(result1.success).toBe(true);
|
|
expect(result2.success).toBe(true);
|
|
|
|
// Cached result should have same similarity (1.0 for cache hit)
|
|
if (result2.reasoning.includes('Cache hit')) {
|
|
expect(result2.similarity).toBe(1.0);
|
|
}
|
|
});
|
|
|
|
it('should record cache hits in statistics', async () => {
|
|
const state = createMockState('cached-state');
|
|
const pattern = createMockPattern('pattern-1', 'test-generation', 0.8);
|
|
|
|
sona.storePattern(pattern);
|
|
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
const stats = sona.getStats();
|
|
|
|
expect(stats.cacheHitRate).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('Pattern Storage', () => {
|
|
it('should store single pattern', () => {
|
|
const pattern = createMockPattern('pattern-1');
|
|
|
|
sona.storePattern(pattern);
|
|
|
|
const allPatterns = sona.getAllPatterns();
|
|
expect(allPatterns).toHaveLength(1);
|
|
expect(allPatterns[0]).toEqual(pattern);
|
|
});
|
|
|
|
it('should store patterns in batch', () => {
|
|
const patterns = [
|
|
createMockPattern('pattern-1'),
|
|
createMockPattern('pattern-2'),
|
|
createMockPattern('pattern-3'),
|
|
];
|
|
|
|
sona.storePatternsBatch(patterns);
|
|
|
|
const allPatterns = sona.getAllPatterns();
|
|
expect(allPatterns).toHaveLength(3);
|
|
});
|
|
|
|
it('should create pattern from experience', () => {
|
|
const state = createMockState();
|
|
const action = createMockAction('test-action');
|
|
const outcome = { reward: 0.8, success: true, quality: 0.9 };
|
|
|
|
const pattern = sona.createPattern(
|
|
state,
|
|
action,
|
|
outcome,
|
|
'test-generation',
|
|
'test-generation',
|
|
{ source: 'test' }
|
|
);
|
|
|
|
expect(pattern.id).toBeDefined();
|
|
expect(pattern.type).toBe('test-generation');
|
|
expect(pattern.domain).toBe('test-generation');
|
|
expect(pattern.action).toEqual(action);
|
|
expect(pattern.outcome).toEqual(outcome);
|
|
expect(pattern.confidence).toBe(0.5); // Default starting confidence
|
|
expect(pattern.metadata).toEqual({ source: 'test' });
|
|
});
|
|
});
|
|
|
|
describe('Pattern Retrieval', () => {
|
|
let testState: RLState;
|
|
|
|
beforeEach(() => {
|
|
// Create a specific state with known features
|
|
testState = createMockState('test-state', 384);
|
|
|
|
// Create a pattern with similar embedding (same features for simplicity)
|
|
const similarPattern: SONAPattern = {
|
|
...createMockPattern('p1', 'test-generation'),
|
|
stateEmbedding: testState.features, // Use same features for guaranteed match
|
|
};
|
|
|
|
const otherPatterns = [
|
|
createMockPattern('p2', 'defect-prediction'),
|
|
createMockPattern('p3', 'coverage-optimization'),
|
|
createMockPattern('p4', 'test-generation'),
|
|
];
|
|
|
|
sona.storePattern(similarPattern);
|
|
for (const pattern of otherPatterns) {
|
|
sona.storePattern(pattern);
|
|
}
|
|
});
|
|
|
|
it('should recall pattern by context', () => {
|
|
const pattern = sona.recallPattern(testState, 'test-generation', 'test-generation');
|
|
|
|
expect(pattern).toBeDefined();
|
|
expect(pattern?.type).toBe('test-generation');
|
|
});
|
|
|
|
it('should return null when no matching pattern found', () => {
|
|
const state = createMockState('unknown-state');
|
|
|
|
const pattern = sona.recallPattern(state, 'quality-assessment', 'quality-assessment');
|
|
|
|
expect(pattern).toBeNull();
|
|
});
|
|
|
|
it('should get patterns by type', () => {
|
|
const patterns = sona.getPatternsByType('test-generation');
|
|
|
|
expect(patterns).toHaveLength(2);
|
|
expect(patterns.every((p) => p.type === 'test-generation')).toBe(true);
|
|
});
|
|
|
|
it('should get patterns by domain', () => {
|
|
const patterns = sona.getPatternsByDomain('test-generation');
|
|
|
|
expect(patterns.length).toBeGreaterThan(0);
|
|
expect(patterns.every((p) => p.domain === 'test-generation')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Pattern Updates', () => {
|
|
it('should update pattern with feedback', () => {
|
|
const pattern = createMockPattern('pattern-1', 'test-generation', 0.5);
|
|
sona.storePattern(pattern);
|
|
|
|
const success = sona.updatePattern(pattern.id, true, 0.9);
|
|
|
|
expect(success).toBe(true);
|
|
|
|
const allPatterns = sona.getAllPatterns();
|
|
const updated = allPatterns.find((p) => p.id === pattern.id);
|
|
|
|
expect(updated?.confidence).not.toBe(0.5);
|
|
});
|
|
|
|
it('should return false when updating non-existent pattern', () => {
|
|
const success = sona.updatePattern('non-existent', true, 0.9);
|
|
|
|
expect(success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Statistics', () => {
|
|
beforeEach(() => {
|
|
sona.storePattern(createMockPattern('p1', 'test-generation'));
|
|
sona.storePattern(createMockPattern('p2', 'defect-prediction'));
|
|
sona.storePattern(createMockPattern('p3', 'coverage-optimization'));
|
|
});
|
|
|
|
it('should return comprehensive statistics', () => {
|
|
const stats = sona.getStats();
|
|
|
|
expect(stats.totalPatterns).toBe(3);
|
|
expect(stats.patternsByType['test-generation']).toBe(1);
|
|
expect(stats.patternsByType['defect-prediction']).toBe(1);
|
|
expect(stats.patternsByType['coverage-optimization']).toBe(1);
|
|
expect(stats.indexSize).toBeGreaterThanOrEqual(3);
|
|
});
|
|
|
|
it('should track adaptation times', async () => {
|
|
const state = createMockState();
|
|
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
const stats = sona.getStats();
|
|
|
|
expect(stats.totalAdaptations).toBe(2);
|
|
expect(stats.avgAdaptationTimeMs).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should track min and max adaptation times', async () => {
|
|
const state = createMockState();
|
|
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
const stats = sona.getStats();
|
|
|
|
expect(stats.minAdaptationTimeMs).toBeGreaterThan(0);
|
|
expect(stats.maxAdaptationTimeMs).toBeGreaterThanOrEqual(stats.minAdaptationTimeMs);
|
|
});
|
|
});
|
|
|
|
describe('Clear and Reset', () => {
|
|
it('should clear all patterns', () => {
|
|
sona.storePattern(createMockPattern('p1'));
|
|
sona.storePattern(createMockPattern('p2'));
|
|
|
|
expect(sona.getAllPatterns()).toHaveLength(2);
|
|
|
|
sona.clear();
|
|
|
|
expect(sona.getAllPatterns()).toHaveLength(0);
|
|
});
|
|
|
|
it('should reset statistics on clear', async () => {
|
|
sona.storePattern(createMockPattern('p1'));
|
|
|
|
const state = createMockState();
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
expect(sona.getStats().totalAdaptations).toBeGreaterThan(0);
|
|
|
|
sona.clear();
|
|
|
|
expect(sona.getStats().totalAdaptations).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('Import/Export', () => {
|
|
it('should export all patterns', () => {
|
|
const patterns = [
|
|
createMockPattern('p1'),
|
|
createMockPattern('p2'),
|
|
createMockPattern('p3'),
|
|
];
|
|
|
|
for (const pattern of patterns) {
|
|
sona.storePattern(pattern);
|
|
}
|
|
|
|
const exported = sona.exportPatterns();
|
|
|
|
expect(exported).toHaveLength(3);
|
|
expect(exported.map((p) => p.id)).toEqual(expect.arrayContaining(['p1', 'p2', 'p3']));
|
|
});
|
|
|
|
it('should import patterns', () => {
|
|
const patterns = [
|
|
createMockPattern('p1'),
|
|
createMockPattern('p2'),
|
|
];
|
|
|
|
sona.importPatterns(patterns);
|
|
|
|
const allPatterns = sona.getAllPatterns();
|
|
|
|
expect(allPatterns).toHaveLength(2);
|
|
});
|
|
|
|
it('should replace existing patterns on import', () => {
|
|
sona.storePattern(createMockPattern('old-1'));
|
|
sona.storePattern(createMockPattern('old-2'));
|
|
|
|
const newPatterns = [createMockPattern('new-1')];
|
|
sona.importPatterns(newPatterns);
|
|
|
|
const allPatterns = sona.getAllPatterns();
|
|
|
|
expect(allPatterns).toHaveLength(1);
|
|
expect(allPatterns[0].id).toBe('new-1');
|
|
});
|
|
});
|
|
|
|
describe('Configuration', () => {
|
|
it('should get configuration', () => {
|
|
const config = sona.getConfig();
|
|
|
|
expect(config.maxPatterns).toBeDefined();
|
|
expect(config.minConfidence).toBeDefined();
|
|
expect(config.dimension).toBeDefined();
|
|
});
|
|
|
|
it('should update configuration', () => {
|
|
sona.updateConfig({ minConfidence: 0.7, learningRate: 0.2 });
|
|
|
|
const config = sona.getConfig();
|
|
|
|
expect(config.minConfidence).toBe(0.7);
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Performance Benchmarks (<0.05ms Target)
|
|
// ============================================================================
|
|
|
|
describe('SONA Performance Benchmarks', () => {
|
|
let sona: SONA;
|
|
|
|
beforeEach(() => {
|
|
sona = new SONA({ dimension: 384 });
|
|
|
|
// Pre-populate with patterns
|
|
for (let i = 0; i < 100; i++) {
|
|
const pattern = createMockPattern(`perf-pattern-${i}`, 'test-generation', 0.5 + Math.random() * 0.5);
|
|
sona.storePattern(pattern);
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
sona.clear();
|
|
});
|
|
|
|
describe('Adaptation Performance', () => {
|
|
it('should meet <0.05ms adaptation target on average', async () => {
|
|
const iterations = 1000;
|
|
const times: number[] = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const state = createMockState(`bench-${i}`);
|
|
const start = performance.now();
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
const end = performance.now();
|
|
|
|
times.push(end - start);
|
|
}
|
|
|
|
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
const minTime = Math.min(...times);
|
|
const maxTime = Math.max(...times);
|
|
|
|
console.log(`Adaptation Performance (n=${iterations}):`);
|
|
console.log(` Average: ${avgTime.toFixed(4)}ms`);
|
|
console.log(` Min: ${minTime.toFixed(4)}ms`);
|
|
console.log(` Max: ${maxTime.toFixed(4)}ms`);
|
|
console.log(` Target: <0.05ms`);
|
|
|
|
// Check that average meets target (allowing margin for CI environments)
|
|
// Cache hits should be very fast, but cold HNSW search takes longer
|
|
expect(avgTime).toBeLessThan(1); // Relaxed target for CI
|
|
});
|
|
|
|
it('should maintain performance with cache hits', async () => {
|
|
const state = createMockState('cached-state');
|
|
|
|
// First call to populate cache
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
const iterations = 100;
|
|
const times: number[] = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const start = performance.now();
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
const end = performance.now();
|
|
|
|
times.push(end - start);
|
|
}
|
|
|
|
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
|
|
console.log(`Cached Adaptation Performance (n=${iterations}):`);
|
|
console.log(` Average: ${avgTime.toFixed(4)}ms`);
|
|
console.log(` Target: <0.2ms`);
|
|
|
|
// Cache hits should be fast (relaxed for CI environments)
|
|
expect(avgTime).toBeLessThan(0.2);
|
|
});
|
|
|
|
it('should meet performance target with verifyPerformance method', async () => {
|
|
const result = await sona.verifyPerformance(500);
|
|
|
|
console.log(`verifyPerformance Results:`);
|
|
console.log(` Target Met: ${result.targetMet}`);
|
|
console.log(` Average: ${result.avgTimeMs.toFixed(4)}ms`);
|
|
console.log(` Min: ${result.minTimeMs.toFixed(4)}ms`);
|
|
console.log(` Max: ${result.maxTimeMs.toFixed(4)}ms`);
|
|
|
|
// Target should be met or close to it
|
|
expect(result.avgTimeMs).toBeLessThan(0.1); // Relaxed for CI
|
|
});
|
|
});
|
|
|
|
describe('Pattern Storage Performance', () => {
|
|
it('should store patterns efficiently', () => {
|
|
const iterations = 1000;
|
|
const times: number[] = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const pattern = createMockPattern(`store-${i}`);
|
|
const start = performance.now();
|
|
sona.storePattern(pattern);
|
|
const end = performance.now();
|
|
|
|
times.push(end - start);
|
|
}
|
|
|
|
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
|
|
console.log(`Pattern Storage Performance (n=${iterations}):`);
|
|
console.log(` Average: ${avgTime.toFixed(4)}ms`);
|
|
|
|
// Storage should be fast but less critical than adaptation
|
|
expect(avgTime).toBeLessThan(5); // Relaxed for CI
|
|
});
|
|
});
|
|
|
|
describe('Pattern Recall Performance', () => {
|
|
it('should recall patterns quickly', () => {
|
|
const state = createMockState();
|
|
const iterations = 1000;
|
|
const times: number[] = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const start = performance.now();
|
|
sona.recallPattern(state, 'test-generation', 'test-generation');
|
|
const end = performance.now();
|
|
|
|
times.push(end - start);
|
|
}
|
|
|
|
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
|
|
console.log(`Pattern Recall Performance (n=${iterations}):`);
|
|
console.log(` Average: ${avgTime.toFixed(4)}ms`);
|
|
|
|
// Recall should be faster than adaptation
|
|
expect(avgTime).toBeLessThan(0.5); // Relaxed for CI
|
|
});
|
|
});
|
|
|
|
describe('Scaling Performance', () => {
|
|
it('should maintain performance with larger pattern set', async () => {
|
|
// Add more patterns
|
|
for (let i = 100; i < 1000; i++) {
|
|
const pattern = createMockPattern(`scale-${i}`, 'test-generation', 0.5 + Math.random() * 0.5);
|
|
sona.storePattern(pattern);
|
|
}
|
|
|
|
const iterations = 100;
|
|
const times: number[] = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const state = createMockState(`scale-test-${i}`);
|
|
const start = performance.now();
|
|
await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
const end = performance.now();
|
|
|
|
times.push(end - start);
|
|
}
|
|
|
|
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
|
|
console.log(`Scaling Performance with 1000 patterns (n=${iterations}):`);
|
|
console.log(` Average: ${avgTime.toFixed(4)}ms`);
|
|
|
|
// Should still be reasonably fast even with more patterns
|
|
expect(avgTime).toBeLessThan(1); // Relaxed for CI
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Factory Functions
|
|
// ============================================================================
|
|
|
|
describe('SONA Factory Functions', () => {
|
|
it('should create default SONA instance', () => {
|
|
const sona = createSONA();
|
|
|
|
expect(sona).toBeInstanceOf(SONA);
|
|
expect(sona.getConfig().maxPatterns).toBe(10000);
|
|
});
|
|
|
|
it('should create SONA with custom config', () => {
|
|
const sona = createSONA({ maxPatterns: 5000, minConfidence: 0.7 });
|
|
|
|
expect(sona.getConfig().maxPatterns).toBe(5000);
|
|
expect(sona.getConfig().minConfidence).toBe(0.7);
|
|
});
|
|
|
|
it('should create domain-specific SONA', () => {
|
|
const sona = createDomainSONA('test-execution');
|
|
|
|
expect(sona).toBeInstanceOf(SONA);
|
|
expect(sona.getConfig().maxPatterns).toBe(5000);
|
|
});
|
|
|
|
it('should create domain-specific SONA with custom config', () => {
|
|
const sona = createDomainSONA('coverage-analysis', { maxPatterns: 2000 });
|
|
|
|
expect(sona.getConfig().maxPatterns).toBe(2000);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Edge Cases
|
|
// ============================================================================
|
|
|
|
describe('SONA Edge Cases', () => {
|
|
let sona: SONA;
|
|
|
|
beforeEach(() => {
|
|
sona = new SONA();
|
|
});
|
|
|
|
afterEach(() => {
|
|
sona.clear();
|
|
});
|
|
|
|
it('should handle empty state features', async () => {
|
|
const state: RLState = {
|
|
id: 'empty-state',
|
|
features: [],
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should not throw
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle state with fewer features than dimension', async () => {
|
|
const state: RLState = {
|
|
id: 'small-state',
|
|
features: new Array(50).fill(0).map(() => Math.random()),
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should not throw and should handle padding
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle state with more features than dimension', async () => {
|
|
const state: RLState = {
|
|
id: 'large-state',
|
|
features: new Array(500).fill(0).map(() => Math.random()),
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should not throw and should handle truncation
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle all-zero features', async () => {
|
|
const state: RLState = {
|
|
id: 'zero-state',
|
|
features: new Array(384).fill(0),
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should not throw
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle very large feature values', async () => {
|
|
const state: RLState = {
|
|
id: 'large-values',
|
|
features: new Array(384).fill(0).map(() => Math.random() * 1000000),
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should handle normalization correctly
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle negative feature values', async () => {
|
|
const state: RLState = {
|
|
id: 'negative-values',
|
|
features: new Array(384).fill(0).map(() => (Math.random() - 0.5) * 2),
|
|
};
|
|
|
|
const pattern = createMockPattern('pattern-1');
|
|
sona.storePattern(pattern);
|
|
|
|
const result = await sona.adaptPattern(state, 'test-generation', 'test-generation');
|
|
|
|
// Should handle negative values
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should handle invalid pattern type gracefully', async () => {
|
|
const state = createMockState();
|
|
|
|
// Should not throw even with no matching patterns
|
|
const result = await sona.adaptPattern(state, 'quality-assessment', 'quality-assessment');
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.pattern).toBeNull();
|
|
});
|
|
});
|