mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
cb27ee303f
## Major Features ### G4: Unified Memory Architecture - BinaryCache Integration - Integrate BinaryMetadataCache with UnifiedMemoryCoordinator - Add TRM pattern caching with 6x faster pattern loading - New methods: cacheTRMPattern(), getCachedTRMPattern(), persistBinaryCache() ### G6: OpenRouter Provider with Model Hot-Swap - Full ILLMProvider implementation for OpenRouter API (300+ models) - Model hot-swapping at runtime without restart - Smart environment detection for auto provider selection ### ruvLLM Integration (Issue #131) - SONA Learning Strategy with MicroLoRA/BaseLoRA/EWC++ - TRM Learning Strategy for trajectory tracking - ReasoningBank integration for pattern storage - 104 new integration tests Closes #131 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
283 lines
8.3 KiB
TypeScript
283 lines
8.3 KiB
TypeScript
/**
|
|
* Unit Tests for Constitution Schema
|
|
* Tests REAL implementation
|
|
*/
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import {
|
|
ConstitutionLoader,
|
|
validateConstitution,
|
|
loadConstitution,
|
|
mergeConstitutions,
|
|
getConstitutionForAgent,
|
|
getBaseConstitutionsPath,
|
|
listAvailableConstitutions
|
|
} from '../../../src/constitution/loader';
|
|
|
|
describe('Constitution Schema', () => {
|
|
const constitutionDir = path.join(__dirname, '../../../src/constitution/base');
|
|
let loader: ConstitutionLoader;
|
|
|
|
beforeEach(() => {
|
|
loader = new ConstitutionLoader();
|
|
});
|
|
|
|
describe('Schema Validation', () => {
|
|
it('should validate a valid constitution', () => {
|
|
const validConstitution = {
|
|
id: 'test-constitution',
|
|
name: 'Test Constitution',
|
|
version: '1.0.0',
|
|
description: 'A test constitution',
|
|
principles: [
|
|
{
|
|
id: 'principle-1',
|
|
name: 'Test Principle',
|
|
description: 'A test principle',
|
|
priority: 'high',
|
|
category: 'testing',
|
|
},
|
|
],
|
|
rules: [],
|
|
metrics: [],
|
|
thresholds: [],
|
|
metadata: {
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
status: 'active',
|
|
author: 'test',
|
|
applicableTo: ['*'],
|
|
},
|
|
};
|
|
|
|
const result = validateConstitution(validConstitution);
|
|
|
|
expect(result.valid).toBe(true);
|
|
expect(result.errors).toHaveLength(0);
|
|
});
|
|
|
|
it('should reject constitution without required fields', () => {
|
|
const invalidConstitution = {
|
|
name: 'Missing ID',
|
|
};
|
|
|
|
const result = validateConstitution(invalidConstitution);
|
|
|
|
expect(result.valid).toBe(false);
|
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should detect orphaned rules', () => {
|
|
const constitutionWithOrphanedRule = {
|
|
id: 'orphan-test',
|
|
name: 'Orphan Test',
|
|
version: '1.0.0',
|
|
description: 'Test',
|
|
principles: [
|
|
{
|
|
id: 'principle-1',
|
|
name: 'Principle 1',
|
|
description: 'Desc',
|
|
priority: 'high',
|
|
category: 'test',
|
|
},
|
|
],
|
|
rules: [
|
|
{
|
|
id: 'rule-1',
|
|
principleId: 'non-existent-principle',
|
|
condition: { field: 'test', operator: 'equals', value: 'x' },
|
|
action: { type: 'warn', message: 'test' },
|
|
severity: 'warning',
|
|
},
|
|
],
|
|
metrics: [],
|
|
thresholds: [],
|
|
metadata: { created: new Date().toISOString(), applicableTo: ['*'] },
|
|
};
|
|
|
|
const result = validateConstitution(constitutionWithOrphanedRule);
|
|
|
|
expect(result.valid).toBe(false);
|
|
expect(result.errors.some(e => e.code === 'ORPHANED_RULE')).toBe(true);
|
|
});
|
|
|
|
it('should warn about missing metric descriptions', () => {
|
|
const constitutionWithoutDesc = {
|
|
id: 'no-desc-test',
|
|
name: 'No Desc Test',
|
|
version: '1.0.0',
|
|
description: 'Test',
|
|
principles: [],
|
|
rules: [],
|
|
metrics: [
|
|
{
|
|
id: 'metric-1',
|
|
name: 'Test Metric',
|
|
unit: 'count',
|
|
aggregation: 'sum',
|
|
// No description
|
|
},
|
|
],
|
|
thresholds: [],
|
|
metadata: { created: new Date().toISOString(), applicableTo: ['*'] },
|
|
};
|
|
|
|
const result = validateConstitution(constitutionWithoutDesc);
|
|
|
|
expect(result.warnings.some(w => w.code === 'MISSING_DESCRIPTION')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Loading Constitutions', () => {
|
|
it('should load default constitution from file', () => {
|
|
const constitution = loadConstitution(
|
|
path.join(constitutionDir, 'default.constitution.json')
|
|
);
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.id).toBe('default');
|
|
expect(constitution.name).toBe('Default Quality Constitution');
|
|
expect(constitution.principles.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should load test-generation constitution', () => {
|
|
const constitution = loadConstitution(
|
|
path.join(constitutionDir, 'test-generation.constitution.json')
|
|
);
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.id).toBe('test-generation');
|
|
expect(constitution.metrics.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should load code-review constitution', () => {
|
|
const constitution = loadConstitution(
|
|
path.join(constitutionDir, 'code-review.constitution.json')
|
|
);
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.id).toBe('code-review');
|
|
});
|
|
|
|
it('should load performance constitution', () => {
|
|
const constitution = loadConstitution(
|
|
path.join(constitutionDir, 'performance.constitution.json')
|
|
);
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.id).toBe('performance');
|
|
});
|
|
|
|
it('should throw error for non-existent file', () => {
|
|
expect(() => {
|
|
loadConstitution('/non/existent/path.json');
|
|
}).toThrow('Constitution file not found');
|
|
});
|
|
|
|
it('should cache loaded constitutions', () => {
|
|
const filePath = path.join(constitutionDir, 'default.constitution.json');
|
|
|
|
const constitution1 = loader.loadConstitution(filePath);
|
|
const constitution2 = loader.loadConstitution(filePath);
|
|
|
|
// Should be the same object reference due to caching
|
|
expect(constitution1).toBe(constitution2);
|
|
|
|
const stats = loader.getCacheStats();
|
|
expect(stats.size).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('Merging Constitutions', () => {
|
|
it('should merge base and override constitutions', () => {
|
|
const base = loadConstitution(
|
|
path.join(constitutionDir, 'default.constitution.json')
|
|
);
|
|
const override = loadConstitution(
|
|
path.join(constitutionDir, 'test-generation.constitution.json')
|
|
);
|
|
|
|
const merged = mergeConstitutions(base, override);
|
|
|
|
// Override properties should take precedence
|
|
expect(merged.id).toBe(override.id);
|
|
expect(merged.name).toBe(override.name);
|
|
|
|
// Should have principles from both
|
|
expect(merged.principles.length).toBeGreaterThan(base.principles.length);
|
|
});
|
|
|
|
it('should preserve override metadata', () => {
|
|
const base = loadConstitution(
|
|
path.join(constitutionDir, 'default.constitution.json')
|
|
);
|
|
const override = loadConstitution(
|
|
path.join(constitutionDir, 'test-generation.constitution.json')
|
|
);
|
|
|
|
const merged = mergeConstitutions(base, override);
|
|
|
|
expect(merged.metadata).toEqual(override.metadata);
|
|
});
|
|
});
|
|
|
|
describe('Agent Constitution Lookup', () => {
|
|
it('should get constitution for qe-test-generator', () => {
|
|
const constitution = getConstitutionForAgent('qe-test-generator');
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.name).toBeDefined();
|
|
});
|
|
|
|
it('should get constitution for qe-code-reviewer', () => {
|
|
const constitution = getConstitutionForAgent('qe-code-reviewer');
|
|
|
|
expect(constitution).toBeDefined();
|
|
});
|
|
|
|
it('should get constitution for qe-performance-tester', () => {
|
|
const constitution = getConstitutionForAgent('qe-performance-tester');
|
|
|
|
expect(constitution).toBeDefined();
|
|
});
|
|
|
|
it('should fall back to default for unknown agent', () => {
|
|
const constitution = getConstitutionForAgent('unknown-agent-type');
|
|
|
|
expect(constitution).toBeDefined();
|
|
expect(constitution.id).toBe('default');
|
|
});
|
|
});
|
|
|
|
describe('Utility Functions', () => {
|
|
it('should get base constitutions path', () => {
|
|
const basePath = getBaseConstitutionsPath();
|
|
|
|
expect(basePath).toBeDefined();
|
|
expect(fs.existsSync(basePath)).toBe(true);
|
|
});
|
|
|
|
it('should list available constitutions', () => {
|
|
const available = listAvailableConstitutions();
|
|
|
|
expect(available).toContain('default');
|
|
expect(available).toContain('test-generation');
|
|
expect(available).toContain('code-review');
|
|
expect(available).toContain('performance');
|
|
});
|
|
|
|
it('should clear cache', () => {
|
|
const filePath = path.join(constitutionDir, 'default.constitution.json');
|
|
loader.loadConstitution(filePath);
|
|
|
|
expect(loader.getCacheStats().size).toBe(1);
|
|
|
|
loader.clearCache();
|
|
|
|
expect(loader.getCacheStats().size).toBe(0);
|
|
});
|
|
});
|
|
});
|