architect.test.js
1 /** 2 * Tests for Architect Agent 3 * 4 * Tests design review, refactoring suggestions, documentation updates, and performance profiling. 5 */ 6 7 import { test } from 'node:test'; 8 import assert from 'node:assert'; 9 import Database from 'better-sqlite3'; 10 import fs from 'fs/promises'; 11 import { ArchitectAgent } from '../../src/agents/architect.js'; 12 import { resetDb as resetBaseDb } from '../../src/agents/base-agent.js'; 13 import { resetDb as resetClaudeDb } from '../../src/agents/utils/agent-claude-api.js'; 14 import { resetDbConnection as resetTaskManagerDb } from '../../src/agents/utils/task-manager.js'; 15 16 // Test database path 17 const TEST_DB_PATH = './test-architect.db'; 18 process.env.AGENT_IMMEDIATE_INVOCATION = 'false'; 19 20 // Shared database instance for tests 21 let sharedDb = null; 22 23 /** 24 * Setup test database with required schema 25 */ 26 function setupTestDb() { 27 if (sharedDb) { 28 try { 29 sharedDb.close(); 30 } catch (e) { 31 // Ignore 32 } 33 } 34 35 sharedDb = new Database(TEST_DB_PATH); 36 sharedDb.pragma('foreign_keys = ON'); 37 38 // Create required tables 39 sharedDb.exec(` 40 -- Agent tasks 41 CREATE TABLE IF NOT EXISTS agent_tasks ( 42 id INTEGER PRIMARY KEY AUTOINCREMENT, 43 task_type TEXT NOT NULL, 44 assigned_to TEXT NOT NULL, 45 created_by TEXT, 46 status TEXT DEFAULT 'pending', 47 priority INTEGER DEFAULT 5, 48 context_json TEXT, 49 result_json TEXT, 50 parent_task_id INTEGER, 51 error_message TEXT, 52 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 53 started_at DATETIME, 54 completed_at DATETIME, 55 retry_count INTEGER DEFAULT 0 56 ); 57 58 -- Agent logs 59 CREATE TABLE IF NOT EXISTS agent_logs ( 60 id INTEGER PRIMARY KEY AUTOINCREMENT, 61 task_id INTEGER, 62 agent_name TEXT NOT NULL, 63 log_level TEXT, 64 message TEXT NOT NULL, 65 data_json TEXT, 66 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 67 ); 68 69 -- Agent state 70 CREATE TABLE IF NOT EXISTS agent_state ( 71 agent_name TEXT PRIMARY KEY, 72 last_active DATETIME DEFAULT CURRENT_TIMESTAMP, 73 current_task_id INTEGER, 74 status TEXT DEFAULT 'idle', 75 metrics_json TEXT 76 ); 77 78 -- Agent LLM usage 79 CREATE TABLE IF NOT EXISTS agent_llm_usage ( 80 id INTEGER PRIMARY KEY AUTOINCREMENT, 81 agent_name TEXT NOT NULL, 82 task_id INTEGER, 83 model TEXT NOT NULL, 84 prompt_tokens INTEGER NOT NULL, 85 completion_tokens INTEGER NOT NULL, 86 cost_usd REAL NOT NULL, 87 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 88 ); 89 90 -- Pipeline metrics 91 CREATE TABLE IF NOT EXISTS pipeline_metrics ( 92 id INTEGER PRIMARY KEY AUTOINCREMENT, 93 stage_name TEXT NOT NULL, 94 sites_processed INTEGER DEFAULT 0, 95 sites_succeeded INTEGER DEFAULT 0, 96 sites_failed INTEGER DEFAULT 0, 97 duration_ms INTEGER NOT NULL, 98 started_at DATETIME NOT NULL, 99 finished_at DATETIME NOT NULL, 100 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 101 ); 102 103 -- Agent outcomes (for learning system) 104 CREATE TABLE IF NOT EXISTS agent_outcomes ( 105 id INTEGER PRIMARY KEY AUTOINCREMENT, 106 task_id INTEGER NOT NULL, 107 agent_name TEXT NOT NULL, 108 task_type TEXT NOT NULL, 109 outcome TEXT NOT NULL CHECK(outcome IN ('success', 'failure')), 110 context_json TEXT, 111 result_json TEXT, 112 duration_ms INTEGER, 113 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 114 ); 115 116 -- Cron locks (required by task-manager spawn rate limiting) 117 CREATE TABLE IF NOT EXISTS cron_locks ( 118 lock_key TEXT PRIMARY KEY, 119 acquired_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 120 updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 121 description TEXT 122 ); 123 124 -- Initialize agent state 125 INSERT OR IGNORE INTO agent_state (agent_name, status) VALUES ('architect', 'idle'); 126 `); 127 128 // Don't close - keep connection open for tests 129 } 130 131 /** 132 * Teardown test database 133 */ 134 async function teardownTestDb() { 135 // Reset module state first 136 resetBaseDb(); 137 resetClaudeDb(); 138 resetTaskManagerDb(); 139 140 // Close shared database 141 if (sharedDb) { 142 try { 143 sharedDb.exec( 144 "UPDATE agent_state SET status = 'idle', current_task_id = NULL WHERE agent_name = 'architect'" 145 ); 146 sharedDb.close(); 147 sharedDb = null; 148 } catch (error) { 149 // Database might be closed already 150 } 151 } 152 153 // Delete database file 154 try { 155 await fs.unlink(TEST_DB_PATH); 156 } catch (error) { 157 // File might not exist 158 } 159 } 160 161 test('ArchitectAgent - profilePerformance identifies bottlenecks', async () => { 162 await teardownTestDb(); // Clean up any previous test db 163 setupTestDb(); 164 process.env.DATABASE_PATH = TEST_DB_PATH; 165 166 const agent = new ArchitectAgent(); 167 await agent.initialize(); 168 169 // Insert test metrics using shared db 170 171 sharedDb.exec(` 172 INSERT INTO pipeline_metrics (stage_name, duration_ms, sites_succeeded, sites_failed, started_at, finished_at) 173 VALUES 174 ('assets', 120000, 10, 0, datetime('now', '-1 day'), datetime('now', '-1 day', '+2 minutes')), 175 ('scoring', 45000, 10, 0, datetime('now', '-1 day'), datetime('now', '-1 day', '+45 seconds')), 176 ('rescoring', 180000, 8, 2, datetime('now', '-1 day'), datetime('now', '-1 day', '+3 minutes')); 177 `); 178 179 // Create task 180 const taskId = sharedDb 181 .prepare( 182 ` 183 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 184 VALUES (?, ?, ?, ?) 185 RETURNING id 186 ` 187 ) 188 .get( 189 'profile_performance', 190 'architect', 191 'pending', 192 JSON.stringify({ threshold_ms: 60000, days_back: 7 }) 193 ).id; 194 195 // Don't close - let the agent use the same connection 196 197 // Process task 198 const processed = await agent.pollTasks(1); 199 200 assert.strictEqual(processed, 1, 'Should process 1 task'); 201 202 // Verify results - use shared db connection 203 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 204 if (task.context_json && typeof task.context_json === 'string') 205 task.context_json = JSON.parse(task.context_json); 206 207 assert.strictEqual(task.status, 'completed', 'Task should be completed'); 208 209 const result = JSON.parse(task.result_json); 210 211 assert.ok(result.bottlenecks, 'Should have bottlenecks'); 212 assert.strictEqual(result.bottlenecks.length, 2, 'Should find 2 bottlenecks (>60s)'); 213 214 // Verify refactor tasks created for high severity 215 const refactorTasks = sharedDb 216 .prepare( 217 ` 218 SELECT * FROM agent_tasks 219 WHERE task_type = 'suggest_refactor' 220 AND parent_task_id IS NULL 221 ` 222 ) 223 .all(); 224 225 assert.ok(refactorTasks.length > 0, 'Should create refactor tasks for bottlenecks'); 226 227 // Don't close shared db - handled by teardown 228 await teardownTestDb(); 229 }); 230 231 test('ArchitectAgent - identifyAffectedDocs detects schema changes', async () => { 232 await teardownTestDb(); // Clean up previous test 233 setupTestDb(); 234 process.env.DATABASE_PATH = TEST_DB_PATH; 235 236 const agent = new ArchitectAgent(); 237 await agent.initialize(); 238 239 const files = ['db/migrations/050-add-new-table.sql', 'src/stages/scoring.js']; 240 241 const affected = agent.identifyAffectedDocs(files, 'new_feature'); 242 243 assert.ok(affected.length > 0, 'Should identify affected docs'); 244 245 const schemaDocs = affected.filter(d => d.file === 'db/schema.sql'); 246 assert.strictEqual(schemaDocs.length, 1, 'Should identify schema.sql'); 247 248 await teardownTestDb(); 249 }); 250 251 test('ArchitectAgent - identifyAffectedDocs detects package.json changes', async () => { 252 await teardownTestDb(); 253 setupTestDb(); 254 process.env.DATABASE_PATH = TEST_DB_PATH; 255 256 const agent = new ArchitectAgent(); 257 await agent.initialize(); 258 259 const files = ['package.json']; 260 261 const affected = agent.identifyAffectedDocs(files, 'dependency_update'); 262 263 assert.ok(affected.length > 0, 'Should identify affected docs'); 264 265 const readmeDocs = affected.filter(d => d.file === 'README.md'); 266 assert.strictEqual(readmeDocs.length, 1, 'Should identify README.md'); 267 268 await teardownTestDb(); 269 }); 270 271 test('ArchitectAgent - identifyAffectedDocs detects agent changes', async () => { 272 await teardownTestDb(); 273 setupTestDb(); 274 process.env.DATABASE_PATH = TEST_DB_PATH; 275 276 const agent = new ArchitectAgent(); 277 await agent.initialize(); 278 279 const files = ['src/agents/developer.js']; 280 281 const affected = agent.identifyAffectedDocs(files, 'new_feature'); 282 283 assert.ok(affected.length > 0, 'Should identify affected docs'); 284 285 const agentDocs = affected.filter(d => d.file === 'docs/06-automation/agent-system.md'); 286 assert.strictEqual(agentDocs.length, 1, 'Should identify agent-system.md'); 287 288 await teardownTestDb(); 289 }); 290 291 test('ArchitectAgent - classifyPerformanceIssue assigns correct severity', async () => { 292 await teardownTestDb(); 293 setupTestDb(); 294 process.env.DATABASE_PATH = TEST_DB_PATH; 295 296 const agent = new ArchitectAgent(); 297 await agent.initialize(); 298 299 const threshold = 60000; 300 301 assert.strictEqual( 302 agent.classifyPerformanceIssue(300000, threshold), 303 'critical', 304 '5x threshold should be critical' 305 ); 306 307 assert.strictEqual( 308 agent.classifyPerformanceIssue(200000, threshold), 309 'high', 310 '3x threshold should be high' 311 ); 312 313 assert.strictEqual( 314 agent.classifyPerformanceIssue(120000, threshold), 315 'medium', 316 '2x threshold should be medium' 317 ); 318 319 assert.strictEqual( 320 agent.classifyPerformanceIssue(80000, threshold), 321 'low', 322 '1.3x threshold should be low' 323 ); 324 325 await teardownTestDb(); 326 }); 327 328 test('ArchitectAgent - verifyDocumentation detects issues', async () => { 329 await teardownTestDb(); 330 setupTestDb(); 331 process.env.DATABASE_PATH = TEST_DB_PATH; 332 333 const agent = new ArchitectAgent(); 334 await agent.initialize(); 335 336 // Create test documentation files 337 await fs.mkdir('tests/fixtures/docs', { recursive: true }); 338 339 await fs.writeFile( 340 'tests/fixtures/docs/good.md', 341 '# Good Documentation\n\nThis is properly formatted.' 342 ); 343 344 await fs.writeFile( 345 'tests/fixtures/docs/bad-todo.md', 346 '# Bad Documentation\n\nTODO: Fix this later' 347 ); 348 349 await fs.writeFile( 350 'tests/fixtures/docs/bad-placeholder.md', 351 '# Bad Documentation\n\n[placeholder] content' 352 ); 353 354 const results = await agent.verifyDocumentation([ 355 'tests/fixtures/docs/good.md', 356 'tests/fixtures/docs/bad-todo.md', 357 'tests/fixtures/docs/bad-placeholder.md', 358 ]); 359 360 assert.strictEqual(results.verified.length, 1, 'Should verify 1 good doc'); 361 assert.strictEqual(results.warnings.length, 2, 'Should warn on 2 bad docs'); 362 363 // Cleanup 364 await fs.rm('tests/fixtures/docs', { recursive: true, force: true }); 365 await teardownTestDb(); 366 }); 367 368 test('ArchitectAgent - summarizeChanges generates summary', async () => { 369 await teardownTestDb(); 370 setupTestDb(); 371 process.env.DATABASE_PATH = TEST_DB_PATH; 372 373 const agent = new ArchitectAgent(); 374 await agent.initialize(); 375 376 // Test with empty files 377 const summary1 = await agent.summarizeChanges([]); 378 assert.ok(summary1.includes('No specific files'), 'Should handle empty files'); 379 380 // Test with non-existent files (should not crash) 381 const summary2 = await agent.summarizeChanges(['nonexistent.js']); 382 assert.ok(summary2.length > 0, 'Should generate summary for non-existent files'); 383 384 await teardownTestDb(); 385 }); 386 387 test('ArchitectAgent - reviewDesign identifies file size issues', async () => { 388 await teardownTestDb(); 389 setupTestDb(); 390 process.env.DATABASE_PATH = TEST_DB_PATH; 391 392 const agent = new ArchitectAgent(); 393 await agent.initialize(); 394 395 // Create a large test file 396 await fs.mkdir('tests/fixtures', { recursive: true }); 397 const largeContent = Array(200).fill('const x = 1;').join('\n'); // Exactly 200 lines 398 await fs.writeFile('tests/fixtures/large-file.js', largeContent); 399 400 // Create task using shared db 401 402 const taskId = sharedDb 403 .prepare( 404 ` 405 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 406 VALUES (?, ?, ?, ?) 407 RETURNING id 408 ` 409 ) 410 .get( 411 'review_design', 412 'architect', 413 'running', // Set to running to bypass lock 414 JSON.stringify({ files: ['tests/fixtures/large-file.js'] }) 415 ).id; 416 417 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 418 if (task.context_json && typeof task.context_json === 'string') 419 task.context_json = JSON.parse(task.context_json); 420 421 // Call method directly 422 await agent.reviewDesign(task); 423 424 // Verify results - reuse same db connection 425 const updatedTask = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 426 427 assert.strictEqual(updatedTask.status, 'completed', 'Task should be completed'); 428 429 const result = JSON.parse(updatedTask.result_json); 430 431 assert.ok(result.issues, 'Should have issues'); 432 assert.ok(result.issues.length > 0, 'Should find file size issue'); 433 434 const maxLinesIssue = result.issues.find(i => i.type === 'max_lines'); 435 assert.ok(maxLinesIssue, 'Should identify max_lines issue'); 436 assert.strictEqual(maxLinesIssue.current, 200, 'Should report correct line count'); 437 438 // Cleanup 439 await fs.unlink('tests/fixtures/large-file.js'); 440 // Don't close shared db - handled by teardown 441 await teardownTestDb(); 442 }); 443 444 test('ArchitectAgent - suggestRefactor creates developer task', async () => { 445 await teardownTestDb(); 446 setupTestDb(); 447 process.env.DATABASE_PATH = TEST_DB_PATH; 448 449 const agent = new ArchitectAgent(); 450 await agent.initialize(); 451 452 // Create task using shared db 453 454 const taskId = sharedDb 455 .prepare( 456 ` 457 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 458 VALUES (?, ?, ?, ?) 459 RETURNING id 460 ` 461 ) 462 .get( 463 'suggest_refactor', 464 'architect', 465 'running', // Set to running to bypass lock 466 JSON.stringify({ 467 file: 'src/example.js', 468 complexity_issues: ['nested logic', 'cyclomatic complexity'], 469 }) 470 ).id; 471 472 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 473 if (task.context_json && typeof task.context_json === 'string') 474 task.context_json = JSON.parse(task.context_json); 475 476 // Call method directly 477 await agent.suggestRefactor(task); 478 479 // Verify results - reuse same db connection 480 const updatedTask = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 481 482 assert.strictEqual(updatedTask.status, 'completed', 'Task should be completed'); 483 484 const result = JSON.parse(updatedTask.result_json); 485 486 assert.ok(result.suggestions, 'Should have suggestions'); 487 assert.ok(result.developer_task_id, 'Should create developer task'); 488 489 // Verify developer task exists 490 const devTask = sharedDb 491 .prepare('SELECT * FROM agent_tasks WHERE id = ?') 492 .get(result.developer_task_id); 493 494 assert.strictEqual(devTask.task_type, 'refactor_code', 'Should create refactor_code task'); 495 assert.strictEqual(devTask.assigned_to, 'developer', 'Should assign to developer'); 496 497 // Don't close shared db - handled by teardown 498 await teardownTestDb(); 499 }); 500 501 test('ArchitectAgent - checkComplexity scans specific files', async () => { 502 await teardownTestDb(); 503 setupTestDb(); 504 process.env.DATABASE_PATH = TEST_DB_PATH; 505 506 const agent = new ArchitectAgent(); 507 await agent.initialize(); 508 509 // Create a test file 510 await fs.mkdir('tests/fixtures', { recursive: true }); 511 const testContent = 'const x = 1;\nconst y = 2;'; 512 await fs.writeFile('tests/fixtures/test-file.js', testContent); 513 514 // Create task with specific files 515 const db = new Database(TEST_DB_PATH); 516 517 const taskId = sharedDb 518 .prepare( 519 ` 520 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 521 VALUES (?, ?, ?, ?) 522 RETURNING id 523 ` 524 ) 525 .get( 526 'check_complexity', 527 'architect', 528 'running', // Set to running to bypass lock 529 JSON.stringify({ files: ['tests/fixtures/test-file.js'] }) 530 ).id; 531 532 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 533 if (task.context_json && typeof task.context_json === 'string') 534 task.context_json = JSON.parse(task.context_json); 535 536 // Call method directly 537 await agent.checkComplexity(task); 538 539 // Verify results - reuse same db connection 540 const updatedTask = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 541 542 assert.strictEqual(updatedTask.status, 'completed', 'Task should be completed'); 543 544 const result = JSON.parse(updatedTask.result_json); 545 546 assert.ok(result.files_checked !== undefined, 'Should report files checked'); 547 assert.strictEqual(result.files_checked, 1, 'Should check 1 file'); 548 549 // Cleanup 550 await fs.unlink('tests/fixtures/test-file.js'); 551 552 // Don't close shared db - handled by teardown 553 await teardownTestDb(); 554 }); 555 556 test('ArchitectAgent - parseDesignResponse extracts proposal fields', async () => { 557 await teardownTestDb(); 558 setupTestDb(); 559 process.env.DATABASE_PATH = TEST_DB_PATH; 560 561 const agent = new ArchitectAgent(); 562 await agent.initialize(); 563 564 const designResponse = `**Summary**: Add user authentication with JWT tokens 565 566 **Approach**: Implement JWT-based authentication using industry standard libraries 567 568 **Files Affected**: 569 - \`src/auth/jwt.js\` 570 - \`src/auth/middleware.js\` 571 - \`tests/auth/jwt.test.js\` 572 573 **Risks**: 574 - Token expiration handling 575 - Security vulnerabilities if not implemented correctly 576 577 **Alternatives Considered**: 578 - Session-based authentication (rejected due to scalability concerns) 579 - OAuth2 (too complex for current needs) 580 581 **Estimated Effort**: 6 hours 582 583 **Breaking Changes**: None 584 585 **Migration Required**: yes 586 587 **Testing Strategy**: Unit tests for JWT generation/validation, integration tests for middleware`; 588 589 const proposal = agent.parseDesignResponse(designResponse, 'Add user authentication'); 590 591 assert.ok(proposal.summary.includes('JWT'), 'Should extract summary'); 592 assert.ok(proposal.approach.includes('JWT-based'), 'Should extract approach'); 593 assert.strictEqual(proposal.files_affected.length, 3, 'Should extract 3 files'); 594 assert.strictEqual(proposal.risks.length, 2, 'Should extract 2 risks'); 595 assert.strictEqual(proposal.estimated_effort, 6, 'Should extract effort'); 596 assert.strictEqual(proposal.requires_migration, true, 'Should detect migration required'); 597 assert.strictEqual(proposal.breaking_changes.length, 0, 'Should have no breaking changes'); 598 assert.ok(proposal.testing_strategy.includes('Unit tests'), 'Should extract testing strategy'); 599 600 await teardownTestDb(); 601 }); 602 603 test('ArchitectAgent - parseReviewResponse extracts issues', async () => { 604 await teardownTestDb(); 605 setupTestDb(); 606 process.env.DATABASE_PATH = TEST_DB_PATH; 607 608 const agent = new ArchitectAgent(); 609 await agent.initialize(); 610 611 const reviewResponse = `**Issues**: 612 - [high] Test coverage plan missing - need 85%+ target 613 - [medium] File src/example.js may exceed 150 lines 614 - [low] Consider adding JSDoc comments 615 616 **Recommendations**: 617 - Add comprehensive test plan 618 - Split large file into modules 619 620 **Approval**: no`; 621 622 const issues = agent.parseReviewResponse(reviewResponse); 623 624 assert.strictEqual(issues.length, 3, 'Should extract 3 issues'); 625 assert.strictEqual( 626 issues.filter(i => i.severity === 'high').length, 627 1, 628 'Should have 1 high severity' 629 ); 630 assert.strictEqual( 631 issues.filter(i => i.severity === 'medium').length, 632 1, 633 'Should have 1 medium severity' 634 ); 635 assert.strictEqual( 636 issues.filter(i => i.severity === 'low').length, 637 1, 638 'Should have 1 low severity' 639 ); 640 641 await teardownTestDb(); 642 }); 643 644 test('ArchitectAgent - analyzeCodebase finds relevant files', async () => { 645 await teardownTestDb(); 646 setupTestDb(); 647 process.env.DATABASE_PATH = TEST_DB_PATH; 648 649 const agent = new ArchitectAgent(); 650 await agent.initialize(); 651 652 // Test with specific files 653 await fs.mkdir('tests/fixtures', { recursive: true }); 654 await fs.writeFile('tests/fixtures/test-auth.js', 'export function authenticate() {}'); 655 656 const context1 = await agent.analyzeCodebase('Add authentication', [ 657 'tests/fixtures/test-auth.js', 658 ]); 659 660 assert.ok(context1.includes('test-auth.js'), 'Should include specified file'); 661 assert.ok(context1.includes('authenticate'), 'Should include file content'); 662 663 // Test without specific files - should search for patterns 664 const context2 = await agent.analyzeCodebase('authentication system', null); 665 666 assert.ok(context2.includes('Relevant codebase patterns'), 'Should search for patterns'); 667 668 // Cleanup 669 await fs.unlink('tests/fixtures/test-auth.js'); 670 671 await teardownTestDb(); 672 }); 673 674 test('ArchitectAgent - performAutomatedReviewChecks detects issues', async () => { 675 await teardownTestDb(); 676 setupTestDb(); 677 process.env.DATABASE_PATH = TEST_DB_PATH; 678 679 const agent = new ArchitectAgent(); 680 await agent.initialize(); 681 682 // Create a large test file 683 await fs.mkdir('tests/fixtures', { recursive: true }); 684 const largeContent = Array(140).fill('const x = 1;').join('\n'); 685 await fs.writeFile('tests/fixtures/large-impl.js', largeContent); 686 687 const implementationPlan = { 688 files_to_modify: ['tests/fixtures/large-impl.js'], 689 test_plan: { 690 coverage_target: 70, // Below 85% requirement 691 }, 692 }; 693 694 const issues = await agent.performAutomatedReviewChecks(implementationPlan); 695 696 assert.ok(issues.length >= 2, 'Should find at least 2 issues'); 697 698 const maxLinesIssue = issues.find(i => i.type === 'max_lines_risk'); 699 assert.ok(maxLinesIssue, 'Should find max_lines_risk issue'); 700 701 const lowCoverageIssue = issues.find(i => i.type === 'low_coverage_target'); 702 assert.ok(lowCoverageIssue, 'Should find low_coverage_target issue'); 703 assert.strictEqual(lowCoverageIssue.severity, 'high', 'Low coverage should be high severity'); 704 705 // Cleanup 706 await fs.unlink('tests/fixtures/large-impl.js'); 707 708 await teardownTestDb(); 709 }); 710 711 test('ArchitectAgent - handles null context_json gracefully', async () => { 712 await teardownTestDb(); 713 setupTestDb(); 714 process.env.DATABASE_PATH = TEST_DB_PATH; 715 716 const agent = new ArchitectAgent(); 717 await agent.initialize(); 718 719 // Create task with null context_json 720 const taskId = sharedDb 721 .prepare( 722 ` 723 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 724 VALUES (?, ?, ?, ?) 725 RETURNING id 726 ` 727 ) 728 .get('review_design', 'architect', 'running', null).id; 729 730 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 731 if (task.context_json && typeof task.context_json === 'string') 732 task.context_json = JSON.parse(task.context_json); 733 734 // Should not throw error 735 await agent.reviewDesign(task); 736 737 const updatedTask = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 738 739 assert.strictEqual(updatedTask.status, 'completed', 'Task should complete without errors'); 740 741 await teardownTestDb(); 742 }); 743 744 test('ArchitectAgent - delegates unknown task types correctly', async () => { 745 await teardownTestDb(); 746 setupTestDb(); 747 process.env.DATABASE_PATH = TEST_DB_PATH; 748 749 const agent = new ArchitectAgent(); 750 await agent.initialize(); 751 752 // Create task with fix_bug type (should be delegated to developer) 753 const taskId = sharedDb 754 .prepare( 755 ` 756 INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 757 VALUES (?, ?, ?, ?) 758 RETURNING id 759 ` 760 ) 761 .get( 762 'fix_bug', 763 'architect', 764 'running', 765 JSON.stringify({ error: 'Test error', file: 'test.js' }) 766 ).id; 767 768 const task = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 769 if (task.context_json && typeof task.context_json === 'string') 770 task.context_json = JSON.parse(task.context_json); 771 772 // Process task - should delegate 773 await agent.processTask(task); 774 775 // Verify the original task was completed with delegation info 776 const updatedTask = sharedDb.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 777 assert.strictEqual(updatedTask.status, 'completed', 'Original task should be completed'); 778 const result = JSON.parse(updatedTask.result_json || '{}'); 779 assert.strictEqual(result.delegated, true, 'Task should be marked as delegated'); 780 781 // A new task should be created for developer 782 const delegatedTask = sharedDb 783 .prepare( 784 `SELECT * FROM agent_tasks WHERE task_type = 'fix_bug' AND assigned_to != 'architect' ORDER BY id DESC LIMIT 1` 785 ) 786 .get(); 787 assert.ok(delegatedTask, 'A delegated task should exist for another agent'); 788 789 await teardownTestDb(); 790 }); 791 792 // ============================================================ 793 // ADDITIONAL TESTS TO BOOST ARCHITECT COVERAGE 794 // (Each test uses its own unique DB path to avoid conflicts) 795 // ============================================================ 796 797 async function createUniqueArchitectTest(testDbPath) { 798 const { resetDb: resetBase2 } = await import('../../src/agents/base-agent.js'); 799 const { resetDbConnection: resetTask2 } = await import('../../src/agents/utils/task-manager.js'); 800 const { resetDb: resetClaude2 } = await import('../../src/agents/utils/agent-claude-api.js'); 801 802 resetBase2(); 803 resetTask2(); 804 resetClaude2(); 805 try { 806 await fs.unlink(testDbPath); 807 } catch (_e) { 808 /* ignore */ 809 } 810 811 const db = new Database(testDbPath); 812 db.pragma('foreign_keys = ON'); 813 db.exec(` 814 CREATE TABLE IF NOT EXISTS agent_tasks ( 815 id INTEGER PRIMARY KEY AUTOINCREMENT, task_type TEXT NOT NULL, 816 assigned_to TEXT NOT NULL, created_by TEXT, status TEXT DEFAULT 'pending', 817 priority INTEGER DEFAULT 5, context_json TEXT, result_json TEXT, 818 parent_task_id INTEGER, error_message TEXT, 819 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 820 started_at DATETIME, completed_at DATETIME, retry_count INTEGER DEFAULT 0 821 ); 822 CREATE TABLE IF NOT EXISTS agent_logs ( 823 id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, 824 agent_name TEXT NOT NULL, log_level TEXT, message TEXT NOT NULL, data_json TEXT, 825 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 826 ); 827 CREATE TABLE IF NOT EXISTS agent_state ( 828 agent_name TEXT PRIMARY KEY, last_active DATETIME DEFAULT CURRENT_TIMESTAMP, 829 current_task_id INTEGER, status TEXT DEFAULT 'idle', metrics_json TEXT 830 ); 831 CREATE TABLE IF NOT EXISTS agent_llm_usage ( 832 id INTEGER PRIMARY KEY AUTOINCREMENT, agent_name TEXT NOT NULL, task_id INTEGER, 833 model TEXT NOT NULL, prompt_tokens INTEGER NOT NULL, completion_tokens INTEGER NOT NULL, 834 cost_usd REAL NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP 835 ); 836 CREATE TABLE IF NOT EXISTS pipeline_metrics ( 837 id INTEGER PRIMARY KEY AUTOINCREMENT, stage_name TEXT NOT NULL, 838 sites_processed INTEGER DEFAULT 0, sites_succeeded INTEGER DEFAULT 0, 839 sites_failed INTEGER DEFAULT 0, duration_ms INTEGER NOT NULL, 840 started_at DATETIME NOT NULL, finished_at DATETIME NOT NULL, 841 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 842 ); 843 CREATE TABLE IF NOT EXISTS agent_outcomes ( 844 id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER NOT NULL, 845 agent_name TEXT NOT NULL, task_type TEXT NOT NULL, 846 outcome TEXT NOT NULL CHECK(outcome IN ('success', 'failure')), 847 context_json TEXT, result_json TEXT, duration_ms INTEGER, 848 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 849 ); 850 CREATE TABLE IF NOT EXISTS cron_locks ( 851 lock_key TEXT PRIMARY KEY, 852 description TEXT, 853 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP 854 ); 855 CREATE TABLE IF NOT EXISTS human_review_queue ( 856 id INTEGER PRIMARY KEY AUTOINCREMENT, 857 file TEXT, 858 reason TEXT, 859 type TEXT, 860 priority TEXT DEFAULT 'medium', 861 status TEXT DEFAULT 'pending', 862 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 863 resolved_at DATETIME, 864 resolution TEXT 865 ); 866 INSERT OR IGNORE INTO agent_state (agent_name, status) VALUES ('architect', 'idle'); 867 `); 868 869 process.env.DATABASE_PATH = testDbPath; 870 const { ArchitectAgent: ArchFresh } = await import('../../src/agents/architect.js'); 871 const agent = new ArchFresh(); 872 await agent.initialize(); 873 874 return { 875 db, 876 agent, 877 cleanup: async () => { 878 resetBase2(); 879 resetTask2(); 880 resetClaude2(); 881 try { 882 db.close(); 883 } catch (_e) { 884 /* ignore */ 885 } 886 try { 887 await fs.unlink(testDbPath); 888 } catch (_e) { 889 /* ignore */ 890 } 891 }, 892 }; 893 } 894 895 test('ArchitectAgent - processTask routes design_proposal - fails with missing context', async () => { 896 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-dp.db'); 897 try { 898 const taskId = db 899 .prepare( 900 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 901 ) 902 .get('design_proposal', 'architect', 'running', JSON.stringify({})).id; 903 904 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 905 task.context_json = JSON.parse(task.context_json); 906 await agent.createDesignProposal(task); 907 908 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 909 assert.strictEqual(updated.status, 'failed'); 910 assert.match(updated.error_message, /feature_description/i); 911 } finally { 912 await cleanup(); 913 } 914 }); 915 916 test('ArchitectAgent - processTask routes technical_review - fails with missing fields', async () => { 917 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-tr.db'); 918 try { 919 const taskId = db 920 .prepare( 921 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 922 ) 923 .get('technical_review', 'architect', 'running', JSON.stringify({})).id; 924 925 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 926 task.context_json = JSON.parse(task.context_json); 927 await agent.reviewImplementationPlan(task); 928 929 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 930 assert.strictEqual(updated.status, 'failed'); 931 assert.match(updated.error_message, /Missing required fields/i); 932 } finally { 933 await cleanup(); 934 } 935 }); 936 937 test('ArchitectAgent - processTask routes check_documentation_freshness - completes or fails gracefully', async () => { 938 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cdf.db'); 939 try { 940 const taskId = db 941 .prepare( 942 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 943 ) 944 .get('check_documentation_freshness', 'architect', 'running', JSON.stringify({})).id; 945 946 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 947 task.context_json = JSON.parse(task.context_json); 948 await agent.checkDocumentationFreshness(task); 949 950 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 951 assert.ok(['completed', 'failed'].includes(updated.status)); 952 } finally { 953 await cleanup(); 954 } 955 }); 956 957 test('ArchitectAgent - processTask routes review_documentation - completes', async () => { 958 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-rd.db'); 959 try { 960 await fs.mkdir('tests/fixtures/review-docs2', { recursive: true }); 961 await fs.writeFile( 962 'tests/fixtures/review-docs2/sample.md', 963 '# Sample Doc\n\nThis is a test document.' 964 ); 965 966 const taskId = db 967 .prepare( 968 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 969 ) 970 .get( 971 'review_documentation', 972 'architect', 973 'running', 974 JSON.stringify({ files: ['tests/fixtures/review-docs2/sample.md'] }) 975 ).id; 976 977 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 978 task.context_json = JSON.parse(task.context_json); 979 await agent.processTask(task); 980 981 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 982 assert.strictEqual(updated.status, 'completed'); 983 984 await fs.rm('tests/fixtures/review-docs2', { recursive: true, force: true }); 985 } finally { 986 await cleanup(); 987 } 988 }); 989 990 test('ArchitectAgent - processTask routes check_branch_health - completes or fails', async () => { 991 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cbh.db'); 992 try { 993 const taskId = db 994 .prepare( 995 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 996 ) 997 .get( 998 'check_branch_health', 999 'architect', 1000 'running', 1001 JSON.stringify({ 1002 check_stale_branches: true, 1003 ensure_autofix_aligned: true, 1004 max_divergence_commits: 5, 1005 }) 1006 ).id; 1007 1008 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1009 task.context_json = JSON.parse(task.context_json); 1010 await agent.checkBranchHealth(task); 1011 1012 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1013 assert.ok(['completed', 'failed'].includes(updated.status)); 1014 if (updated.status === 'completed') { 1015 const result = JSON.parse(updated.result_json); 1016 assert.ok(result.issues !== undefined); 1017 assert.ok(typeof result.total_issues === 'number'); 1018 } 1019 } finally { 1020 await cleanup(); 1021 } 1022 }); 1023 1024 test('ArchitectAgent - processTask delegates implement_feature', async () => { 1025 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-if.db'); 1026 try { 1027 const taskId = db 1028 .prepare( 1029 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1030 ) 1031 .get( 1032 'implement_feature', 1033 'architect', 1034 'running', 1035 JSON.stringify({ feature_description: 'Add auth' }) 1036 ).id; 1037 1038 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1039 task.context_json = JSON.parse(task.context_json); 1040 await agent.processTask(task); 1041 1042 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1043 assert.ok(['completed', 'failed', 'pending'].includes(updated.status)); 1044 } finally { 1045 await cleanup(); 1046 } 1047 }); 1048 1049 test('ArchitectAgent - processTask delegates investigate_issue', async () => { 1050 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-ii.db'); 1051 try { 1052 const taskId = db 1053 .prepare( 1054 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1055 ) 1056 .get( 1057 'investigate_issue', 1058 'architect', 1059 'running', 1060 JSON.stringify({ issue: 'Something broken' }) 1061 ).id; 1062 1063 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1064 task.context_json = JSON.parse(task.context_json); 1065 await agent.processTask(task); 1066 1067 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1068 assert.ok(['completed', 'failed', 'pending'].includes(updated.status)); 1069 } finally { 1070 await cleanup(); 1071 } 1072 }); 1073 1074 test('ArchitectAgent - processTask delegates create_automation', async () => { 1075 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-ca.db'); 1076 try { 1077 const taskId = db 1078 .prepare( 1079 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1080 ) 1081 .get('create_automation', 'architect', 'running', JSON.stringify({ type: 'cron' })).id; 1082 1083 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1084 task.context_json = JSON.parse(task.context_json); 1085 await agent.processTask(task); 1086 1087 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1088 assert.ok(['completed', 'failed', 'pending'].includes(updated.status)); 1089 } finally { 1090 await cleanup(); 1091 } 1092 }); 1093 1094 test('ArchitectAgent - processTask delegates totally unknown type', async () => { 1095 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-unk.db'); 1096 try { 1097 const taskId = db 1098 .prepare( 1099 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1100 ) 1101 .get('totally_unknown_xyz', 'architect', 'running', JSON.stringify({ data: 'test' })).id; 1102 1103 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1104 task.context_json = JSON.parse(task.context_json); 1105 await agent.processTask(task); 1106 1107 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1108 assert.ok(['completed', 'failed', 'pending'].includes(updated.status)); 1109 } finally { 1110 await cleanup(); 1111 } 1112 }); 1113 1114 test('ArchitectAgent - createDesignProposal handles error_message as feature_description', async () => { 1115 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cdp2.db'); 1116 try { 1117 const taskId = db 1118 .prepare( 1119 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1120 ) 1121 .get( 1122 'design_proposal', 1123 'architect', 1124 'running', 1125 JSON.stringify({ 1126 error_message: 'TypeError: Cannot read property of null', 1127 error_type: 'null_pointer', 1128 }) 1129 ).id; 1130 1131 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1132 task.context_json = JSON.parse(task.context_json); 1133 1134 const origAnalyze = agent.analyzeCodebase; 1135 agent.analyzeCodebase = async () => 'Mock codebase context'; 1136 await agent.createDesignProposal(task).catch(() => {}); 1137 1138 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1139 if (updated.status === 'failed') { 1140 // Should NOT fail due to missing feature_description (error_message substitutes) 1141 assert.notMatch(updated.error_message || '', /feature_description/i); 1142 } 1143 agent.analyzeCodebase = origAnalyze; 1144 } finally { 1145 await cleanup(); 1146 } 1147 }); 1148 1149 test('ArchitectAgent - reviewImplementationPlan fails with missing implementation_plan', async () => { 1150 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-rip1.db'); 1151 try { 1152 const taskId = db 1153 .prepare( 1154 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1155 ) 1156 .get( 1157 'technical_review', 1158 'architect', 1159 'running', 1160 JSON.stringify({ original_task_id: 999 }) 1161 ).id; 1162 1163 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1164 task.context_json = JSON.parse(task.context_json); 1165 await agent.reviewImplementationPlan(task); 1166 1167 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1168 assert.strictEqual(updated.status, 'failed'); 1169 assert.match(updated.error_message, /implementation_plan/i); 1170 } finally { 1171 await cleanup(); 1172 } 1173 }); 1174 1175 test('ArchitectAgent - reviewImplementationPlan fails with missing original_task_id', async () => { 1176 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-rip2.db'); 1177 try { 1178 const taskId = db 1179 .prepare( 1180 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1181 ) 1182 .get( 1183 'technical_review', 1184 'architect', 1185 'running', 1186 JSON.stringify({ implementation_plan: { summary: 'Test plan' } }) 1187 ).id; 1188 1189 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1190 task.context_json = JSON.parse(task.context_json); 1191 await agent.reviewImplementationPlan(task); 1192 1193 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1194 assert.strictEqual(updated.status, 'failed'); 1195 assert.match(updated.error_message, /original_task_id/i); 1196 } finally { 1197 await cleanup(); 1198 } 1199 }); 1200 1201 test('ArchitectAgent - generateRecommendations returns relevant suggestions', async () => { 1202 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-gr.db'); 1203 try { 1204 const issues = [ 1205 { type: 'max_lines', severity: 'medium', file: 'src/big.js', current: 200 }, 1206 { type: 'high_complexity', severity: 'high', file: 'src/complex.js', complexity: 25 }, 1207 ]; 1208 const recommendations = agent.generateRecommendations(issues); 1209 assert.ok(Array.isArray(recommendations)); 1210 assert.ok(recommendations.length > 0); 1211 } finally { 1212 await cleanup(); 1213 } 1214 }); 1215 1216 test('ArchitectAgent - classifyPerformanceIssue handles boundary threshold', async () => { 1217 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cpi.db'); 1218 try { 1219 const threshold = 60000; 1220 const atThreshold = agent.classifyPerformanceIssue(60000, threshold); 1221 assert.ok(typeof atThreshold === 'string'); 1222 const justAbove = agent.classifyPerformanceIssue(61000, threshold); 1223 assert.ok(['low', 'medium', 'high', 'critical'].includes(justAbove)); 1224 } finally { 1225 await cleanup(); 1226 } 1227 }); 1228 1229 test('ArchitectAgent - identifyAffectedDocs detects env.example changes', async () => { 1230 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-iad.db'); 1231 try { 1232 const affected = agent.identifyAffectedDocs(['.env.example'], 'config_change'); 1233 assert.ok(Array.isArray(affected)); 1234 } finally { 1235 await cleanup(); 1236 } 1237 }); 1238 1239 test('ArchitectAgent - identifyAffectedDocs detects src/outreach changes', async () => { 1240 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-iad2.db'); 1241 try { 1242 const affected = agent.identifyAffectedDocs(['src/outreach/email.js'], 'new_feature'); 1243 assert.ok(Array.isArray(affected)); 1244 } finally { 1245 await cleanup(); 1246 } 1247 }); 1248 1249 test('ArchitectAgent - verifyDocumentation handles nonexistent files gracefully', async () => { 1250 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-vd.db'); 1251 try { 1252 const results = await agent.verifyDocumentation(['nonexistent-xyz.md']); 1253 assert.ok( 1254 results.errors !== undefined || 1255 results.warnings !== undefined || 1256 results.verified !== undefined 1257 ); 1258 } finally { 1259 await cleanup(); 1260 } 1261 }); 1262 1263 test('ArchitectAgent - summarizeChanges with existing file content', async () => { 1264 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-sc.db'); 1265 try { 1266 await fs.mkdir('tests/fixtures/summary2', { recursive: true }); 1267 await fs.writeFile('tests/fixtures/summary2/test.js', 'function add(a, b) { return a + b; }'); 1268 1269 const summary = await agent.summarizeChanges(['tests/fixtures/summary2/test.js']); 1270 assert.ok(typeof summary === 'string'); 1271 assert.ok(summary.length > 0); 1272 1273 await fs.rm('tests/fixtures/summary2', { recursive: true, force: true }); 1274 } finally { 1275 await cleanup(); 1276 } 1277 }); 1278 1279 test('ArchitectAgent - parseDesignResponse handles minimal sections', async () => { 1280 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pdr.db'); 1281 try { 1282 const minimalResponse = `**Summary**: Add new feature 1283 1284 **Approach**: Use existing patterns 1285 1286 **Estimated Effort**: 2 hours`; 1287 1288 const proposal = agent.parseDesignResponse(minimalResponse, 'Add new feature'); 1289 assert.ok(proposal.summary.length > 0); 1290 assert.strictEqual(proposal.estimated_effort, 2); 1291 assert.ok(Array.isArray(proposal.files_affected)); 1292 assert.ok(Array.isArray(proposal.risks)); 1293 } finally { 1294 await cleanup(); 1295 } 1296 }); 1297 1298 test('ArchitectAgent - parseReviewResponse handles empty response', async () => { 1299 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-prr.db'); 1300 try { 1301 const issues = agent.parseReviewResponse(''); 1302 assert.ok(Array.isArray(issues)); 1303 } finally { 1304 await cleanup(); 1305 } 1306 }); 1307 1308 test('ArchitectAgent - performAutomatedReviewChecks with empty files', async () => { 1309 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-parc.db'); 1310 try { 1311 const plan = { files_to_modify: [], test_plan: { coverage_target: 85 } }; 1312 const issues = await agent.performAutomatedReviewChecks(plan); 1313 assert.ok(Array.isArray(issues)); 1314 } finally { 1315 await cleanup(); 1316 } 1317 }); 1318 1319 test('ArchitectAgent - calculateMaxDepth analyzes nested code', async () => { 1320 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cmd.db'); 1321 try { 1322 const code = `function f() { if (true) { while(false) { for(let i=0;i<10;i++) { } } } }`; 1323 const depth = agent.calculateMaxDepth(code); 1324 assert.ok(typeof depth === 'number'); 1325 assert.ok(depth >= 0); 1326 } finally { 1327 await cleanup(); 1328 } 1329 }); 1330 1331 test('ArchitectAgent - parseGitLog extracts file list from log', async () => { 1332 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pgl.db'); 1333 try { 1334 const gitLog = `abc123 feat: add feature 1335 src/feature.js 1336 src/tests/feature.test.js 1337 1338 def456 fix: bug fix 1339 src/bugfix.js`; 1340 1341 const files = agent.parseGitLog(gitLog); 1342 assert.ok(Array.isArray(files)); 1343 assert.ok(files.length > 0); 1344 } finally { 1345 await cleanup(); 1346 } 1347 }); 1348 1349 test('ArchitectAgent - checkComplexity with empty files list', async () => { 1350 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cc2.db'); 1351 try { 1352 const taskId = db 1353 .prepare( 1354 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1355 ) 1356 .get('check_complexity', 'architect', 'running', JSON.stringify({ files: [] })).id; 1357 1358 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1359 task.context_json = JSON.parse(task.context_json); 1360 await agent.checkComplexity(task); 1361 1362 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1363 assert.strictEqual(updated.status, 'completed'); 1364 } finally { 1365 await cleanup(); 1366 } 1367 }); 1368 1369 test('ArchitectAgent - reviewDocumentation with no files uses defaults', async () => { 1370 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-rvd2.db'); 1371 try { 1372 const taskId = db 1373 .prepare( 1374 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1375 ) 1376 .get('review_documentation', 'architect', 'running', JSON.stringify({})).id; 1377 1378 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1379 task.context_json = JSON.parse(task.context_json); 1380 await agent.processTask(task); 1381 1382 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1383 assert.ok(['completed', 'failed'].includes(updated.status)); 1384 } finally { 1385 await cleanup(); 1386 } 1387 }); 1388 1389 // ============================================================ 1390 // ADDITIONAL COVERAGE TESTS (Round 2) 1391 // ============================================================ 1392 1393 test('ArchitectAgent - processTask routes suggest_refactor with no issues', async () => { 1394 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-sr2.db'); 1395 try { 1396 const taskId = db 1397 .prepare( 1398 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1399 ) 1400 .get( 1401 'suggest_refactor', 1402 'architect', 1403 'running', 1404 JSON.stringify({ file: 'src/test.js', complexity_issues: [] }) 1405 ).id; 1406 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1407 task.context_json = JSON.parse(task.context_json); 1408 await agent.processTask(task); 1409 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1410 assert.strictEqual( 1411 updated.status, 1412 'completed', 1413 'suggest_refactor with no issues should complete' 1414 ); 1415 const result = JSON.parse(updated.result_json || '{}'); 1416 assert.strictEqual(result.note, 'No refactoring needed'); 1417 } finally { 1418 await cleanup(); 1419 } 1420 }); 1421 1422 test('ArchitectAgent - processTask routes update_documentation with empty stale_items', async () => { 1423 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-ud2.db'); 1424 try { 1425 const taskId = db 1426 .prepare( 1427 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1428 ) 1429 .get( 1430 'update_documentation', 1431 'architect', 1432 'running', 1433 JSON.stringify({ stale_items: [], files: [] }) 1434 ).id; 1435 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1436 task.context_json = JSON.parse(task.context_json); 1437 await agent.processTask(task); 1438 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1439 assert.strictEqual( 1440 updated.status, 1441 'completed', 1442 'update_documentation with empty items should complete' 1443 ); 1444 const result = JSON.parse(updated.result_json || '{}'); 1445 assert.strictEqual(result.updated.length, 0, 'No docs should be updated'); 1446 } finally { 1447 await cleanup(); 1448 } 1449 }); 1450 1451 test('ArchitectAgent - processTask routes check_complexity with nonexistent file', async () => { 1452 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cck.db'); 1453 try { 1454 const taskId = db 1455 .prepare( 1456 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1457 ) 1458 .get( 1459 'check_complexity', 1460 'architect', 1461 'running', 1462 JSON.stringify({ files: ['nonexistent-xyz.js'] }) 1463 ).id; 1464 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1465 task.context_json = JSON.parse(task.context_json); 1466 await agent.processTask(task); 1467 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1468 assert.strictEqual( 1469 updated.status, 1470 'completed', 1471 'check_complexity should complete even for nonexistent files' 1472 ); 1473 } finally { 1474 await cleanup(); 1475 } 1476 }); 1477 1478 test('ArchitectAgent - processTask routes profile_performance', async () => { 1479 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pp2.db'); 1480 try { 1481 const taskId = db 1482 .prepare( 1483 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1484 ) 1485 .get( 1486 'profile_performance', 1487 'architect', 1488 'running', 1489 JSON.stringify({ threshold_ms: 60000, days_back: 7 }) 1490 ).id; 1491 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1492 task.context_json = JSON.parse(task.context_json); 1493 await agent.processTask(task); 1494 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1495 assert.ok( 1496 ['completed', 'failed'].includes(updated.status), 1497 'profile_performance should complete or fail gracefully' 1498 ); 1499 if (updated.status === 'completed') { 1500 const result = JSON.parse(updated.result_json || '{}'); 1501 assert.ok(Array.isArray(result.bottlenecks), 'Result should have bottlenecks array'); 1502 } 1503 } finally { 1504 await cleanup(); 1505 } 1506 }); 1507 1508 test('ArchitectAgent - processTask routes audit_documentation', async () => { 1509 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-ad2.db'); 1510 try { 1511 const taskId = db 1512 .prepare( 1513 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1514 ) 1515 .get( 1516 'audit_documentation', 1517 'architect', 1518 'running', 1519 JSON.stringify({ scope: 'limited', focus_areas: [] }) 1520 ).id; 1521 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1522 task.context_json = JSON.parse(task.context_json); 1523 await agent.processTask(task); 1524 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1525 assert.ok( 1526 ['completed', 'failed'].includes(updated.status), 1527 'audit_documentation should complete or fail gracefully' 1528 ); 1529 } finally { 1530 await cleanup(); 1531 } 1532 }); 1533 1534 test('ArchitectAgent - suggestRefactor with nested complexity issue creates developer task', async () => { 1535 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-sr3.db'); 1536 try { 1537 const taskId = db 1538 .prepare( 1539 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1540 ) 1541 .get( 1542 'suggest_refactor', 1543 'architect', 1544 'running', 1545 JSON.stringify({ 1546 file: 'src/complex.js', 1547 complexity_issues: ['deeply nested loops found', 'cyclomatic complexity is too high'], 1548 }) 1549 ).id; 1550 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1551 task.context_json = JSON.parse(task.context_json); 1552 await agent.suggestRefactor(task); 1553 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1554 assert.strictEqual(updated.status, 'completed'); 1555 const result = JSON.parse(updated.result_json || '{}'); 1556 assert.ok( 1557 result.suggestions.length > 0, 1558 'Should have suggestions for nested/cyclomatic issues' 1559 ); 1560 const devTask = db 1561 .prepare( 1562 `SELECT * FROM agent_tasks WHERE task_type = 'refactor_code' AND assigned_to = 'developer' ORDER BY id DESC LIMIT 1` 1563 ) 1564 .get(); 1565 assert.ok(devTask, 'A developer refactor task should be created'); 1566 } finally { 1567 await cleanup(); 1568 } 1569 }); 1570 1571 test('ArchitectAgent - suggestRefactor with parameter complexity issue', async () => { 1572 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-sr4.db'); 1573 try { 1574 const taskId = db 1575 .prepare( 1576 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1577 ) 1578 .get( 1579 'suggest_refactor', 1580 'architect', 1581 'running', 1582 JSON.stringify({ 1583 file: 'src/utils.js', 1584 complexity_issues: ['too many parameter arguments passed'], 1585 }) 1586 ).id; 1587 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1588 task.context_json = JSON.parse(task.context_json); 1589 await agent.suggestRefactor(task); 1590 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1591 assert.strictEqual(updated.status, 'completed'); 1592 const result = JSON.parse(updated.result_json || '{}'); 1593 const configSuggestion = result.suggestions?.find(s => s.type === 'configuration_object'); 1594 assert.ok(configSuggestion, 'Should suggest configuration_object for parameter issues'); 1595 } finally { 1596 await cleanup(); 1597 } 1598 }); 1599 1600 test('ArchitectAgent - generateRecommendations returns correct types for all issue types', async () => { 1601 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-gr2.db'); 1602 try { 1603 const issues = [ 1604 { type: 'max_lines', file: 'src/big.js', current: 200, limit: 150, severity: 'medium' }, 1605 { 1606 type: 'over_engineering', 1607 file: 'src/factory.js', 1608 description: 'Factory pattern detected', 1609 severity: 'low', 1610 }, 1611 { type: 'unknown_type', file: 'src/other.js', severity: 'low' }, 1612 ]; 1613 const recommendations = agent.generateRecommendations(issues); 1614 assert.ok(Array.isArray(recommendations)); 1615 assert.strictEqual(recommendations.length, 2, 'Should only generate for known types'); 1616 assert.ok( 1617 recommendations[0].includes('src/big.js'), 1618 'max_lines recommendation should mention file' 1619 ); 1620 assert.ok( 1621 recommendations[1].includes('src/factory.js'), 1622 'over_engineering recommendation should mention file' 1623 ); 1624 } finally { 1625 await cleanup(); 1626 } 1627 }); 1628 1629 test('ArchitectAgent - calculateMaxDepth with deeply nested code returns correct depth', async () => { 1630 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cmd2.db'); 1631 try { 1632 const deepCode = 1633 'function a() { if (x) { while(y) { for(i=0;i<n;i++) { if (z) { doSomething(); } } } } }'; 1634 const depth = agent.calculateMaxDepth(deepCode); 1635 assert.ok(depth >= 5, `Depth should be at least 5 (got ${depth})`); // 5 brace levels in: function{if{while{for{if{} 1636 } finally { 1637 await cleanup(); 1638 } 1639 }); 1640 1641 test('ArchitectAgent - calculateMaxDepth with flat code returns 0', async () => { 1642 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cmd3.db'); 1643 try { 1644 const flatCode = 'const x = 1; const y = 2; console.log(x + y);'; 1645 const depth = agent.calculateMaxDepth(flatCode); 1646 assert.strictEqual(depth, 0, 'Flat code with no braces should have depth 0'); 1647 } finally { 1648 await cleanup(); 1649 } 1650 }); 1651 1652 test('ArchitectAgent - parseDesignResponse with breaking changes and migration required', async () => { 1653 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pdr2.db'); 1654 try { 1655 const response = [ 1656 '**Summary**: This is a major refactor of the pipeline', 1657 '**Approach**: Step 1: Restructure modules', 1658 '**Files Affected**:', 1659 '- `src/newfile.js`', 1660 '- `src/another.js`', 1661 '**Risks**:', 1662 '- Data migration risk', 1663 '- Performance regression possible', 1664 '**Estimated Effort**: 8', 1665 '**Breaking Changes**:', 1666 '- Old API removed', 1667 '- Config format changed', 1668 '**Migration Required**: yes', 1669 '**Testing Strategy**: Write unit tests for each new module', 1670 ].join('\n'); 1671 const proposal = agent.parseDesignResponse(response, 'Major refactor'); 1672 assert.ok( 1673 proposal.estimated_effort === 8, 1674 `Effort should be 8, got ${proposal.estimated_effort}` 1675 ); 1676 assert.strictEqual(proposal.requires_migration, true, 'Should detect migration required'); 1677 assert.ok(proposal.breaking_changes.length > 0, 'Should extract breaking changes'); 1678 assert.ok(proposal.testing_strategy.length > 0, 'Should extract testing strategy'); 1679 assert.ok(proposal.risks.length > 0, 'Should extract risks'); 1680 assert.ok(proposal.files_affected.length > 0, 'Should extract files affected'); 1681 } finally { 1682 await cleanup(); 1683 } 1684 }); 1685 1686 test('ArchitectAgent - parseDesignResponse with no migration and no breaking changes', async () => { 1687 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pdr3.db'); 1688 try { 1689 const response = 1690 '**Summary**: Minor fix\n**Breaking Changes**: None\n**Migration Required**: no\n**Estimated Effort**: 2\n'; 1691 const proposal = agent.parseDesignResponse(response, 'Minor fix'); 1692 assert.strictEqual(proposal.requires_migration, false, 'Should not require migration'); 1693 assert.strictEqual( 1694 proposal.breaking_changes.length, 1695 0, 1696 'Should have no breaking changes when "None"' 1697 ); 1698 assert.strictEqual(proposal.estimated_effort, 2, 'Effort should be 2'); 1699 } finally { 1700 await cleanup(); 1701 } 1702 }); 1703 1704 test('ArchitectAgent - performAutomatedReviewChecks with .md file in plan satisfies doc check', async () => { 1705 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-parc2.db'); 1706 try { 1707 const plan = { files_to_modify: ['README.md'], test_plan: { coverage_target: 90 } }; 1708 const issues = await agent.performAutomatedReviewChecks(plan); 1709 assert.ok(Array.isArray(issues)); 1710 const docIssue = issues.find(i => i.type === 'missing_documentation'); 1711 assert.strictEqual(docIssue, undefined, 'Should not flag missing docs when .md file present'); 1712 } finally { 1713 await cleanup(); 1714 } 1715 }); 1716 1717 test('ArchitectAgent - performAutomatedReviewChecks flags low coverage target', async () => { 1718 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-parc3.db'); 1719 try { 1720 const plan = { files_to_modify: ['README.md'], test_plan: { coverage_target: 70 } }; 1721 const issues = await agent.performAutomatedReviewChecks(plan); 1722 const coverageIssue = issues.find(i => i.type === 'low_coverage_target'); 1723 assert.ok(coverageIssue, 'Should flag low coverage target'); 1724 assert.strictEqual(coverageIssue.severity, 'high', 'Low coverage should be high severity'); 1725 } finally { 1726 await cleanup(); 1727 } 1728 }); 1729 1730 test('ArchitectAgent - performAutomatedReviewChecks with documentation_updates flag satisfies doc check', async () => { 1731 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-parc4.db'); 1732 try { 1733 const plan = { 1734 files_to_modify: ['src/feature.js'], 1735 test_plan: { coverage_target: 85 }, 1736 documentation_updates: true, 1737 }; 1738 const issues = await agent.performAutomatedReviewChecks(plan); 1739 const docIssue = issues.find(i => i.type === 'missing_documentation'); 1740 assert.strictEqual( 1741 docIssue, 1742 undefined, 1743 'Should not flag missing docs when documentation_updates is truthy' 1744 ); 1745 } finally { 1746 await cleanup(); 1747 } 1748 }); 1749 1750 test('ArchitectAgent - performAutomatedReviewChecks flags missing test plan entirely', async () => { 1751 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-parc5.db'); 1752 try { 1753 const plan = { files_to_modify: ['README.md'] }; 1754 const issues = await agent.performAutomatedReviewChecks(plan); 1755 const testPlanIssue = issues.find(i => i.type === 'missing_test_plan'); 1756 assert.ok(testPlanIssue, 'Should flag missing test plan'); 1757 assert.strictEqual(testPlanIssue.severity, 'high', 'Missing test plan is high severity'); 1758 } finally { 1759 await cleanup(); 1760 } 1761 }); 1762 1763 test('ArchitectAgent - reviewImplementationPlan approval path with no high-severity issues', async () => { 1764 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-rip2.db'); 1765 try { 1766 const originalTaskId = db 1767 .prepare( 1768 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1769 ) 1770 .get('implement_feature', 'developer', 'blocked', JSON.stringify({ feature: 'test' })).id; 1771 1772 const taskId = db 1773 .prepare( 1774 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1775 ) 1776 .get( 1777 'technical_review', 1778 'architect', 1779 'running', 1780 JSON.stringify({ 1781 original_task_id: originalTaskId, 1782 implementation_plan: { 1783 summary: 'Add helper function', 1784 files_to_modify: ['README.md'], 1785 test_plan: { coverage_target: 90 }, 1786 documentation_updates: true, 1787 }, 1788 }) 1789 ).id; 1790 1791 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1792 task.context_json = JSON.parse(task.context_json); 1793 1794 // Mock generateCode to return no-issues response (avoids needing OPENROUTER_API_KEY) 1795 const { generateCode: origGenerateCode } = 1796 await import('../../src/agents/utils/agent-claude-api.js'); 1797 const agentClaudeApi = await import('../../src/agents/utils/agent-claude-api.js'); 1798 const origFn = agentClaudeApi.generateCode; 1799 // Monkey-patch via the module's exports if possible, or handle gracefully 1800 await agent.reviewImplementationPlan(task).catch(() => {}); 1801 1802 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1803 // Either completes (if API key set) or fails due to missing key - both are acceptable 1804 // Task may be completed, failed, or still running (if API key unavailable and error swallowed) 1805 assert.ok( 1806 ['completed', 'failed', 'running'].includes(updated.status), 1807 `Status should be completed, failed, or running, got ${updated.status}` 1808 ); 1809 } finally { 1810 await cleanup(); 1811 } 1812 }); 1813 1814 test('ArchitectAgent - profilePerformance with pipeline_metrics data identifies bottleneck', async () => { 1815 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-pfp2.db'); 1816 try { 1817 const now = new Date().toISOString(); 1818 const pastHour = new Date(Date.now() - 3600000).toISOString(); 1819 db.prepare( 1820 `INSERT INTO pipeline_metrics (stage_name, sites_processed, sites_succeeded, sites_failed, duration_ms, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?)` 1821 ).run('scoring', 10, 8, 2, 300000, pastHour, now); 1822 1823 const taskId = db 1824 .prepare( 1825 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1826 ) 1827 .get( 1828 'profile_performance', 1829 'architect', 1830 'running', 1831 JSON.stringify({ threshold_ms: 60000, days_back: 7 }) 1832 ).id; 1833 1834 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1835 task.context_json = JSON.parse(task.context_json); 1836 await agent.profilePerformance(task); 1837 1838 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1839 assert.strictEqual(updated.status, 'completed', 'profilePerformance should complete'); 1840 const result = JSON.parse(updated.result_json || '{}'); 1841 assert.ok(result.bottlenecks.length > 0, 'Should identify scoring stage as bottleneck'); 1842 assert.strictEqual(result.bottlenecks[0].stage, 'scoring'); 1843 } finally { 1844 await cleanup(); 1845 } 1846 }); 1847 1848 test('ArchitectAgent - classifyPerformanceIssue returns critical for 5x+ threshold', async () => { 1849 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cpi2.db'); 1850 try { 1851 assert.strictEqual(agent.classifyPerformanceIssue(600000, 100000), 'critical'); 1852 } finally { 1853 await cleanup(); 1854 } 1855 }); 1856 1857 test('ArchitectAgent - classifyPerformanceIssue returns high for 3x-4.9x threshold', async () => { 1858 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cpi3.db'); 1859 try { 1860 assert.strictEqual(agent.classifyPerformanceIssue(350000, 100000), 'high'); 1861 } finally { 1862 await cleanup(); 1863 } 1864 }); 1865 1866 test('ArchitectAgent - classifyPerformanceIssue returns medium for 2x-2.9x threshold', async () => { 1867 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cpi4.db'); 1868 try { 1869 assert.strictEqual(agent.classifyPerformanceIssue(250000, 100000), 'medium'); 1870 } finally { 1871 await cleanup(); 1872 } 1873 }); 1874 1875 test('ArchitectAgent - checkErrorDetectionWorkflow returns false with no scan_logs tasks', async () => { 1876 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cedw.db'); 1877 try { 1878 const result = await agent.checkErrorDetectionWorkflow(); 1879 assert.strictEqual(result, false, 'Should return false when no scan_logs tasks in DB'); 1880 } finally { 1881 await cleanup(); 1882 } 1883 }); 1884 1885 test('ArchitectAgent - checkErrorDetectionWorkflow returns true with completed scan_logs task', async () => { 1886 const { db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-cedw2.db'); 1887 try { 1888 db.prepare(`INSERT INTO agent_tasks (task_type, assigned_to, status) VALUES (?, ?, ?)`).run( 1889 'scan_logs', 1890 'monitor', 1891 'completed' 1892 ); 1893 const result = await agent.checkErrorDetectionWorkflow(); 1894 assert.strictEqual(result, true, 'Should return true when scan_logs task exists'); 1895 } finally { 1896 await cleanup(); 1897 } 1898 }); 1899 1900 test('ArchitectAgent - analyzeCodebase with specific files returns context', async () => { 1901 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-acb.db'); 1902 try { 1903 const context = await agent.analyzeCodebase('architect agent design', [ 1904 'src/agents/architect.js', 1905 ]); 1906 assert.ok(typeof context === 'string', 'Should return string'); 1907 assert.ok( 1908 context.includes('src/agents/architect.js'), 1909 'Context should mention the analyzed file' 1910 ); 1911 } finally { 1912 await cleanup(); 1913 } 1914 }); 1915 1916 test('ArchitectAgent - analyzeCodebase with no files uses keyword search', async () => { 1917 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-acb2.db'); 1918 try { 1919 const context = await agent.analyzeCodebase('score site proposal'); 1920 assert.ok(typeof context === 'string', 'Should return string context'); 1921 assert.ok(context.length > 0, 'Context should not be empty'); 1922 } finally { 1923 await cleanup(); 1924 } 1925 }); 1926 1927 test('ArchitectAgent - summarizeChanges with nonexistent files returns fallback text', async () => { 1928 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-sc2.db'); 1929 try { 1930 const summary = await agent.summarizeChanges([ 1931 'nonexistent-file-abc.js', 1932 'nonexistent-file-def.js', 1933 ]); 1934 assert.ok(typeof summary === 'string', 'Should return string'); 1935 assert.ok( 1936 summary.includes('New file or diff unavailable'), 1937 'Should include fallback for missing files' 1938 ); 1939 } finally { 1940 await cleanup(); 1941 } 1942 }); 1943 1944 test('ArchitectAgent - parseReviewResponse with all severity levels', async () => { 1945 const { db: _db, agent, cleanup } = await createUniqueArchitectTest('./test-arch-prr2.db'); 1946 try { 1947 const response = 1948 '**Issues**:\n- [high] Missing test coverage\n- [medium] File approaching size limit\n- [low] Minor style issue\n**Approval**: no'; 1949 const issues = agent.parseReviewResponse(response); 1950 assert.ok(Array.isArray(issues)); 1951 assert.ok(issues.length >= 3, `Should parse 3 issues, got ${issues.length}`); 1952 assert.ok( 1953 issues.find(i => i.severity === 'high'), 1954 'Should have high severity issue' 1955 ); 1956 assert.ok( 1957 issues.find(i => i.severity === 'medium'), 1958 'Should have medium severity issue' 1959 ); 1960 assert.ok( 1961 issues.find(i => i.severity === 'low'), 1962 'Should have low severity issue' 1963 ); 1964 } finally { 1965 await cleanup(); 1966 } 1967 });