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>
266 lines
8.6 KiB
TypeScript
266 lines
8.6 KiB
TypeScript
/**
|
|
* Unit Tests: TokenOptimizerService
|
|
* ADR-042: Token Optimization Service Tests
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import { TokenOptimizerService, initializeTokenOptimizer } from '../../../src/optimization/token-optimizer-service.js';
|
|
import { TokenMetricsCollector } from '../../../src/learning/token-tracker.js';
|
|
import { InMemoryBackend } from '../../../src/kernel/memory-backend.js';
|
|
|
|
describe('TokenOptimizerService', () => {
|
|
let memoryBackend: InMemoryBackend;
|
|
|
|
beforeEach(async () => {
|
|
// Reset singletons
|
|
TokenMetricsCollector.reset();
|
|
(TokenOptimizerService as any).initialized = false;
|
|
(TokenOptimizerService as any).optimizer = null;
|
|
(TokenOptimizerService as any).patternStore = null;
|
|
|
|
// Create fresh memory backend
|
|
memoryBackend = new InMemoryBackend();
|
|
await memoryBackend.initialize();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
// Cleanup
|
|
await memoryBackend.dispose();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('Initialization', () => {
|
|
it('should start disabled before initialization', () => {
|
|
expect(TokenOptimizerService.isEnabled()).toBe(false);
|
|
});
|
|
|
|
it('should be enabled after initialization', async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
|
|
expect(TokenOptimizerService.isEnabled()).toBe(true);
|
|
});
|
|
|
|
it('should stay disabled if config.enabled is false', async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: false });
|
|
|
|
expect(TokenOptimizerService.isEnabled()).toBe(false);
|
|
});
|
|
|
|
it('should be idempotent - multiple initializations have no effect', async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
const firstEnabled = TokenOptimizerService.isEnabled();
|
|
|
|
// Second initialization should be a no-op
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: false });
|
|
const secondEnabled = TokenOptimizerService.isEnabled();
|
|
|
|
expect(firstEnabled).toBe(true);
|
|
expect(secondEnabled).toBe(true); // Still true from first init
|
|
});
|
|
});
|
|
|
|
describe('Early Exit Check (Not Initialized)', () => {
|
|
it('should return canExit=false when not initialized', async () => {
|
|
const result = await TokenOptimizerService.checkEarlyExit({
|
|
description: 'Test task',
|
|
domain: 'test-generation',
|
|
});
|
|
|
|
expect(result.canExit).toBe(false);
|
|
expect(result.reason).toBe('no_matching_pattern');
|
|
expect(result.explanation).toContain('not initialized');
|
|
});
|
|
|
|
it('should return canExit=false for checkTaskEarlyExit when not initialized', async () => {
|
|
const result = await TokenOptimizerService.checkTaskEarlyExit(
|
|
'Generate unit tests',
|
|
'test-generation'
|
|
);
|
|
|
|
expect(result.canExit).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Early Exit Check (Initialized)', () => {
|
|
beforeEach(async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
});
|
|
|
|
it('should return canExit=false when no patterns match', async () => {
|
|
const result = await TokenOptimizerService.checkTaskEarlyExit(
|
|
'Generate unit tests for new service',
|
|
'test-generation'
|
|
);
|
|
|
|
expect(result.canExit).toBe(false);
|
|
// Reason should be one of the valid reasons (no patterns yet)
|
|
expect(['no_matching_pattern', 'confidence_too_low', 'pattern_too_old']).toContain(result.reason);
|
|
});
|
|
|
|
it('should record early exits in TokenMetricsCollector', async () => {
|
|
// Spy on TokenMetricsCollector
|
|
const recordSpy = vi.spyOn(TokenMetricsCollector, 'recordEarlyExit');
|
|
|
|
// Even without a match, the service should work
|
|
await TokenOptimizerService.checkTaskEarlyExit(
|
|
'Test task',
|
|
'test-generation'
|
|
);
|
|
|
|
// No early exit recorded because no pattern matched
|
|
expect(recordSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('Pattern Storage', () => {
|
|
beforeEach(async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
});
|
|
|
|
it('should store patterns successfully', async () => {
|
|
const result = await TokenOptimizerService.storePattern({
|
|
name: 'test-pattern',
|
|
domain: 'test-generation',
|
|
description: 'A test pattern for unit testing',
|
|
template: 'describe("{{className}}", () => { it("should work", () => {}); });',
|
|
confidence: 0.9,
|
|
successRate: 0.95,
|
|
usageCount: 10,
|
|
estimatedTokensSaved: 500,
|
|
tags: ['unit-test', 'vitest'],
|
|
});
|
|
|
|
// storePattern returns the pattern ID string or null
|
|
// When store succeeds, it should return a string ID
|
|
// Note: The result could be null if the store operation fails internally
|
|
expect(result === null || typeof result === 'string').toBe(true);
|
|
});
|
|
|
|
it('should return null when storing pattern fails (not initialized)', async () => {
|
|
// Reset to uninitialized state
|
|
(TokenOptimizerService as any).patternStore = null;
|
|
|
|
const patternId = await TokenOptimizerService.storePattern({
|
|
name: 'test-pattern',
|
|
domain: 'test-generation',
|
|
description: 'A test pattern',
|
|
template: 'test template',
|
|
confidence: 0.9,
|
|
successRate: 0.95,
|
|
usageCount: 1,
|
|
estimatedTokensSaved: 100,
|
|
tags: [],
|
|
});
|
|
|
|
expect(patternId).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Reuse Statistics', () => {
|
|
it('should return null or empty stats when not initialized', () => {
|
|
const stats = TokenOptimizerService.getReuseStats();
|
|
|
|
// When not initialized, stats may be null or have 0 attempts
|
|
if (stats) {
|
|
expect(stats.totalAttempts).toBeGreaterThanOrEqual(0);
|
|
} else {
|
|
expect(stats).toBeNull();
|
|
}
|
|
});
|
|
|
|
it('should track reuse attempts after initialization', async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
|
|
// Make some early exit attempts
|
|
await TokenOptimizerService.checkTaskEarlyExit('Task 1', 'test-generation');
|
|
await TokenOptimizerService.checkTaskEarlyExit('Task 2', 'coverage-analysis');
|
|
await TokenOptimizerService.checkTaskEarlyExit('Task 3', 'test-generation');
|
|
|
|
const stats = TokenOptimizerService.getReuseStats();
|
|
|
|
expect(stats).toBeDefined();
|
|
expect(stats?.totalAttempts).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe('Failed Reuse Recording', () => {
|
|
beforeEach(async () => {
|
|
await initializeTokenOptimizer(memoryBackend, { enabled: true });
|
|
});
|
|
|
|
it('should record failed reuse without throwing', () => {
|
|
// Should not throw even with non-existent pattern
|
|
expect(() => {
|
|
TokenOptimizerService.recordFailedReuse('non-existent-pattern-id');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should be no-op when not initialized', () => {
|
|
(TokenOptimizerService as any).optimizer = null;
|
|
|
|
// Should not throw
|
|
expect(() => {
|
|
TokenOptimizerService.recordFailedReuse('pattern-id');
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('Configuration', () => {
|
|
it('should return current config', async () => {
|
|
await initializeTokenOptimizer(memoryBackend, {
|
|
enabled: true,
|
|
verbose: true,
|
|
});
|
|
|
|
const config = TokenOptimizerService.getConfig();
|
|
|
|
expect(config).toBeDefined();
|
|
expect(config.enabled).toBe(true);
|
|
expect(config.verbose).toBe(true);
|
|
});
|
|
|
|
it('should use default config values', async () => {
|
|
await initializeTokenOptimizer(memoryBackend);
|
|
|
|
const config = TokenOptimizerService.getConfig();
|
|
|
|
expect(config.enabled).toBe(true); // Default
|
|
expect(config.verbose).toBe(false); // Default
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Token Bootstrap Integration', () => {
|
|
beforeEach(() => {
|
|
TokenMetricsCollector.reset();
|
|
(TokenOptimizerService as any).initialized = false;
|
|
(TokenOptimizerService as any).optimizer = null;
|
|
(TokenOptimizerService as any).patternStore = null;
|
|
});
|
|
|
|
it('should work end-to-end with bootstrap', async () => {
|
|
const { bootstrapTokenTracking, shutdownTokenTracking, isTokenTrackingInitialized } =
|
|
await import('../../../src/init/token-bootstrap.js');
|
|
|
|
// Reset any previous state
|
|
await shutdownTokenTracking();
|
|
|
|
expect(isTokenTrackingInitialized()).toBe(false);
|
|
|
|
// Bootstrap
|
|
await bootstrapTokenTracking({
|
|
enableOptimization: true,
|
|
enablePersistence: false, // Skip persistence for unit test
|
|
verbose: false,
|
|
});
|
|
|
|
expect(isTokenTrackingInitialized()).toBe(true);
|
|
expect(TokenOptimizerService.isEnabled()).toBe(true);
|
|
|
|
// Cleanup
|
|
await shutdownTokenTracking();
|
|
|
|
expect(isTokenTrackingInitialized()).toBe(false);
|
|
});
|
|
});
|