widgetdc-cortex / apps /backend /src /tests /autonomous.integration.test.ts
Kraft102's picture
Deploy from GitHub Actions 2025-12-15_17-15-49
a9f0bb1 verified
/**
* ╔══════════════════════════════════════════════════════════════════════════════╗
* β•‘ AUTONOMOUS SYSTEM INTEGRATION TESTS β•‘
* β•‘ β•‘
* β•‘ Comprehensive test suite for the autonomous MCP routing system. β•‘
* β•‘ Covers: β•‘
* β•‘ β€’ PatternMemory persistence β•‘
* β•‘ β€’ FailureMemory recording and recovery paths β•‘
* β•‘ β€’ DecisionEngine source scoring β•‘
* β•‘ β€’ AutonomousAgent routing and learning β•‘
* β•‘ β€’ SelfHealing circuit breaker β•‘
* β•‘ β€’ API endpoint validation β•‘
* β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
import { initializeDatabase, getDatabase, getSqlJsDatabase } from '../database/index.js';
import { SqlJsStorageAdapter } from '../mcp/memory/StorageAdapter.js';
// Mock Embeddings to avoid ONNX/Float32Array issues in tests
vi.mock('../platform/embeddings/TransformersEmbeddings', () => ({
getTransformersEmbeddings: () => ({
embed: vi.fn().mockResolvedValue(new Array(384).fill(0.1)), // Mock 384-dim vector
embedBatch: vi.fn().mockImplementation(async (texts) => texts.map(() => new Array(384).fill(0.1))),
initialize: vi.fn().mockResolvedValue(undefined),
isInitialized: vi.fn().mockReturnValue(true),
})
}));
// ═══════════════════════════════════════════════════════════════════════════════
// TEST SETUP
// ═══════════════════════════════════════════════════════════════════════════════
// Mock Prisma to avoid DATABASE_URLRequirement
vi.mock('../database/prisma', () => ({
prisma: {
$connect: vi.fn(),
$disconnect: vi.fn(),
$queryRaw: vi.fn().mockResolvedValue([]),
widgetPermission: {
findUnique: vi.fn(),
upsert: vi.fn(),
findMany: vi.fn(),
}
},
checkPrismaConnection: vi.fn().mockResolvedValue(false),
disconnectPrisma: vi.fn(),
default: {
$connect: vi.fn(),
$disconnect: vi.fn(),
}
}));
let db: any;
beforeAll(async () => {
// Initialize test database
await initializeDatabase();
// Use raw SqlJs database for Memory components that require .exec()
db = getSqlJsDatabase();
if (!db) {
throw new Error('Failed to initialize SqlJs database for testing');
}
// Force clean tables
try {
db.exec('DROP TABLE IF EXISTS mcp_failure_memory');
db.exec('DROP TABLE IF EXISTS mcp_query_patterns');
} catch (e) { /* ignore */ }
});
afterAll(async () => {
// Cleanup would go here
vi.clearAllMocks();
});
afterEach(() => {
vi.clearAllMocks();
});
// ═══════════════════════════════════════════════════════════════════════════════
// PATTERN MEMORY TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('PatternMemory - Enterprise Persistence', () => {
let patternMemory: any;
beforeEach(async () => {
const { PatternMemory } = await import('../mcp/memory/PatternMemory.js');
patternMemory = new PatternMemory(new SqlJsStorageAdapter(db));
});
it('should record query patterns', async () => {
await patternMemory.recordQuery({
widgetId: 'test-widget',
queryType: 'agents.list',
queryParams: { limit: 10 },
sourceUsed: 'test-source',
latencyMs: 50,
resultSize: 1024,
success: true
});
// Pattern should be in cache immediately
const similar = await patternMemory.findSimilarQueries('agents.list', { limit: 10 });
const cache = (patternMemory as any).cache;
const genSig = (patternMemory as any).generateSignature('agents.list', { limit: 10 });
const debugInfo = {
cacheLen: cache.length,
firstSig: cache[0]?.querySignature,
expectedSig: genSig,
matches: cache[0]?.querySignature === genSig,
firstItem: cache[0]
};
expect(similar.length).toBeGreaterThan(0);
expect(similar[0].pattern.widgetId).toBe('test-widget');
});
it('should calculate average latency correctly', async () => {
// Record multiple patterns with different latencies
for (let i = 0; i < 5; i++) {
await patternMemory.recordQuery({
widgetId: 'latency-test',
queryType: 'test.query',
queryParams: {},
sourceUsed: 'latency-source',
latencyMs: (i + 1) * 20, // 20, 40, 60, 80, 100
success: true
});
}
const avgLatency = await patternMemory.getAverageLatency('latency-source');
expect(avgLatency).toBe(60); // Average of 20, 40, 60, 80, 100
});
it('should calculate success rate correctly', async () => {
// Record 4 successful, 1 failed
for (let i = 0; i < 5; i++) {
await patternMemory.recordQuery({
widgetId: 'success-test',
queryType: 'success.query',
queryParams: {},
sourceUsed: 'success-source',
latencyMs: 50,
success: i < 4 // First 4 succeed, last fails
});
}
const successRate = await patternMemory.getSuccessRate('success-source', 'success.query');
expect(successRate).toBe(0.8); // 4 out of 5
});
it('should provide widget usage patterns', async () => {
// Record patterns for a widget using multiple sources
await patternMemory.recordQuery({
widgetId: 'multi-source-widget',
queryType: 'data.fetch',
queryParams: {},
sourceUsed: 'primary-source',
latencyMs: 30,
success: true
});
await patternMemory.recordQuery({
widgetId: 'multi-source-widget',
queryType: 'data.fetch',
queryParams: {},
sourceUsed: 'primary-source',
latencyMs: 40,
success: true
});
await patternMemory.recordQuery({
widgetId: 'multi-source-widget',
queryType: 'data.fetch',
queryParams: {},
sourceUsed: 'secondary-source',
latencyMs: 100,
success: true
});
const patterns = await patternMemory.getWidgetPatterns('multi-source-widget');
expect(patterns.widgetId).toBe('multi-source-widget');
expect(patterns.queryCount).toBe(3);
expect(patterns.commonSources[0]).toBe('primary-source'); // Most used
expect(patterns.successRate).toBe(1.0);
});
it('should provide comprehensive statistics', async () => {
const stats = await patternMemory.getStatistics();
expect(stats).toHaveProperty('totalPatterns');
expect(stats).toHaveProperty('uniqueWidgets');
expect(stats).toHaveProperty('uniqueSources');
expect(stats).toHaveProperty('avgLatencyMs');
expect(stats).toHaveProperty('successRate');
expect(stats).toHaveProperty('queriesLast24h');
expect(stats).toHaveProperty('queriesLast7d');
expect(stats).toHaveProperty('topSources');
expect(stats).toHaveProperty('topWidgets');
});
});
// ═══════════════════════════════════════════════════════════════════════════════
// FAILURE MEMORY TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('FailureMemory - Self-Healing Intelligence', () => {
let failureMemory: any;
beforeEach(async () => {
const { FailureMemory } = await import('../mcp/memory/FailureMemory.js');
failureMemory = new FailureMemory(new SqlJsStorageAdapter(db));
});
it('should record failures with context', async () => {
const error = new Error('Connection refused');
(error as any).code = 'ECONNREFUSED';
const id = await failureMemory.recordFailure({
sourceName: 'failing-source',
error,
queryContext: { operation: 'test' }
});
expect(id).toBeDefined();
expect(typeof id).toBe('string');
const history = await failureMemory.getFailureHistory('failing-source', 1);
expect(history.length).toBe(1);
expect(history[0].errorType).toBe('Error');
expect(history[0].errorMessage).toBe('Connection refused');
expect(history[0].errorContext.code).toBe('ECONNREFUSED');
});
it('should track recovery paths', async () => {
const error = new Error('Timeout');
// Record failure with recovery
await failureMemory.recordFailure({
sourceName: 'recovery-test',
error,
recoveryAction: 'retry-with-backoff',
recoverySuccess: true,
recoveryTimeMs: 500
});
await failureMemory.recordFailure({
sourceName: 'recovery-test',
error,
recoveryAction: 'retry-with-backoff',
recoverySuccess: true,
recoveryTimeMs: 600
});
await failureMemory.recordFailure({
sourceName: 'recovery-test',
error,
recoveryAction: 'use-fallback',
recoverySuccess: false,
recoveryTimeMs: 1000
});
const paths = await failureMemory.getRecoveryPaths('recovery-test', 'Error');
expect(paths.length).toBeGreaterThan(0);
// retry-with-backoff should be ranked higher (100% success)
const retryPath = paths.find((p: any) => p.action === 'retry-with-backoff');
expect(retryPath).toBeDefined();
expect(retryPath.successRate).toBe(1.0);
expect(retryPath.occurrences).toBe(2);
});
it('should detect recurring failures', async () => {
const error = new Error('Rate limited');
// Record 3 failures within a short time
for (let i = 0; i < 3; i++) {
await failureMemory.recordFailure({
sourceName: 'recurring-source',
error
});
}
const isRecurring = await failureMemory.isRecurringFailure('recurring-source', 'Error', 60);
expect(isRecurring).toBe(true);
});
it('should recommend best recovery action', async () => {
const error = new Error('DB connection lost');
// Create a history of recoveries
for (let i = 0; i < 3; i++) {
await failureMemory.recordFailure({
sourceName: 'db-source',
error,
recoveryAction: 'reconnect',
recoverySuccess: true,
recoveryTimeMs: 200
});
}
await failureMemory.recordFailure({
sourceName: 'db-source',
error,
recoveryAction: 'restart-pool',
recoverySuccess: false,
recoveryTimeMs: 5000
});
const best = await failureMemory.getBestRecoveryAction('db-source', 'Error');
expect(best).toBeDefined();
expect(best?.action).toBe('reconnect');
expect(best?.successRate).toBe(1.0);
});
it('should provide source health summary', async () => {
const summary = await failureMemory.getSourceHealthSummary('db-source');
expect(summary).toHaveProperty('sourceName');
expect(summary).toHaveProperty('totalFailures');
expect(summary).toHaveProperty('uniqueErrorTypes');
expect(summary).toHaveProperty('recoverySuccessRate');
expect(summary).toHaveProperty('isRecurring');
});
});
// ═══════════════════════════════════════════════════════════════════════════════
// DECISION ENGINE TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('DecisionEngine - AI-Powered Source Selection', () => {
let decisionEngine: any;
let cognitiveMemory: any;
beforeEach(async () => {
const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js');
const { DecisionEngine } = await import('../mcp/autonomous/DecisionEngine.js');
initCognitiveMemory(db);
cognitiveMemory = getCognitiveMemory();
decisionEngine = new DecisionEngine(cognitiveMemory);
});
it('should analyze query intent correctly', async () => {
const intent = await decisionEngine.analyzeIntent({
type: 'agents.list',
priority: 'high',
freshness: 'realtime'
});
expect(intent.type).toBe('agents.list');
expect(intent.domain).toBe('agents');
expect(intent.operation).toBe('list');
expect(intent.priority).toBe('high');
expect(intent.freshness).toBe('realtime');
});
it('should score sources with reasoning', async () => {
const mockSources = [
{
name: 'fast-source',
type: 'database',
capabilities: ['agents.list', 'agents.get'],
isHealthy: async () => true,
estimatedLatency: 20,
costPerQuery: 0
},
{
name: 'slow-source',
type: 'api',
capabilities: ['agents.*'],
isHealthy: async () => true,
estimatedLatency: 500,
costPerQuery: 0.01
}
];
const intent = {
type: 'agents.list',
domain: 'agents',
operation: 'list',
params: {},
priority: 'high' as const,
freshness: 'normal' as const
};
const scores = await decisionEngine.scoreAllSources(mockSources, intent);
expect(scores.length).toBe(2);
expect(scores[0].source.name).toBe('fast-source'); // Should be ranked first
expect(scores[0].score).toBeGreaterThan(scores[1].score);
expect(scores[0].reasoning).toBeDefined();
});
it('should make confident decisions', async () => {
const mockSources = [
{
name: 'primary',
type: 'database',
capabilities: ['*'],
isHealthy: async () => true,
estimatedLatency: 50,
costPerQuery: 0
}
];
const intent = {
type: 'test.query',
domain: 'test',
operation: 'query',
params: {},
priority: 'normal' as const,
freshness: 'normal' as const
};
const decision = await decisionEngine.decide(mockSources, intent);
expect(decision.selectedSource).toBeDefined();
expect(decision.selectedSource.name).toBe('primary');
expect(decision.confidence).toBeGreaterThan(0);
expect(decision.confidence).toBeLessThanOrEqual(1);
expect(decision.reasoning).toBeDefined();
expect(decision.alternatives).toBeInstanceOf(Array);
});
});
// ═══════════════════════════════════════════════════════════════════════════════
// SELF-HEALING ADAPTER TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('SelfHealingAdapter - Circuit Breaker & Recovery', () => {
let selfHealing: any;
beforeEach(async () => {
const { selfHealing: sh } = await import('../services/SelfHealingAdapter.js');
selfHealing = sh;
});
it('should have default healing strategies', () => {
// The adapter should initialize with default strategies
expect(selfHealing).toBeDefined();
});
it('should attempt healing and return result', async () => {
const error = new Error('Test Error');
const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing;
// Mock the internal logic if needed, or rely on integration
// Since we are testing integration, we expect actual behavior
// But heal returns HealingResult object now?
const healed = await selfHealing.heal(error, 'test-context', { source: 'test-source' });
expect(healed).toHaveProperty('healed');
expect(healed).toHaveProperty('strategyUsed');
});
it('should prevent recursive healing loops', async () => {
const error = new Error('Recursive Error');
const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing;
const first = await selfHealing.heal(error, 'recursion-test', { source: 'test-source' });
// If we somehow trigger during healing, it should handle gracefully
expect(first).toBeDefined();
});
it('should provide system status', async () => {
const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing;
const status = selfHealing.getSystemStatus();
expect(status).toHaveProperty('overallHealth');
expect(status).toHaveProperty('services');
expect(status).toHaveProperty('uptime');
expect(['HEALTHY', 'DEGRADED', 'CRITICAL']).toContain(status.overallHealth);
});
// it('should update service status', () => {
// selfHealing.updateServiceStatus('test-service', 'healthy');
// const status = selfHealing.getSystemStatus();
// expect(status.services).toContainEqual({ name: 'test-service', status: 'healthy' });
// });
// const testService = status.services.find((s: any) => s.name === 'test-service');
// expect(testService).toBeDefined();
// expect(testService?.status).toBe('healthy');
});
// ═══════════════════════════════════════════════════════════════════════════════
// AUTONOMOUS AGENT TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('AutonomousAgent - Orchestration & Learning', () => {
let agent: any;
let registry: any;
beforeEach(async () => {
const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js');
const { AutonomousAgent } = await import('../mcp/autonomous/AutonomousAgent.js');
const { getSourceRegistry } = await import('../mcp/SourceRegistry.js');
initCognitiveMemory(db);
const memory = getCognitiveMemory();
registry = getSourceRegistry();
// Register a test source
registry.registerSource({
name: 'test-agent-source',
type: 'test',
capabilities: ['test.*'],
isHealthy: async () => true,
estimatedLatency: 50,
costPerQuery: 0,
query: async () => ({ result: 'test-data' })
});
agent = new AutonomousAgent(memory, registry);
});
it('should route queries to appropriate sources', async () => {
const source = await agent.route({
type: 'test.query',
domain: 'test',
operation: 'query',
params: {}
});
expect(source).toBeDefined();
expect(source.name).toBe('test-agent-source');
});
it('should execute and learn from queries', async () => {
const result = await agent.executeAndLearn(
{
type: 'test.query',
widgetId: 'agent-test-widget'
},
async (source: any) => {
return { data: 'executed-result', source: source.name };
}
);
expect(result).toBeDefined();
expect(result.data).toBeDefined();
expect(result.source).toBeDefined();
expect(result.latencyMs).toBeGreaterThanOrEqual(0);
expect(result.cached).toBe(false);
});
it('should provide agent statistics', async () => {
const stats = await agent.getStats();
expect(stats).toHaveProperty('totalDecisions');
expect(stats).toHaveProperty('averageConfidence');
expect(stats).toHaveProperty('topSources');
});
});
// ═══════════════════════════════════════════════════════════════════════════════
// COGNITIVE MEMORY INTEGRATION TESTS
// ═══════════════════════════════════════════════════════════════════════════════
describe('CognitiveMemory - Unified Interface', () => {
let cognitiveMemory: any;
beforeEach(async () => {
const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js');
initCognitiveMemory(db);
cognitiveMemory = getCognitiveMemory();
});
it('should initialize correctly', () => {
expect(cognitiveMemory).toBeDefined();
});
it('should record successful queries', async () => {
await cognitiveMemory.recordSuccess({
widgetId: 'cognitive-test',
queryType: 'data.fetch',
queryParams: { id: 123 },
sourceUsed: 'test-source',
latencyMs: 75,
resultSize: 2048
});
// Query should be recorded in pattern memory
const patterns = await cognitiveMemory.patternMemory.getWidgetPatterns('cognitive-test');
expect(patterns.queryCount).toBeGreaterThan(0);
});
it('should record failures with recovery info', async () => {
const error = new Error('Test failure');
await cognitiveMemory.recordFailure({
sourceName: 'cognitive-failure-test',
error,
queryContext: { operation: 'test' },
recoveryAction: 'retry',
recoverySuccess: true,
recoveryTimeMs: 100
});
const history = await cognitiveMemory.failureMemory.getFailureHistory('cognitive-failure-test');
expect(history.length).toBeGreaterThan(0);
});
it('should get source intelligence combining patterns and failures', async () => {
const intel = await cognitiveMemory.getSourceIntelligence('test-source');
expect(intel).toHaveProperty('averageLatency');
expect(intel).toHaveProperty('overallSuccessRate');
expect(intel).toHaveProperty('recentFailures');
});
});