mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2b71ae4eb8
## 🔒 Security Fixes ### eval() Removal - Critical Security Vulnerability Fixed - **File**: `src/agents/TestDataArchitectAgent.ts` - **Impact**: Eliminated arbitrary code execution risk - **Solution**: Implemented safe expression evaluator with parser-based approach - **Supports**: - Comparison operators: `===`, `!==`, `==`, `!=`, `>`, `<`, `>=`, `<=` - Logical operators: `&&`, `||` - Boolean values: `true`, `false` - Number and string comparisons - **Lines**: 102 lines added (safeEvaluateExpression + parseValue methods) ## ✨ UX Enhancements ### CLAUDE.md Append Strategy - User-Requested Feature - **File**: `src/cli/commands/init.ts` - **User Feedback**: "AQE instructions should append by default, not prepend" - **Interactive Mode**: Prompts user to choose prepend or append - **--yes Mode**: Defaults to append (less disruptive to existing configs) - **Features**: - Auto-backup existing CLAUDE.md to CLAUDE.md.backup - Clear visual separator (---) between sections - User confirmation messages - **Lines**: 49 lines modified ### CLI Skills Count Fix - Accurate Display - **File**: `src/cli/commands/skills/index.ts` - **Before**: Showed "8/17" (hardcoded, incorrect) - **After**: Shows "34/34" (dynamic, accurate) - **Solution**: Added `getTotalQESkillsCount()` method using `QE_SKILLS.flatMap()` - **Future-proof**: Auto-updates when skills added - **Lines**: 11 lines modified ## 🎓 New Agent - Cherry-Picked from PR #22 ### CodeComplexityAnalyzerAgent Integration - **Author**: @mondweep (PR #22) - **Purpose**: Educational code complexity analysis and refactoring recommendations - **Files**: 7 files, 2,758 lines of code - **Capabilities**: - Cyclomatic complexity calculation - Cognitive complexity analysis - AI-powered refactoring suggestions - Quality scoring (0-100 scale) - Memory integration (`aqe/complexity/*`) - **Test Coverage**: 529 lines, 13+ test scenarios - **Documentation**: 754 lines (README + Claude agent definition) - **Examples**: 871 lines (3 interactive examples) ## 🔧 Code Quality Improvements ### Agent Enhancements - **BaseAgent.ts**: 136 lines modified - Improved property access patterns - Better encapsulation with public accessors - Enhanced lifecycle management - **TestGeneratorAgent.ts**: 377 lines added - Comprehensive null safety improvements - Enhanced error handling - Better context validation - **QualityGateAgent.ts**: 64 lines modified - Improved context handling - Better validation logic ### MCP Handler Improvements - **test-generate.ts**: 33 lines modified (better parameter handling) - **quality-analyze.ts**: 28 lines modified (context fix) - **regression-risk-analyze.ts**: 85 lines modified (parameter aliasing fix) ### Infrastructure Additions - **AgentLifecycleManager.ts**: 274 new lines (lifecycle coordination) - **SwarmMemoryManager.ts**: 3 lines modified (improved coordination) - **AgentRegistry.ts**: 20 lines added (better agent registration) ## 🧪 Testing Enhancements ### New Integration Tests (5 Test Suites) - **fleet-management.integration.test.ts**: 491 lines - **parameter-validation.integration.test.ts**: 606 lines - **quality-analyze.integration.test.ts**: 393 lines - **regression-risk.integration.test.ts**: 505 lines - **test-execution.integration.test.ts**: 536 lines - **test-harness.ts**: 289 lines (shared test utilities) - **Total**: 2,820 lines of integration test coverage ### Enhanced Unit Tests - **BaseAgent.enhanced.test.ts**: 909 lines (comprehensive BaseAgent testing) - **TestGeneratorAgent.null-safety.test.ts**: 1,511 lines (null safety validation) - **Total**: 2,420 lines of new unit test coverage ### Test Improvements - **PredictionTools.test.ts**: 118 lines modified - **Agent.test.ts**: 11 lines modified ## 📚 Documentation Expansion (27,439 insertions) ### Release Documentation (7 documents) - **RELEASE-1.3.6-PREPARATION.md**: 443 lines (complete release checklist) - **SECURITY-SCAN-RELEASE-1.3.6.md**: 422 lines (security validation) - **THREE-ISSUE-FIXES-SUMMARY.md**: 196 lines (user-reported fixes) - **CHERRY-PICK-PR22-SUMMARY.md**: 412 lines (integration details) - **FINAL-STATUS-REPORT-2025-10-30.md**: 403 lines (status summary) - **FINAL-MCP-VERIFICATION-SUMMARY.md**: 338 lines (MCP validation) - **MCP-PRODUCTION-READINESS-REPORT.md**: 318 lines (production readiness) ### Technical Documentation (7 documents) - **MCP-TOOLS-REFERENCE.md**: 1,555 lines (complete MCP API reference) - **MCP-TOOLS-USER-GUIDE.md**: 626 lines (user guide) - **MCP-TOOLS-AUTO-GENERATED.md**: 934 lines (auto-generated docs) - **MCP-TOOLS-MIGRATION.md**: 437 lines (migration guide) - **complexity-mitigation-plan.md**: 537 lines (code quality roadmap) - **swarm-execution-report.md**: 985 lines (execution analysis) - **HONEST-IMPLEMENTATION-ANALYSIS.md**: 62 lines (transparency report) ### Fix Documentation (7 documents) - **MCP-TOOLS-COMPLETE-FIX-REPORT.md**: 619 lines (comprehensive fix report) - **three-issue-fixes.md**: 345 lines (user-reported fixes) - **quality-analyze-context-fix.md**: 195 lines - **regression-risk-parameter-aliasing.md**: 303 lines - **test-generator-agent-null-safety-fixes.md**: 422 lines - **typescript-compilation-errors-fixed-2025-10-30.md**: 273 lines - **MCP-TOOLS-FIX-SUMMARY.md**: 272 lines ### Refactoring Plans (4 documents) - **swarm-memory-manager-refactoring-plan.md**: 796 lines - **base-agent-refactoring-plan.md**: 658 lines - **refactoring-summary.md**: 404 lines - **REFACTORING_STATUS.md**: 210 lines ### Testing Documentation (6 documents) - **mcp-tools-test-report.md**: 1,260 lines (comprehensive test report) - **testing-workflow.md**: 691 lines (MCP testing guide) - **TestGeneratorAgent-test-plan.md**: 318 lines (test plan) - **coverage-analysis-2025-10-30.md**: 661 lines (coverage report) - **regression-validation-report-2025-10-30.md**: 450 lines (regression validation) - **README.md** (tests/integration/mcp): 277 lines (integration test guide) ### CI/CD Documentation (3 documents) - **mcp-cicd-pipeline.md**: 402 lines (pipeline architecture) - **mcp-pipeline-setup-summary.md**: 343 lines (setup guide) - **mcp-cicd-implementation-notes.md**: 280 lines (implementation notes) ### MCP Verification (2 documents) - **MCP-VERIFICATION-SUMMARY.md**: 286 lines - **PR-22-ANALYSIS.md**: 373 lines ### Coverage Analysis (2 documents) - **coverage-analysis-2025-10-30.md**: 661 lines - **coverage-analysis-summary.json**: 417 lines (structured data) ## 🛠️ Scripts & Automation (5 Scripts, 2,117 Lines) ### New Utility Scripts - **validate-mcp-tools.js**: 280 lines (MCP tool validation) - **generate-mcp-report.js**: 252 lines (report generation) - **generate-mcp-docs.js**: 270 lines (documentation generation) - **verify-mcp-production-readiness.js**: 564 lines (production checks) - **test-issue-fixes.js**: 224 lines (fix verification) - **mcp-tools-summary.json**: 344 lines (structured summary) ## 📊 Examples & Guides ### MCP Integration Examples (2 Examples) - **basic-test-generation.js**: 200 lines (starter example) - **quality-analysis-pipeline.js**: 360 lines (advanced workflow) ## ⚙️ CI/CD Enhancements ### GitHub Actions Workflow - **.github/workflows/mcp-tools-test.yml**: 197 lines - Automated MCP tool testing - Multi-environment validation - Production readiness checks ### Git Hooks - **.husky/pre-commit**: 14 lines (pre-commit validation) ## 📦 Configuration Updates ### Version & Dependencies - **package.json**: Version bumped to 1.3.6 - **package-lock.json**: Dependency lock updated - **README.md**: Version references updated (27 lines modified) - **.claude/settings.json**: 4 lines modified ### CHANGELOG - **CHANGELOG.md**: 37 lines added - Complete v1.3.6 changelog entry - Security fixes documented - UX enhancements documented - Testing improvements documented ## 📈 Statistics Summary ### Code Changes - **78 files changed** - **27,439 insertions** (+) - **181 deletions** (-) - **Net change**: +27,258 lines ### Breakdown by Category - **Source Code**: ~1,200 lines (agents, handlers, infrastructure) - **Tests**: ~5,240 lines (integration + unit tests) - **Documentation**: ~18,000 lines (guides, reports, plans) - **Scripts**: ~2,117 lines (automation utilities) - **Examples**: ~560 lines (MCP examples) - **CI/CD**: ~211 lines (workflows + hooks) ### Quality Metrics - ✅ **TypeScript Compilation**: 0 errors (16 errors fixed) - ✅ **Security Vulnerabilities**: 0 critical (1 fixed) - ✅ **Integration Tests**: 5 new test suites, 2,820 lines - ✅ **Unit Tests**: 2,420 new lines - ✅ **Documentation**: 100% comprehensive coverage - ✅ **Backward Compatibility**: 100% maintained ## 🎯 User Impact ### Immediate Benefits 1. **Security**: Eliminates arbitrary code execution vulnerability 2. **UX**: Less disruptive CLAUDE.md initialization 3. **Accuracy**: Correct skills count display (34/34) 4. **Quality**: CodeComplexityAnalyzerAgent for refactoring guidance 5. **Reliability**: 16 TypeScript errors resolved ### Developer Experience 1. **Better Documentation**: 18,000+ lines of guides and references 2. **Better Testing**: 5,240 new lines of test coverage 3. **Better Tooling**: 2,117 lines of automation scripts 4. **Better Examples**: MCP integration examples 5. **Better CI/CD**: Automated testing and validation ## 🔄 Migration & Compatibility ### Breaking Changes - **NONE** - Fully backward compatible ### Deprecations - **NONE** ### New Features (Opt-in) - CodeComplexityAnalyzerAgent (available via Claude Code or MCP) - Interactive CLAUDE.md placement (interactive mode only) ## ✅ Verification ### Pre-Release Checks - ✅ TypeScript compilation: 0 errors - ✅ All three user-reported fixes verified - ✅ Integration tests passing - ✅ Documentation complete - ✅ CHANGELOG updated - ✅ Version numbers updated (package.json, README.md) - ✅ Security scan: No vulnerabilities - ✅ Backward compatibility verified ### Production Readiness - ✅ MCP tools validated (54 tools) - ✅ Agent registry verified (17 agents) - ✅ Skills library verified (34 skills) - ✅ CI/CD pipeline configured - ✅ Pre-commit hooks installed ## 🙏 Acknowledgments - **@mondweep**: CodeComplexityAnalyzerAgent contribution (PR #22) - **User Feedback**: CLAUDE.md append strategy request - **User Feedback**: CLI skills count display bug report - **User Feedback**: eval() security concern ## 📝 Release Notes **Version**: 1.3.6 **Date**: 2025-10-30 **Type**: Security & Quality Patch Release **Status**: Production Ready **Upgrade Recommendation**: 🔴 **HIGH PRIORITY** - Security vulnerability fixed (eval() removal) - User experience improvements - Enhanced stability (16 TypeScript errors fixed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
910 lines
25 KiB
TypeScript
910 lines
25 KiB
TypeScript
/**
|
|
* Enhanced BaseAgent Test Suite - Targeting High-Risk Areas (Lines 76-157)
|
|
* Focus: Uncovered code paths, edge cases, and high-defect-risk function2
|
|
*
|
|
* Target Coverage: >80% (currently 62.84%)
|
|
* High-Risk Area: Lines 76-157 (57.4% defect risk)
|
|
*/
|
|
|
|
import { BaseAgent, BaseAgentConfig } from '../../../src/agents/BaseAgent';
|
|
import { SwarmMemoryManager } from '../../../src/core/memory/SwarmMemoryManager';
|
|
import { EventEmitter } from 'events';
|
|
import {
|
|
QEAgentType as AgentType,
|
|
AgentStatus,
|
|
AgentCapability,
|
|
AgentContext,
|
|
QETask,
|
|
TaskAssignment,
|
|
MemoryStore
|
|
} from '../../../src/types';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs-extra';
|
|
|
|
// Mock MemoryStore implementation for null memory scenarios
|
|
class NullMemoryStore implements MemoryStore {
|
|
async initialize(): Promise<void> {}
|
|
async store(key: string, value: any, ttl?: number): Promise<void> {}
|
|
async retrieve(key: string): Promise<any> {
|
|
return null;
|
|
}
|
|
async delete(key: string): Promise<void> {}
|
|
async query(pattern: string): Promise<any[]> {
|
|
return [];
|
|
}
|
|
async close(): Promise<void> {}
|
|
|
|
// Required by MemoryStoreAdapter
|
|
async set(key: string, value: any, ttl?: number): Promise<void> {}
|
|
async get(key: string): Promise<any> {
|
|
return null;
|
|
}
|
|
async clear(): Promise<void> {}
|
|
}
|
|
|
|
// Test agent implementation
|
|
class EnhancedTestAgent extends BaseAgent {
|
|
public shouldFailInit = false;
|
|
public shouldFailTask = false;
|
|
public shouldFailCleanup = false;
|
|
public taskResult: any = { success: true };
|
|
|
|
protected async initializeComponents(): Promise<void> {
|
|
if (this.shouldFailInit) throw new Error('Init component failed');
|
|
}
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
if (this.shouldFailTask) throw new Error('Task perform failed');
|
|
return { ...this.taskResult, taskId: task.id };
|
|
}
|
|
|
|
protected async loadKnowledge(): Promise<void> {}
|
|
|
|
protected async cleanup(): Promise<void> {
|
|
if (this.shouldFailCleanup) throw new Error('Cleanup failed');
|
|
}
|
|
|
|
// Expose protected methods for testing
|
|
public exposeStoreMemory(key: string, value: any, ttl?: number) {
|
|
return this.storeMemory(key, value, ttl);
|
|
}
|
|
|
|
public exposeRetrieveMemory(key: string) {
|
|
return this.retrieveMemory(key);
|
|
}
|
|
|
|
public exposeStoreSharedMemory(key: string, value: any, ttl?: number) {
|
|
return this.storeSharedMemory(key, value, ttl);
|
|
}
|
|
|
|
public exposeRetrieveSharedMemory(agentType: AgentType, key: string) {
|
|
return this.retrieveSharedMemory(agentType, key);
|
|
}
|
|
|
|
public exposeEmitEvent(type: string, data: any, priority?: any) {
|
|
return this.emitEvent(type, data, priority);
|
|
}
|
|
|
|
public exposeBroadcastMessage(type: string, payload: any) {
|
|
return this.broadcastMessage(type, payload);
|
|
}
|
|
|
|
// Access protected properties
|
|
public getAgentDBConfig() {
|
|
return (this as any).agentDBConfig;
|
|
}
|
|
|
|
public getPerformanceTracker() {
|
|
return (this as any).performanceTracker;
|
|
}
|
|
|
|
public getLearningEngineInternal() {
|
|
return (this as any).learningEngine;
|
|
}
|
|
}
|
|
|
|
describe('BaseAgent - Enhanced Coverage (High-Risk Lines 76-157)', () => {
|
|
let memoryStore: SwarmMemoryManager;
|
|
let eventBus: EventEmitter;
|
|
let testDbPath: string;
|
|
|
|
const capabilities: AgentCapability[] = [
|
|
{
|
|
name: 'test-capability',
|
|
version: '1.0.0',
|
|
description: 'Test capability',
|
|
taskTypes: ['test'],
|
|
parameters: {}
|
|
}
|
|
];
|
|
|
|
const context: AgentContext = {
|
|
id: 'ctx',
|
|
type: 'test-generator',
|
|
status: AgentStatus.IDLE,
|
|
metadata: {}
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
testDbPath = ':memory:';
|
|
memoryStore = new SwarmMemoryManager(testDbPath);
|
|
await memoryStore.initialize();
|
|
eventBus = new EventEmitter();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
if (memoryStore) {
|
|
await memoryStore.close();
|
|
}
|
|
});
|
|
|
|
describe('Constructor - AgentDB Config Building (Lines 101-115)', () => {
|
|
it('should build AgentDB config from agentDBConfig parameter', () => {
|
|
const agentDBConfig = {
|
|
dbPath: '/custom/path.db',
|
|
enableQUICSync: true,
|
|
syncPort: 5000,
|
|
syncPeers: ['peer1:4433', 'peer2:4433'],
|
|
enableLearning: true,
|
|
enableReasoning: false,
|
|
cacheSize: 2000,
|
|
quantizationType: 'binary' as const
|
|
};
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
agentDBConfig
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config).toEqual(agentDBConfig);
|
|
expect(config?.dbPath).toBe('/custom/path.db');
|
|
expect(config?.enableQUICSync).toBe(true);
|
|
expect(config?.syncPort).toBe(5000);
|
|
expect(config?.quantizationType).toBe('binary');
|
|
});
|
|
|
|
it('should build AgentDB config from shorthand properties (agentDBPath)', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
agentDBPath: '/shorthand/path.db'
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config?.dbPath).toBe('/shorthand/path.db');
|
|
expect(config?.enableQUICSync).toBe(false);
|
|
expect(config?.syncPort).toBe(4433); // default
|
|
expect(config?.quantizationType).toBe('scalar'); // default
|
|
});
|
|
|
|
it('should build AgentDB config from shorthand properties (enableQUICSync)', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
enableQUICSync: true,
|
|
syncPort: 9000,
|
|
syncPeers: ['peer1:9000']
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config?.enableQUICSync).toBe(true);
|
|
expect(config?.syncPort).toBe(9000);
|
|
expect(config?.syncPeers).toEqual(['peer1:9000']);
|
|
expect(config?.dbPath).toBe('.agentdb/reasoningbank.db'); // default
|
|
});
|
|
|
|
it('should build AgentDB config with custom quantization type', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
agentDBPath: '/path.db',
|
|
quantizationType: 'product'
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config?.quantizationType).toBe('product');
|
|
});
|
|
|
|
it('should not build AgentDB config when no AgentDB params provided', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config).toBeUndefined();
|
|
});
|
|
|
|
it('should enable learning in AgentDB config from enableLearning flag', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
enableLearning: true,
|
|
agentDBPath: '/learning.db'
|
|
});
|
|
|
|
const config = agent.getAgentDBConfig();
|
|
expect(config?.enableLearning).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Initialization - Learning Engine Integration (Lines 146-160)', () => {
|
|
it('should initialize PerformanceTracker when learning enabled with SwarmMemoryManager', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore, // SwarmMemoryManager instance
|
|
eventBus,
|
|
enableLearning: true
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const tracker = agent.getPerformanceTracker();
|
|
expect(tracker).toBeDefined();
|
|
|
|
const learningEngine = agent.getLearningEngineInternal();
|
|
expect(learningEngine).toBeDefined();
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should not initialize PerformanceTracker when learning disabled', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
enableLearning: false
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const tracker = agent.getPerformanceTracker();
|
|
expect(tracker).toBeUndefined();
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should initialize with custom learning config', async () => {
|
|
const learningConfig = {
|
|
alpha: 0.2,
|
|
gamma: 0.8,
|
|
epsilon: 0.3,
|
|
epsilonDecay: 0.95,
|
|
minEpsilon: 0.05
|
|
};
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
enableLearning: true,
|
|
learningConfig
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const learningStatus = agent.getLearningStatus();
|
|
expect(learningStatus).not.toBeNull();
|
|
expect(learningStatus?.enabled).toBe(true);
|
|
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('AgentDB Integration Methods (Lines 371-422)', () => {
|
|
it('should initialize AgentDB with full config', async () => {
|
|
const agentDBPath = path.join(__dirname, '../../../.agentdb-test', `full-${Date.now()}.db`);
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const agentDBConfig = {
|
|
dbPath: agentDBPath,
|
|
enableQUICSync: false,
|
|
syncPort: 4433,
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
cacheSize: 500
|
|
};
|
|
|
|
await agent.initializeAgentDB(agentDBConfig);
|
|
|
|
expect(agent.hasAgentDB()).toBe(true);
|
|
|
|
const status = await agent.getAgentDBStatus();
|
|
expect(status).not.toBeNull();
|
|
expect(status?.enabled).toBe(true);
|
|
|
|
await agent.terminate();
|
|
await fs.remove(path.dirname(agentDBPath));
|
|
});
|
|
|
|
it('should warn if AgentDB already initialized', async () => {
|
|
const agentDBPath = path.join(__dirname, '../../../.agentdb-test', `warn-${Date.now()}.db`);
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
agentDBPath
|
|
});
|
|
|
|
await agent.initialize(); // Initializes AgentDB
|
|
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
// Try to initialize again
|
|
await agent.initializeAgentDB({ dbPath: agentDBPath });
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('AgentDB already initialized')
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
await agent.terminate();
|
|
await fs.remove(path.dirname(agentDBPath));
|
|
});
|
|
|
|
it('should handle AgentDB initialization errors', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
// Invalid config that will cause AgentDB to fail
|
|
const invalidConfig = {
|
|
dbPath: '/invalid/\0/path.db' // Null byte in path
|
|
};
|
|
|
|
await expect(agent.initializeAgentDB(invalidConfig)).rejects.toThrow();
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should return AgentDB status with stats', async () => {
|
|
const agentDBPath = path.join(__dirname, '../../../.agentdb-test', `stats-${Date.now()}.db`);
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus,
|
|
agentDBPath
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const status = await agent.getAgentDBStatus();
|
|
expect(status).not.toBeNull();
|
|
expect(status?.enabled).toBe(true);
|
|
expect(status?.stats).toBeDefined();
|
|
|
|
await agent.terminate();
|
|
await fs.remove(path.dirname(agentDBPath));
|
|
});
|
|
|
|
it('should return null for AgentDB status when not initialized', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const status = await agent.getAgentDBStatus();
|
|
expect(status).toBeNull();
|
|
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('Memory Operations with Null Store (Lines 528-571)', () => {
|
|
it('should handle undefined memory store in storeMemory', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Force memoryStore to undefined to test the warning path
|
|
(agent as any).memoryStore = undefined;
|
|
|
|
// Should warn but not throw
|
|
await agent.exposeStoreMemory('key', 'value');
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Memory store not available')
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle undefined memory store in retrieveMemory', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Force memoryStore to undefined
|
|
(agent as any).memoryStore = undefined;
|
|
|
|
const result = await agent.exposeRetrieveMemory('key');
|
|
expect(result).toBeNull();
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Memory store not available')
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle undefined memory store in storeSharedMemory', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Force memoryStore to undefined
|
|
(agent as any).memoryStore = undefined;
|
|
|
|
await agent.exposeStoreSharedMemory('shared', 'data');
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Memory store not available')
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle undefined memory store in retrieveSharedMemory', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Force memoryStore to undefined
|
|
(agent as any).memoryStore = undefined;
|
|
|
|
const result = await agent.exposeRetrieveSharedMemory('test-generator', 'key');
|
|
expect(result).toBeNull();
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Memory store not available')
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('should work normally with valid memory store', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'memory-normal',
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
// Normal operations should work
|
|
await agent.exposeStoreMemory('test', { data: 'value' });
|
|
const result = await agent.exposeRetrieveMemory('test');
|
|
expect(result).toEqual({ data: 'value' });
|
|
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('Task Lifecycle - Performance Metrics Edge Cases (Lines 1003-1016)', () => {
|
|
it('should calculate average execution time correctly for multiple tasks', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'metrics-agent',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
// Execute multiple tasks
|
|
for (let i = 0; i < 5; i++) {
|
|
const task: QETask = {
|
|
id: `task-${i}`,
|
|
type: 'test',
|
|
payload: {},
|
|
priority: 1,
|
|
status: 'pending'
|
|
};
|
|
await agent.assignTask(task);
|
|
}
|
|
|
|
const metrics = agent.getStatus().performanceMetrics;
|
|
expect(metrics.tasksCompleted).toBe(5);
|
|
expect(metrics.averageExecutionTime).toBeGreaterThanOrEqual(0);
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should handle mixed success and error metrics', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'mixed-agent',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
// Successful task
|
|
await agent.assignTask({
|
|
id: 'success',
|
|
type: 'test',
|
|
payload: {},
|
|
priority: 1,
|
|
status: 'pending'
|
|
});
|
|
|
|
// Failed task
|
|
agent.shouldFailTask = true;
|
|
try {
|
|
await agent.assignTask({
|
|
id: 'fail',
|
|
type: 'test',
|
|
payload: {},
|
|
priority: 1,
|
|
status: 'pending'
|
|
});
|
|
} catch (e) {
|
|
// Expected
|
|
}
|
|
|
|
const metrics = agent.getStatus().performanceMetrics;
|
|
expect(metrics.tasksCompleted).toBe(1);
|
|
expect(metrics.errorCount).toBe(1);
|
|
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('State Persistence Edge Cases (Lines 1026-1060)', () => {
|
|
it('should handle missing state gracefully during restore', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'restore-missing',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// No state exists - should initialize with default metrics
|
|
await agent.initialize();
|
|
|
|
const metrics = agent.getStatus().performanceMetrics;
|
|
expect(metrics.tasksCompleted).toBe(0);
|
|
expect(metrics.errorCount).toBe(0);
|
|
expect(metrics.averageExecutionTime).toBe(0);
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should handle save state errors gracefully', async () => {
|
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'save-fail',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
// Mock store to fail
|
|
jest.spyOn(memoryStore, 'store').mockRejectedValueOnce(new Error('Store failed'));
|
|
|
|
// Termination should handle save error gracefully
|
|
await agent.terminate();
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Could not save state'),
|
|
expect.any(Error)
|
|
);
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle reportStatus errors gracefully', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'report-fail',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Mock store to fail for reportStatus during initialization
|
|
jest.spyOn(memoryStore, 'store').mockRejectedValueOnce(new Error('Report failed'));
|
|
|
|
// Should handle error during initialization's reportStatus call
|
|
await agent.initialize();
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('Could not report status'),
|
|
expect.any(Error)
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('Event Priority Variations (Lines 488-500)', () => {
|
|
it('should emit event with critical priority', (done) => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
eventBus.on('critical-event', (event) => {
|
|
expect(event.priority).toBe('critical');
|
|
done();
|
|
});
|
|
|
|
agent.exposeEmitEvent('critical-event', { urgent: true }, 'critical');
|
|
});
|
|
|
|
it('should emit event with low priority', (done) => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
eventBus.on('low-event', (event) => {
|
|
expect(event.priority).toBe('low');
|
|
done();
|
|
});
|
|
|
|
agent.exposeEmitEvent('low-event', { info: 'data' }, 'low');
|
|
});
|
|
|
|
it('should generate unique event IDs', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
const eventIds = new Set<string>();
|
|
|
|
eventBus.on('id-test', (event) => {
|
|
eventIds.add(event.id);
|
|
});
|
|
|
|
// Emit multiple events
|
|
for (let i = 0; i < 10; i++) {
|
|
agent.exposeEmitEvent('id-test', { index: i });
|
|
}
|
|
|
|
expect(eventIds.size).toBe(10); // All unique
|
|
});
|
|
});
|
|
|
|
describe('Task Assignment Generation (Lines 434-444)', () => {
|
|
it('should generate unique assignment IDs', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
const assignmentIds = new Set<string>();
|
|
|
|
// Capture assignment IDs
|
|
const originalExecute = agent.executeTask.bind(agent);
|
|
agent.executeTask = jest.fn(async (assignment) => {
|
|
assignmentIds.add(assignment.id);
|
|
return originalExecute(assignment);
|
|
});
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
await agent.assignTask({
|
|
id: `task-${i}`,
|
|
type: 'test',
|
|
payload: {},
|
|
priority: 1,
|
|
status: 'pending'
|
|
});
|
|
}
|
|
|
|
expect(assignmentIds.size).toBe(5); // All unique
|
|
|
|
await agent.terminate();
|
|
});
|
|
|
|
it('should set correct assignment status', async () => {
|
|
const agent = new EnhancedTestAgent({
|
|
id: 'assign-status',
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
await agent.initialize();
|
|
|
|
let capturedAssignment: TaskAssignment | undefined;
|
|
|
|
const originalExecute = agent.executeTask.bind(agent);
|
|
agent.executeTask = jest.fn(async (assignment) => {
|
|
capturedAssignment = assignment;
|
|
return originalExecute(assignment);
|
|
});
|
|
|
|
await agent.assignTask({
|
|
id: 'task-status',
|
|
type: 'test',
|
|
payload: {},
|
|
priority: 1,
|
|
status: 'pending'
|
|
});
|
|
|
|
expect(capturedAssignment?.status).toBe('assigned');
|
|
expect(capturedAssignment?.agentId).toBe('assign-status');
|
|
expect(capturedAssignment?.assignedAt).toBeInstanceOf(Date);
|
|
|
|
await agent.terminate();
|
|
});
|
|
});
|
|
|
|
describe('Error Hook Edge Cases (Lines 947-956)', () => {
|
|
it('should handle errors in executeHook gracefully', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
// Trigger hook with non-existent method
|
|
await (agent as any).executeHook('non-existent-hook', {});
|
|
|
|
// Should not throw, just log error
|
|
expect(consoleSpy).not.toHaveBeenCalled(); // No error if method doesn't exist
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('ID Generation Methods (Lines 1062-1072)', () => {
|
|
it('should generate unique agent IDs with timestamp and random component', () => {
|
|
const ids = new Set<string>();
|
|
|
|
for (let i = 0; i < 100; i++) {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-generator' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
ids.add(agent.getStatus().agentId.id);
|
|
}
|
|
|
|
expect(ids.size).toBe(100); // All unique
|
|
});
|
|
|
|
it('should generate message IDs with correct format', () => {
|
|
const agent = new EnhancedTestAgent({
|
|
type: 'test-executor' as AgentType,
|
|
capabilities,
|
|
context,
|
|
memoryStore,
|
|
eventBus
|
|
});
|
|
|
|
const messageIds = new Set<string>();
|
|
|
|
eventBus.on('agent.message', (msg) => {
|
|
messageIds.add(msg.id);
|
|
expect(msg.id).toMatch(/^msg-\d+-\w+$/);
|
|
});
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
agent.exposeBroadcastMessage('test', { index: i });
|
|
}
|
|
|
|
expect(messageIds.size).toBe(10);
|
|
});
|
|
});
|
|
});
|