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>
580 lines
17 KiB
TypeScript
580 lines
17 KiB
TypeScript
/**
|
|
* Agentic QE v3 - Parameterized Plugin Test Generator
|
|
*
|
|
* Consolidates common test patterns for all 12 domain plugins into a single
|
|
* reusable test generator. Reduces code duplication while maintaining
|
|
* comprehensive test coverage.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* generatePluginTests('code-intelligence', CodeIntelligencePlugin, {
|
|
* expectedVersion: '1.0.0',
|
|
* expectedDependencies: [],
|
|
* taskHandlers: ['index', 'search', 'analyze-impact', 'query-dependencies'],
|
|
* factoryFunction: createCodeIntelligencePlugin,
|
|
* requiresAgentCoordinator: true,
|
|
* apiMethods: ['index', 'search', 'analyzeImpact', 'getDependencies'],
|
|
* customTests: () => {
|
|
* it('should handle knowledge graph operations', () => { ... });
|
|
* },
|
|
* });
|
|
* ```
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import type { DomainName } from '../../../src/shared/types';
|
|
import type { BaseDomainPlugin, DomainConsensusConfig } from '../../../src/domains/domain-interface';
|
|
import type { EventBus, MemoryBackend, AgentCoordinator } from '../../../src/kernel/interfaces';
|
|
import {
|
|
MockEventBus,
|
|
MockMemoryBackend,
|
|
MockAgentCoordinator,
|
|
createMockTaskRequest,
|
|
createMockCallback,
|
|
createMockEvent,
|
|
verifyIdleHealth,
|
|
expectSuccess,
|
|
expectError,
|
|
sampleTasks,
|
|
} from './plugin-test-utils';
|
|
|
|
// ============================================================================
|
|
// Configuration Types
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Configuration for plugin test generation
|
|
*/
|
|
export interface PluginTestConfig<T extends BaseDomainPlugin> {
|
|
/**
|
|
* Expected plugin version (e.g., '1.0.0')
|
|
*/
|
|
expectedVersion: string;
|
|
|
|
/**
|
|
* Expected domain dependencies (e.g., ['test-generation'])
|
|
*/
|
|
expectedDependencies: DomainName[];
|
|
|
|
/**
|
|
* List of task types the plugin should handle
|
|
*/
|
|
taskHandlers: string[];
|
|
|
|
/**
|
|
* Factory function to create the plugin
|
|
*/
|
|
factoryFunction: PluginFactory<T>;
|
|
|
|
/**
|
|
* Whether the plugin requires an AgentCoordinator dependency
|
|
* @default true
|
|
*/
|
|
requiresAgentCoordinator?: boolean;
|
|
|
|
/**
|
|
* Expected API method names (for getAPI tests)
|
|
*/
|
|
apiMethods?: string[];
|
|
|
|
/**
|
|
* Expected internal accessor methods (e.g., getCoordinator, getKnowledgeGraph)
|
|
*/
|
|
internalAccessors?: string[];
|
|
|
|
/**
|
|
* Sample tasks from the sampleTasks object for this domain
|
|
* Key should be the domain's key in sampleTasks
|
|
*/
|
|
sampleTasksKey?: keyof typeof sampleTasks;
|
|
|
|
/**
|
|
* Task validation tests: taskType -> missing field -> expected error
|
|
*/
|
|
taskValidationTests?: TaskValidationConfig[];
|
|
|
|
/**
|
|
* Custom event handling tests
|
|
*/
|
|
eventTests?: EventTestConfig[];
|
|
|
|
/**
|
|
* Whether the plugin has custom health behavior (different from verifyIdleHealth)
|
|
*/
|
|
customHealthBehavior?: {
|
|
/** Initial total agents (default: 0) */
|
|
initialTotalAgents?: number;
|
|
/** Initial idle agents (default: 0) */
|
|
initialIdleAgents?: number;
|
|
};
|
|
|
|
/**
|
|
* Optional configuration for factory function tests
|
|
*/
|
|
factoryConfig?: Record<string, unknown>;
|
|
|
|
/**
|
|
* Additional custom tests to run
|
|
*/
|
|
customTests?: () => void;
|
|
}
|
|
|
|
/**
|
|
* Configuration for task validation tests
|
|
*/
|
|
export interface TaskValidationConfig {
|
|
taskType: string;
|
|
missingField: string;
|
|
partialPayload?: Record<string, unknown>;
|
|
expectedErrorContains: string;
|
|
}
|
|
|
|
/**
|
|
* Configuration for event handling tests
|
|
*/
|
|
export interface EventTestConfig {
|
|
eventType: string;
|
|
source: DomainName;
|
|
payload: Record<string, unknown>;
|
|
expectation: 'activity-updated' | 'memory-stored';
|
|
memoryKey?: string;
|
|
}
|
|
|
|
/**
|
|
* Plugin factory function type
|
|
*/
|
|
export type PluginFactory<T extends BaseDomainPlugin> = (
|
|
eventBus: EventBus,
|
|
memory: MemoryBackend,
|
|
agentCoordinator?: AgentCoordinator,
|
|
config?: Record<string, unknown>
|
|
) => T;
|
|
|
|
/**
|
|
* Plugin constructor type - with agent coordinator
|
|
*/
|
|
export type PluginConstructorWithCoordinator<T extends BaseDomainPlugin> = new (
|
|
eventBus: EventBus,
|
|
memory: MemoryBackend,
|
|
agentCoordinator: AgentCoordinator
|
|
) => T;
|
|
|
|
/**
|
|
* Plugin constructor type - without agent coordinator
|
|
*/
|
|
export type PluginConstructorWithoutCoordinator<T extends BaseDomainPlugin> = new (
|
|
eventBus: EventBus,
|
|
memory: MemoryBackend
|
|
) => T;
|
|
|
|
/**
|
|
* Union of plugin constructor types
|
|
*/
|
|
export type PluginConstructor<T extends BaseDomainPlugin> =
|
|
| PluginConstructorWithCoordinator<T>
|
|
| PluginConstructorWithoutCoordinator<T>;
|
|
|
|
// ============================================================================
|
|
// Test Generator
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Generates comprehensive tests for a domain plugin
|
|
*
|
|
* This generator creates standardized tests for:
|
|
* - Metadata (name, version, dependencies)
|
|
* - Lifecycle (initialize, dispose, ready state)
|
|
* - Health tracking (idle status, agent counts)
|
|
* - Task handler registration
|
|
* - API exposure
|
|
* - Integration configuration (MinCut, Consensus defaults)
|
|
*
|
|
* @param pluginName - Domain name (e.g., 'code-intelligence')
|
|
* @param PluginClass - Plugin constructor class
|
|
* @param config - Test configuration
|
|
*/
|
|
export function generatePluginTests<T extends BaseDomainPlugin>(
|
|
pluginName: DomainName,
|
|
PluginClass: PluginConstructor<T>,
|
|
config: PluginTestConfig<T>
|
|
): void {
|
|
const {
|
|
expectedVersion,
|
|
expectedDependencies,
|
|
taskHandlers,
|
|
factoryFunction,
|
|
requiresAgentCoordinator = true,
|
|
apiMethods = [],
|
|
internalAccessors = [],
|
|
sampleTasksKey,
|
|
taskValidationTests = [],
|
|
eventTests = [],
|
|
customHealthBehavior,
|
|
factoryConfig,
|
|
customTests,
|
|
} = config;
|
|
|
|
describe(`${pluginName}Plugin`, () => {
|
|
let plugin: T;
|
|
let eventBus: MockEventBus;
|
|
let memory: MockMemoryBackend;
|
|
let agentCoordinator: MockAgentCoordinator;
|
|
|
|
beforeEach(() => {
|
|
eventBus = new MockEventBus();
|
|
memory = new MockMemoryBackend();
|
|
agentCoordinator = new MockAgentCoordinator();
|
|
|
|
if (requiresAgentCoordinator) {
|
|
plugin = new (PluginClass as PluginConstructorWithCoordinator<T>)(
|
|
eventBus,
|
|
memory,
|
|
agentCoordinator
|
|
);
|
|
} else {
|
|
plugin = new (PluginClass as PluginConstructorWithoutCoordinator<T>)(eventBus, memory);
|
|
}
|
|
});
|
|
|
|
afterEach(async () => {
|
|
if (plugin.isReady()) {
|
|
await plugin.dispose();
|
|
}
|
|
await eventBus.dispose();
|
|
await memory.dispose();
|
|
await agentCoordinator.dispose();
|
|
});
|
|
|
|
// ========================================================================
|
|
// Metadata Tests
|
|
// ========================================================================
|
|
|
|
describe('metadata', () => {
|
|
it('should have correct domain name', () => {
|
|
expect(plugin.name).toBe(pluginName);
|
|
});
|
|
|
|
it('should have correct version', () => {
|
|
expect(plugin.version).toBe(expectedVersion);
|
|
});
|
|
|
|
if (expectedDependencies.length === 0) {
|
|
it('should have no required dependencies', () => {
|
|
expect(plugin.dependencies).toEqual([]);
|
|
});
|
|
} else {
|
|
it('should have correct dependencies', () => {
|
|
for (const dep of expectedDependencies) {
|
|
expect(plugin.dependencies).toContain(dep);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// ========================================================================
|
|
// Lifecycle Tests
|
|
// ========================================================================
|
|
|
|
describe('lifecycle', () => {
|
|
it('should not be ready before initialization', () => {
|
|
expect(plugin.isReady()).toBe(false);
|
|
});
|
|
|
|
it('should be ready after initialization', async () => {
|
|
await plugin.initialize();
|
|
expect(plugin.isReady()).toBe(true);
|
|
});
|
|
|
|
it('should have idle health status after initialization', async () => {
|
|
await plugin.initialize();
|
|
const health = plugin.getHealth();
|
|
|
|
if (customHealthBehavior) {
|
|
expect(health.status).toBe('idle');
|
|
if (customHealthBehavior.initialTotalAgents !== undefined) {
|
|
expect(health.agents.total).toBe(customHealthBehavior.initialTotalAgents);
|
|
}
|
|
if (customHealthBehavior.initialIdleAgents !== undefined) {
|
|
expect(health.agents.idle).toBe(customHealthBehavior.initialIdleAgents);
|
|
}
|
|
} else {
|
|
verifyIdleHealth(health);
|
|
}
|
|
});
|
|
|
|
it('should not be ready after disposal', async () => {
|
|
await plugin.initialize();
|
|
await plugin.dispose();
|
|
expect(plugin.isReady()).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// Factory Function Tests
|
|
// ========================================================================
|
|
|
|
describe('factory function', () => {
|
|
it('should create plugin via factory function', () => {
|
|
let createdPlugin: T;
|
|
if (requiresAgentCoordinator) {
|
|
createdPlugin = factoryFunction(eventBus, memory, agentCoordinator);
|
|
} else {
|
|
createdPlugin = factoryFunction(eventBus, memory);
|
|
}
|
|
expect(createdPlugin).toBeInstanceOf(PluginClass);
|
|
expect(createdPlugin.name).toBe(pluginName);
|
|
});
|
|
|
|
if (factoryConfig) {
|
|
it('should accept optional configuration', () => {
|
|
let createdPlugin: T;
|
|
if (requiresAgentCoordinator) {
|
|
createdPlugin = factoryFunction(eventBus, memory, agentCoordinator, factoryConfig);
|
|
} else {
|
|
createdPlugin = factoryFunction(eventBus, memory, undefined, factoryConfig);
|
|
}
|
|
expect(createdPlugin).toBeInstanceOf(PluginClass);
|
|
});
|
|
}
|
|
});
|
|
|
|
// ========================================================================
|
|
// API Tests
|
|
// ========================================================================
|
|
|
|
if (apiMethods.length > 0 || internalAccessors.length > 0) {
|
|
describe('getAPI', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
if (apiMethods.length > 0) {
|
|
it('should return API with all expected methods', () => {
|
|
const api = plugin.getAPI<Record<string, unknown>>();
|
|
for (const method of apiMethods) {
|
|
expect(api).toHaveProperty(method);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (internalAccessors.length > 0) {
|
|
it('should return API with internal accessor methods', () => {
|
|
const api = plugin.getAPI<Record<string, unknown>>();
|
|
for (const accessor of internalAccessors) {
|
|
expect(api).toHaveProperty(accessor);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================================================
|
|
// canHandleTask Tests
|
|
// ========================================================================
|
|
|
|
describe('canHandleTask', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
for (const taskType of taskHandlers) {
|
|
it(`should handle ${taskType} task type`, () => {
|
|
expect(plugin.canHandleTask(taskType)).toBe(true);
|
|
});
|
|
}
|
|
|
|
it('should not handle unknown task types', () => {
|
|
expect(plugin.canHandleTask('unknown-task')).toBe(false);
|
|
expect(plugin.canHandleTask('')).toBe(false);
|
|
// Use a task type that no domain handles
|
|
expect(plugin.canHandleTask('completely-nonexistent-task-xyz')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// executeTask Tests
|
|
// ========================================================================
|
|
|
|
describe('executeTask', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
// Test with sample task if available
|
|
if (sampleTasksKey && taskHandlers.length > 0) {
|
|
const domainTasks = sampleTasks[sampleTasksKey];
|
|
if (domainTasks) {
|
|
const firstTaskKey = Object.keys(domainTasks)[0] as keyof typeof domainTasks;
|
|
if (firstTaskKey) {
|
|
it('should accept valid task', async () => {
|
|
const request = domainTasks[firstTaskKey];
|
|
const { callback } = createMockCallback();
|
|
|
|
const acceptResult = await plugin.executeTask(request, callback);
|
|
expectSuccess(acceptResult);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
it('should return error for unknown task type', async () => {
|
|
const request = createMockTaskRequest('unknown-task', {});
|
|
const { callback } = createMockCallback();
|
|
|
|
const result = await plugin.executeTask(request, callback);
|
|
expectError(result);
|
|
expect(result.error.message).toContain('no handler');
|
|
});
|
|
|
|
// Task validation tests
|
|
for (const validation of taskValidationTests) {
|
|
it(`should return error for ${validation.taskType} with missing ${validation.missingField}`, async () => {
|
|
const request = createMockTaskRequest(
|
|
validation.taskType,
|
|
validation.partialPayload ?? {}
|
|
);
|
|
const { callback, waitForResult } = createMockCallback();
|
|
|
|
const acceptResult = await plugin.executeTask(request, callback);
|
|
expectSuccess(acceptResult);
|
|
|
|
const result = await waitForResult();
|
|
expect(result.success).toBe(false);
|
|
expect(result.error).toContain(validation.expectedErrorContains);
|
|
});
|
|
}
|
|
});
|
|
|
|
// ========================================================================
|
|
// Event Handling Tests
|
|
// ========================================================================
|
|
|
|
if (eventTests.length > 0) {
|
|
describe('event handling', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
for (const eventTest of eventTests) {
|
|
it(`should handle ${eventTest.eventType} events`, async () => {
|
|
const event = createMockEvent(
|
|
eventTest.eventType,
|
|
eventTest.source,
|
|
eventTest.payload
|
|
);
|
|
|
|
await plugin.handleEvent(event);
|
|
|
|
if (eventTest.expectation === 'activity-updated') {
|
|
const health = plugin.getHealth();
|
|
expect(health.lastActivity).toBeInstanceOf(Date);
|
|
} else if (eventTest.expectation === 'memory-stored' && eventTest.memoryKey) {
|
|
const stored = await memory.get(eventTest.memoryKey);
|
|
expect(stored).toBeDefined();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================================================
|
|
// Health Tracking Tests
|
|
// ========================================================================
|
|
|
|
describe('health tracking', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
it('should start with idle status', () => {
|
|
const health = plugin.getHealth();
|
|
expect(health.status).toBe('idle');
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// Integration Configuration Tests
|
|
// ========================================================================
|
|
|
|
describe('integration configuration', () => {
|
|
beforeEach(async () => {
|
|
await plugin.initialize();
|
|
});
|
|
|
|
it('should not have MinCut integration by default', () => {
|
|
expect(plugin.hasMinCutIntegration()).toBe(false);
|
|
});
|
|
|
|
it('should not have consensus enabled by default', () => {
|
|
expect(plugin.hasConsensusEnabled()).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// Custom Tests
|
|
// ========================================================================
|
|
|
|
if (customTests) {
|
|
describe('domain-specific tests', () => {
|
|
customTests();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Generate standard task validation tests for common patterns
|
|
*/
|
|
export function createTaskValidationTests(
|
|
tests: Array<{
|
|
taskType: string;
|
|
missingField: string;
|
|
partialPayload?: Record<string, unknown>;
|
|
}>
|
|
): TaskValidationConfig[] {
|
|
return tests.map((test) => ({
|
|
taskType: test.taskType,
|
|
missingField: test.missingField,
|
|
partialPayload: test.partialPayload,
|
|
expectedErrorContains: `missing ${test.missingField}`,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Generate standard event handling tests
|
|
*/
|
|
export function createEventTests(
|
|
tests: Array<{
|
|
eventType: string;
|
|
source: DomainName;
|
|
payload: Record<string, unknown>;
|
|
memoryKey?: string;
|
|
}>
|
|
): EventTestConfig[] {
|
|
return tests.map((test) => ({
|
|
eventType: test.eventType,
|
|
source: test.source,
|
|
payload: test.payload,
|
|
expectation: test.memoryKey ? 'memory-stored' : 'activity-updated',
|
|
memoryKey: test.memoryKey,
|
|
}));
|
|
}
|
|
|
|
// Re-export utilities for convenience
|
|
export {
|
|
MockEventBus,
|
|
MockMemoryBackend,
|
|
MockAgentCoordinator,
|
|
createMockTaskRequest,
|
|
createMockCallback,
|
|
createMockEvent,
|
|
verifyIdleHealth,
|
|
expectSuccess,
|
|
expectError,
|
|
sampleTasks,
|
|
};
|