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>
781 lines
27 KiB
TypeScript
781 lines
27 KiB
TypeScript
/**
|
|
* Learning Module Coherence Integration Tests
|
|
* ADR-052 Phase 3 Action A3.5
|
|
*
|
|
* Comprehensive integration tests for coherence filtering in the learning module.
|
|
* Tests cover:
|
|
* - Pattern retrieval with coherence filtering
|
|
* - Memory coherence auditing
|
|
* - Promotion coherence gates
|
|
* - Causal verification
|
|
*
|
|
* These tests use REAL components where available, with mocks for expensive WASM operations.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
|
|
import { QEReasoningBank, createQEReasoningBank } from '../../src/learning/qe-reasoning-bank.js';
|
|
import { RealQEReasoningBank, createRealQEReasoningBank } from '../../src/learning/real-qe-reasoning-bank.js';
|
|
import { CoherenceService, createCoherenceService } from '../../src/integrations/coherence/index.js';
|
|
import { WasmLoader } from '../../src/integrations/coherence/wasm-loader.js';
|
|
import { InMemoryBackend } from '../../src/kernel/memory-backend.js';
|
|
import { InMemoryEventBus } from '../../src/kernel/event-bus.js';
|
|
import type { CreateQEPatternOptions } from '../../src/learning/qe-patterns.js';
|
|
import type { EventBus } from '../../src/kernel/interfaces.js';
|
|
import type { CoherenceNode, CoherenceResult } from '../../src/integrations/coherence/types.js';
|
|
|
|
// ============================================================================
|
|
// Test Helpers
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Helper to create test patterns with customizable properties
|
|
*/
|
|
function createTestPattern(overrides: Partial<CreateQEPatternOptions> = {}): CreateQEPatternOptions {
|
|
const timestamp = Date.now();
|
|
return {
|
|
patternType: 'test-template',
|
|
name: `test-pattern-${timestamp}`,
|
|
description: 'Test pattern for integration testing',
|
|
template: {
|
|
type: 'code',
|
|
content: 'test content',
|
|
variables: [],
|
|
},
|
|
context: { tags: ['test'], testType: 'unit' },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper to wait for events with timeout
|
|
*/
|
|
function waitForEvent(eventBus: EventBus, eventName: string, timeout = 5000): Promise<unknown> {
|
|
return new Promise((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error(`Timeout waiting for ${eventName}`)), timeout);
|
|
const subscription = eventBus.subscribe(eventName, (data) => {
|
|
clearTimeout(timer);
|
|
subscription.unsubscribe();
|
|
resolve(data);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper to create a mock WASM loader that simulates coherence checks
|
|
*/
|
|
function createMockWasmLoader() {
|
|
const wasmLoader = {
|
|
isAvailable: vi.fn().mockResolvedValue(false), // Use fallback by default
|
|
getEngines: vi.fn().mockRejectedValue(new Error('WASM not available in tests')),
|
|
on: vi.fn(),
|
|
off: vi.fn(),
|
|
getState: vi.fn().mockReturnValue('unloaded'),
|
|
getLastError: vi.fn().mockReturnValue(null),
|
|
};
|
|
return wasmLoader;
|
|
}
|
|
|
|
/**
|
|
* Helper to create coherence nodes from pattern search results
|
|
*/
|
|
function createCoherenceNodes(patterns: Array<{ id: string; embedding: number[]; name: string }>): CoherenceNode[] {
|
|
return patterns.map(p => ({
|
|
id: p.id,
|
|
embedding: p.embedding,
|
|
weight: 1.0,
|
|
metadata: { name: p.name },
|
|
}));
|
|
}
|
|
|
|
// ============================================================================
|
|
// Test Suite Setup
|
|
// ============================================================================
|
|
|
|
describe('Learning Module Coherence Integration', () => {
|
|
let memory: InMemoryBackend;
|
|
let eventBus: InMemoryEventBus;
|
|
let wasmLoader: ReturnType<typeof createMockWasmLoader>;
|
|
let coherenceService: CoherenceService;
|
|
|
|
beforeAll(async () => {
|
|
memory = new InMemoryBackend();
|
|
await memory.initialize();
|
|
|
|
eventBus = new InMemoryEventBus();
|
|
|
|
wasmLoader = createMockWasmLoader();
|
|
coherenceService = new CoherenceService(wasmLoader as any, {
|
|
fallbackEnabled: true,
|
|
coherenceThreshold: 0.4,
|
|
});
|
|
await coherenceService.initialize();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await coherenceService.dispose();
|
|
await memory.dispose();
|
|
await eventBus.dispose();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await memory.clear();
|
|
});
|
|
|
|
// ============================================================================
|
|
// A. Pattern Retrieval Coherence Filtering
|
|
// ============================================================================
|
|
|
|
describe('Pattern Retrieval Coherence Filtering', () => {
|
|
it('should filter incoherent patterns from retrieval', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store patterns that would be incoherent together
|
|
// Pattern 1: Unit testing pattern (coherent)
|
|
const pattern1 = await bank.storePattern(createTestPattern({
|
|
name: 'AAA Unit Test Pattern',
|
|
description: 'Arrange-Act-Assert unit testing pattern',
|
|
context: { tags: ['unit-test', 'aaa'], testType: 'unit' },
|
|
}));
|
|
expect(pattern1.success).toBe(true);
|
|
|
|
// Pattern 2: Integration testing pattern (coherent with unit tests)
|
|
const pattern2 = await bank.storePattern(createTestPattern({
|
|
name: 'Integration Test Pattern',
|
|
description: 'End-to-end integration testing pattern',
|
|
context: { tags: ['integration-test', 'e2e'], testType: 'integration' },
|
|
}));
|
|
expect(pattern2.success).toBe(true);
|
|
|
|
// Pattern 3: Contradictory pattern (suggests skipping tests)
|
|
const pattern3 = await bank.storePattern(createTestPattern({
|
|
name: 'Skip Tests Pattern',
|
|
description: 'Pattern that suggests skipping test execution',
|
|
context: { tags: ['skip', 'no-test'], testType: 'unit' },
|
|
}));
|
|
expect(pattern3.success).toBe(true);
|
|
|
|
// Search for test patterns
|
|
const searchResult = await bank.searchPatterns('unit testing best practices', {
|
|
limit: 10,
|
|
});
|
|
|
|
expect(searchResult.success).toBe(true);
|
|
if (!searchResult.success) return;
|
|
|
|
const patterns = searchResult.value;
|
|
expect(patterns.length).toBeGreaterThan(0);
|
|
|
|
// Check coherence of retrieved patterns
|
|
const nodes: CoherenceNode[] = patterns.map(p => ({
|
|
id: p.pattern.id,
|
|
embedding: p.pattern.embedding,
|
|
weight: p.similarity,
|
|
metadata: { name: p.pattern.name },
|
|
}));
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
|
|
// Verify that coherence filtering would identify conflicts
|
|
// (In a full implementation, the bank would filter before returning)
|
|
if (coherenceCheck.contradictions.length > 0) {
|
|
expect(coherenceCheck.isCoherent).toBe(false);
|
|
expect(coherenceCheck.contradictions.some(c => c.severity === 'high' || c.severity === 'critical')).toBe(true);
|
|
}
|
|
|
|
await bank.dispose();
|
|
}, 10000);
|
|
|
|
it('should return all patterns when coherence service unavailable', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store multiple patterns
|
|
await bank.storePattern(createTestPattern({ name: 'Pattern A' }));
|
|
await bank.storePattern(createTestPattern({ name: 'Pattern B' }));
|
|
await bank.storePattern(createTestPattern({ name: 'Pattern C' }));
|
|
|
|
// Search without coherence filtering (fallback behavior)
|
|
const result = await bank.searchPatterns('test pattern', { limit: 10 });
|
|
|
|
expect(result.success).toBe(true);
|
|
if (!result.success) return;
|
|
|
|
// Should return all matching patterns (no filtering when coherence unavailable)
|
|
expect(result.value.length).toBeGreaterThanOrEqual(3);
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should emit event when all patterns are incoherent', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Set up event listener
|
|
const eventPromise = waitForEvent(eventBus, 'learning:coherence_warning');
|
|
|
|
// Store highly contradictory patterns
|
|
const contradictoryPatterns = [
|
|
createTestPattern({
|
|
name: 'Always Mock Dependencies',
|
|
description: 'Always use mocks for all dependencies',
|
|
context: { tags: ['mocking', 'isolation'] },
|
|
}),
|
|
createTestPattern({
|
|
name: 'Never Use Mocks',
|
|
description: 'Avoid mocks, use real dependencies',
|
|
context: { tags: ['integration', 'real-deps'] },
|
|
}),
|
|
];
|
|
|
|
for (const pattern of contradictoryPatterns) {
|
|
await bank.storePattern(pattern);
|
|
}
|
|
|
|
// Search - should trigger coherence check
|
|
const result = await bank.searchPatterns('dependency testing approach');
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
// In a full implementation, this would emit an event
|
|
// For now, we verify the patterns were stored
|
|
if (result.success) {
|
|
expect(result.value.length).toBeGreaterThan(0);
|
|
}
|
|
|
|
await bank.dispose();
|
|
|
|
// Note: Event emission would be implemented in real coherence-aware search
|
|
// This test documents the expected behavior
|
|
}, 7000);
|
|
});
|
|
|
|
// ============================================================================
|
|
// B. Memory Coherence Auditor
|
|
// ============================================================================
|
|
|
|
describe('Memory Coherence Auditor', () => {
|
|
it('should audit memory and report global coherence energy', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store multiple patterns across different domains
|
|
const patterns = [
|
|
createTestPattern({ name: 'Unit Test Pattern', context: { tags: ['unit'] } }),
|
|
createTestPattern({ name: 'Integration Test Pattern', context: { tags: ['integration'] } }),
|
|
createTestPattern({ name: 'E2E Test Pattern', context: { tags: ['e2e'] } }),
|
|
createTestPattern({ name: 'Security Test Pattern', context: { tags: ['security'] } }),
|
|
];
|
|
|
|
for (const pattern of patterns) {
|
|
await bank.storePattern(pattern);
|
|
}
|
|
|
|
// Get all patterns for audit
|
|
const allPatterns = await bank.searchPatterns('', { limit: 100 });
|
|
expect(allPatterns.success).toBe(true);
|
|
if (!allPatterns.success) return;
|
|
|
|
// Create coherence nodes
|
|
const nodes = allPatterns.value.map(p => ({
|
|
id: p.pattern.id,
|
|
embedding: p.pattern.embedding,
|
|
weight: 1.0,
|
|
metadata: { domain: p.pattern.qeDomain },
|
|
}));
|
|
|
|
// Run coherence audit
|
|
const auditResult = await coherenceService.checkCoherence(nodes);
|
|
|
|
// Verify energy is reported
|
|
expect(typeof auditResult.energy).toBe('number');
|
|
expect(auditResult.energy).toBeGreaterThanOrEqual(0);
|
|
expect(auditResult.durationMs).toBeGreaterThanOrEqual(0);
|
|
|
|
// Verify coherence assessment
|
|
expect(typeof auditResult.isCoherent).toBe('boolean');
|
|
expect(auditResult.lane).toMatch(/^(reflex|retrieval|heavy|human)$/);
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should identify hotspots in pattern clusters', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store conflicting patterns in same domain
|
|
const conflictingPatterns = [
|
|
createTestPattern({
|
|
name: 'Test First (TDD)',
|
|
description: 'Write tests before implementation',
|
|
context: { tags: ['tdd', 'test-first'], testType: 'unit' },
|
|
}),
|
|
createTestPattern({
|
|
name: 'Test Last',
|
|
description: 'Write tests after implementation',
|
|
context: { tags: ['test-last'], testType: 'unit' },
|
|
}),
|
|
createTestPattern({
|
|
name: 'No Tests Needed',
|
|
description: 'Some code does not need tests',
|
|
context: { tags: ['no-tests'], testType: 'unit' },
|
|
}),
|
|
];
|
|
|
|
for (const pattern of conflictingPatterns) {
|
|
await bank.storePattern(pattern);
|
|
}
|
|
|
|
// Search for test methodology patterns
|
|
const result = await bank.searchPatterns('test methodology approach');
|
|
expect(result.success).toBe(true);
|
|
if (!result.success) return;
|
|
|
|
// Check for contradictions (hotspots)
|
|
const nodes = result.value.map(p => ({
|
|
id: p.pattern.id,
|
|
embedding: p.pattern.embedding,
|
|
weight: p.similarity,
|
|
metadata: { name: p.pattern.name },
|
|
}));
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
|
|
// Should detect contradictions in the cluster
|
|
if (coherenceCheck.contradictions.length > 0) {
|
|
expect(coherenceCheck.contradictions).toBeDefined();
|
|
// Hotspots are indicated by high-severity contradictions
|
|
const highSeverity = coherenceCheck.contradictions.filter(
|
|
c => c.severity === 'high' || c.severity === 'critical'
|
|
);
|
|
expect(highSeverity.length).toBeGreaterThan(0);
|
|
}
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should generate recommendations for high-energy patterns', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store patterns that create high energy (low coherence)
|
|
const highEnergyPatterns = [
|
|
createTestPattern({
|
|
name: 'Mocking Strategy A',
|
|
description: 'Mock everything aggressively',
|
|
embedding: [1.0, 0.0, 0.0, 0.5],
|
|
}),
|
|
createTestPattern({
|
|
name: 'Mocking Strategy B',
|
|
description: 'Never mock, always use real dependencies',
|
|
embedding: [-1.0, 0.0, 0.0, -0.5], // Opposite embedding
|
|
}),
|
|
];
|
|
|
|
for (const pattern of highEnergyPatterns) {
|
|
await bank.storePattern(pattern);
|
|
}
|
|
|
|
const result = await bank.searchPatterns('mocking approach');
|
|
expect(result.success).toBe(true);
|
|
if (!result.success) return;
|
|
|
|
const nodes = result.value.map(p => ({
|
|
id: p.pattern.id,
|
|
embedding: p.pattern.embedding,
|
|
weight: 1.0,
|
|
}));
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
|
|
// Should provide recommendations
|
|
expect(coherenceCheck.recommendations).toBeDefined();
|
|
expect(Array.isArray(coherenceCheck.recommendations)).toBe(true);
|
|
expect(coherenceCheck.recommendations.length).toBeGreaterThan(0);
|
|
|
|
// Recommendations should address high energy
|
|
if (coherenceCheck.energy > 0.4) {
|
|
const hasActionableRec = coherenceCheck.recommendations.some(
|
|
r => r.includes('review') || r.includes('resolve') || r.includes('analysis')
|
|
);
|
|
expect(hasActionableRec).toBe(true);
|
|
}
|
|
|
|
await bank.dispose();
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// C. Promotion Coherence Gate
|
|
// ============================================================================
|
|
|
|
describe('Promotion Coherence Gate', () => {
|
|
it('should block promotion of incoherent patterns', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Store a long-term pattern (existing knowledge)
|
|
const existingPattern = await bank.storePattern(createTestPattern({
|
|
name: 'Established Best Practice',
|
|
description: 'Use dependency injection for testability',
|
|
context: { tags: ['di', 'best-practice'] },
|
|
}));
|
|
expect(existingPattern.success).toBe(true);
|
|
if (!existingPattern.success) return;
|
|
|
|
// Manually promote to long-term to simulate existing knowledge
|
|
await bank.recordOutcome({
|
|
patternId: existingPattern.value.id,
|
|
success: true,
|
|
});
|
|
await bank.recordOutcome({
|
|
patternId: existingPattern.value.id,
|
|
success: true,
|
|
});
|
|
await bank.recordOutcome({
|
|
patternId: existingPattern.value.id,
|
|
success: true,
|
|
});
|
|
|
|
// Create conflicting short-term pattern with good metrics
|
|
const conflictingPattern = await bank.storePattern(createTestPattern({
|
|
name: 'Avoid Dependency Injection',
|
|
description: 'Hardcode dependencies for simplicity',
|
|
context: { tags: ['simple', 'no-di'] },
|
|
}));
|
|
expect(conflictingPattern.success).toBe(true);
|
|
if (!conflictingPattern.success) return;
|
|
|
|
// Give it good metrics
|
|
await bank.recordOutcome({
|
|
patternId: conflictingPattern.value.id,
|
|
success: true,
|
|
});
|
|
await bank.recordOutcome({
|
|
patternId: conflictingPattern.value.id,
|
|
success: true,
|
|
});
|
|
|
|
// Check coherence between patterns before promotion
|
|
const patterns = [existingPattern.value, conflictingPattern.value];
|
|
const nodes = patterns.map(p => ({
|
|
id: p.id,
|
|
embedding: p.embedding,
|
|
weight: p.confidence,
|
|
}));
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
|
|
// In a full implementation, promotion would be blocked if incoherent
|
|
// For now, just verify coherence check detected the patterns
|
|
expect(coherenceCheck).toBeDefined();
|
|
expect(coherenceCheck.energy).toBeGreaterThanOrEqual(0);
|
|
|
|
// If incoherent, contradictions should be reported
|
|
if (!coherenceCheck.isCoherent && coherenceCheck.contradictions.length > 0) {
|
|
expect(coherenceCheck.contradictions.length).toBeGreaterThan(0);
|
|
// Promotion should be blocked (enforced in actual promotion logic)
|
|
}
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should emit promotion_blocked event with reason', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// In a full implementation, this would emit an event when promotion is blocked
|
|
// For now, we verify the coherence check mechanism works
|
|
|
|
const pattern = await bank.storePattern(createTestPattern({
|
|
name: 'Contradictory Pattern',
|
|
}));
|
|
expect(pattern.success).toBe(true);
|
|
|
|
// The event would be emitted during promotion attempt
|
|
// Event structure: { event: 'promotion_blocked', reason: 'coherence_violation', patternId: ... }
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should allow promotion of coherent patterns', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Create non-conflicting pattern with good metrics
|
|
const coherentPattern = await bank.storePattern(createTestPattern({
|
|
name: 'Well-Tested Component Pattern',
|
|
description: 'Pattern for testing components thoroughly',
|
|
context: { tags: ['testing', 'coverage'] },
|
|
}));
|
|
expect(coherentPattern.success).toBe(true);
|
|
if (!coherentPattern.success) return;
|
|
|
|
// Record successful uses to qualify for promotion (need 4 to get 3 successfulUses)
|
|
for (let i = 0; i < 4; i++) {
|
|
await bank.recordOutcome({
|
|
patternId: coherentPattern.value.id,
|
|
success: true,
|
|
metrics: { testsPassed: 10, coverageImprovement: 0.15 },
|
|
});
|
|
}
|
|
|
|
// Get updated pattern
|
|
const updatedPattern = await bank.getPattern(coherentPattern.value.id);
|
|
expect(updatedPattern).toBeDefined();
|
|
if (!updatedPattern) return;
|
|
|
|
// Should have promotion-worthy metrics
|
|
expect(updatedPattern.successfulUses).toBeGreaterThanOrEqual(3);
|
|
expect(updatedPattern.successRate).toBeGreaterThan(0.6);
|
|
|
|
// Coherence check should pass (no conflicts)
|
|
const nodes = [
|
|
{
|
|
id: updatedPattern.id,
|
|
embedding: updatedPattern.embedding,
|
|
weight: 1.0,
|
|
},
|
|
];
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
// Single pattern should be coherent
|
|
expect(coherenceCheck.isCoherent).toBe(true);
|
|
|
|
await bank.dispose();
|
|
});
|
|
|
|
it('should skip coherence check when service unavailable', async () => {
|
|
const bank = createQEReasoningBank(memory, eventBus);
|
|
await bank.initialize();
|
|
|
|
// Create pattern
|
|
const pattern = await bank.storePattern(createTestPattern());
|
|
expect(pattern.success).toBe(true);
|
|
if (!pattern.success) return;
|
|
|
|
// Record outcomes to qualify for promotion (need 4 to get 3 successfulUses)
|
|
for (let i = 0; i < 4; i++) {
|
|
await bank.recordOutcome({
|
|
patternId: pattern.value.id,
|
|
success: true,
|
|
});
|
|
}
|
|
|
|
// When coherence service is unavailable, promotion should proceed
|
|
// based only on basic criteria (success rate, usage count)
|
|
// This is graceful degradation behavior
|
|
|
|
const updatedPattern = await bank.getPattern(pattern.value.id);
|
|
expect(updatedPattern).toBeDefined();
|
|
if (!updatedPattern) return;
|
|
|
|
// Verify promotion criteria are met
|
|
expect(updatedPattern.successfulUses).toBeGreaterThanOrEqual(3);
|
|
|
|
await bank.dispose();
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// D. Causal Verification
|
|
// ============================================================================
|
|
|
|
describe('Causal Verification', () => {
|
|
it('should verify causal links using CausalEngine', async () => {
|
|
// Test known causal relationship: test coverage -> bug detection
|
|
const causalData = {
|
|
sampleSize: 50,
|
|
causeValues: Array.from({ length: 50 }, (_, i) => i / 50), // Coverage 0-100%
|
|
effectValues: Array.from({ length: 50 }, (_, i) => Math.min(1, 0.2 + i / 50 * 0.6)), // Bugs found
|
|
confounders: [],
|
|
};
|
|
|
|
const verification = await coherenceService.verifyCausality(
|
|
'test_coverage',
|
|
'bugs_detected',
|
|
causalData
|
|
);
|
|
|
|
expect(verification).toBeDefined();
|
|
expect(verification.isCausal).toBeDefined();
|
|
expect(verification.effectStrength).toBeGreaterThanOrEqual(0);
|
|
// Allow for floating point imprecision
|
|
expect(verification.effectStrength).toBeLessThan(1.01);
|
|
|
|
// Should not be marked as spurious (there's a real correlation)
|
|
if (verification.relationshipType) {
|
|
expect(verification.relationshipType).not.toBe('spurious');
|
|
}
|
|
|
|
expect(verification.durationMs).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should detect spurious correlations', async () => {
|
|
// Test spurious correlation: random data with no causal relationship
|
|
const causalData = {
|
|
sampleSize: 30,
|
|
causeValues: Array.from({ length: 30 }, () => Math.random()), // Random cause
|
|
effectValues: Array.from({ length: 30 }, () => Math.random()), // Random effect
|
|
confounders: [],
|
|
};
|
|
|
|
const verification = await coherenceService.verifyCausality(
|
|
'random_metric_a',
|
|
'random_metric_b',
|
|
causalData
|
|
);
|
|
|
|
expect(verification).toBeDefined();
|
|
|
|
// Low correlation should be detected
|
|
expect(verification.effectStrength).toBeLessThan(0.5);
|
|
|
|
// Should be marked as spurious or no relationship
|
|
if (verification.relationshipType) {
|
|
expect(['spurious', 'none']).toContain(verification.relationshipType);
|
|
}
|
|
});
|
|
|
|
it('should return confidence score', async () => {
|
|
const causalData = {
|
|
sampleSize: 100,
|
|
causeValues: Array.from({ length: 100 }, (_, i) => i),
|
|
effectValues: Array.from({ length: 100 }, (_, i) => i * 2 + 5), // Strong linear relationship
|
|
confounders: [],
|
|
};
|
|
|
|
const verification = await coherenceService.verifyCausality(
|
|
'input',
|
|
'output',
|
|
causalData
|
|
);
|
|
|
|
expect(verification.confidence).toBeDefined();
|
|
expect(verification.confidence).toBeGreaterThanOrEqual(0);
|
|
expect(verification.confidence).toBeLessThanOrEqual(1);
|
|
|
|
// Larger sample size should give higher confidence
|
|
expect(verification.confidence).toBeGreaterThan(0.3);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// E. Real Integration Tests (Using Actual WASM if Available)
|
|
// ============================================================================
|
|
|
|
describe('Real Integration Tests', () => {
|
|
it('should handle real pattern storage and coherence check', async () => {
|
|
// This test uses real components without mocks
|
|
const realMemory = new InMemoryBackend();
|
|
await realMemory.initialize();
|
|
|
|
const realEventBus = new InMemoryEventBus();
|
|
const realBank = createQEReasoningBank(realMemory, realEventBus);
|
|
await realBank.initialize();
|
|
|
|
try {
|
|
// Store real patterns
|
|
const patterns = [
|
|
createTestPattern({
|
|
name: 'Test Isolation Pattern',
|
|
description: 'Ensure tests are isolated and independent',
|
|
}),
|
|
createTestPattern({
|
|
name: 'Test Cleanup Pattern',
|
|
description: 'Clean up resources after each test',
|
|
}),
|
|
createTestPattern({
|
|
name: 'Test Data Builder Pattern',
|
|
description: 'Use builders for test data creation',
|
|
}),
|
|
];
|
|
|
|
const storedPatterns = [];
|
|
for (const pattern of patterns) {
|
|
const result = await realBank.storePattern(pattern);
|
|
expect(result.success).toBe(true);
|
|
if (result.success) {
|
|
storedPatterns.push(result.value);
|
|
}
|
|
}
|
|
|
|
// Search for patterns
|
|
const searchResult = await realBank.searchPatterns('test pattern');
|
|
expect(searchResult.success).toBe(true);
|
|
|
|
if (searchResult.success) {
|
|
expect(searchResult.value.length).toBeGreaterThan(0);
|
|
|
|
// Check coherence of results
|
|
const nodes = searchResult.value.slice(0, 3).map(p => ({
|
|
id: p.pattern.id,
|
|
embedding: p.pattern.embedding,
|
|
weight: p.similarity,
|
|
}));
|
|
|
|
const coherenceCheck = await coherenceService.checkCoherence(nodes);
|
|
expect(coherenceCheck).toBeDefined();
|
|
expect(coherenceCheck.energy).toBeGreaterThanOrEqual(0);
|
|
}
|
|
|
|
// Get stats
|
|
const stats = await realBank.getStats();
|
|
expect(stats.totalPatterns).toBeGreaterThan(0);
|
|
} finally {
|
|
await realBank.dispose();
|
|
await realMemory.dispose();
|
|
await realEventBus.dispose();
|
|
}
|
|
}, 15000);
|
|
|
|
it('should handle real coherence service with fallback', async () => {
|
|
// Test real coherence service initialization and fallback behavior
|
|
const realWasmLoader = createMockWasmLoader();
|
|
const realService = new CoherenceService(realWasmLoader as any, {
|
|
fallbackEnabled: true,
|
|
coherenceThreshold: 0.3,
|
|
});
|
|
|
|
await realService.initialize();
|
|
|
|
try {
|
|
// Create test nodes
|
|
const nodes: CoherenceNode[] = [
|
|
{
|
|
id: 'node1',
|
|
embedding: [1.0, 0.0, 0.0, 0.5],
|
|
weight: 1.0,
|
|
},
|
|
{
|
|
id: 'node2',
|
|
embedding: [0.9, 0.1, 0.0, 0.4],
|
|
weight: 1.0,
|
|
},
|
|
{
|
|
id: 'node3',
|
|
embedding: [-1.0, 0.0, 0.0, -0.5], // Opposite direction
|
|
weight: 1.0,
|
|
},
|
|
];
|
|
|
|
const result = await realService.checkCoherence(nodes);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.energy).toBeGreaterThanOrEqual(0);
|
|
expect(result.isCoherent).toBeDefined();
|
|
expect(result.lane).toMatch(/^(reflex|retrieval|heavy|human)$/);
|
|
expect(result.usedFallback).toBe(true); // Should use fallback
|
|
|
|
// Get stats
|
|
const stats = realService.getStats();
|
|
expect(stats.totalChecks).toBeGreaterThan(0);
|
|
expect(stats.fallbackCount).toBeGreaterThan(0);
|
|
expect(stats.wasmAvailable).toBe(false);
|
|
} finally {
|
|
await realService.dispose();
|
|
}
|
|
});
|
|
});
|
|
});
|