claude-flow-novice 2.15.4 → 2.15.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/cfn-extras/.gs-api-quota.json +16 -0
- package/.claude/cfn-extras/.gs-progress-state.json +22 -0
- package/.claude/cfn-extras/GOOGLE_SHEETS_IMPLEMENTATION_SUMMARY.md +414 -0
- package/.claude/cfn-extras/agents/google-sheets/README.md +114 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-advanced-analytics-specialist.md +288 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-api-integrator.md +127 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-automation-scripting-specialist.md +195 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-business-validator.md +179 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-collaboration-security-specialist.md +240 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-coordinator.md +214 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-data-transformer.md +127 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-data-validation-quality-specialist.md +177 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-data-validator.md +119 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-data-visualization-specialist.md +135 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-design-layout-specialist.md +109 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-formula-engineer.md +127 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-formula-engineering-specialist.md +138 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-formula-validator.md +128 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-generalist.md +645 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-integration-api-specialist.md +258 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-performance-analyst.md +125 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-performance-optimization-specialist.md +211 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-schema-designer.md +130 -0
- package/.claude/cfn-extras/agents/google-sheets/google-sheets-template-architecture-specialist.md +259 -0
- package/.claude/cfn-extras/docs/GOOGLE_SHEETS_CFN_LOOP.md +617 -0
- package/.claude/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +453 -0
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +272 -0
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/api-call.sh +254 -0
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/test.sh +174 -0
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/validate.sh +98 -0
- package/.claude/cfn-extras/skills/google-sheets-decomposition/SKILL.md +269 -0
- package/.claude/cfn-extras/skills/google-sheets-decomposition/decompose.sh +313 -0
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +237 -0
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/build-formula.sh +220 -0
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/test.sh +172 -0
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/validate.sh +98 -0
- package/.claude/cfn-extras/skills/google-sheets-progress/SKILL.md +287 -0
- package/.claude/cfn-extras/skills/google-sheets-progress/test.sh +385 -0
- package/.claude/cfn-extras/skills/google-sheets-progress/track-progress.sh +516 -0
- package/.claude/cfn-extras/skills/google-sheets-progress/validate.sh +119 -0
- package/.claude/cfn-extras/skills/google-sheets-sprint-order/SKILL.md +277 -0
- package/.claude/cfn-extras/skills/google-sheets-sprint-order/order-sprints.sh +233 -0
- package/.claude/cfn-extras/skills/google-sheets-validation/SKILL.md +352 -0
- package/.claude/cfn-extras/skills/google-sheets-validation/test.sh +355 -0
- package/.claude/cfn-extras/skills/google-sheets-validation/validate-state.sh +374 -0
- package/.claude/cfn-extras/skills/google-sheets-validation/validate.sh +128 -0
- package/.claude/commands/cfn-context.md +10 -0
- package/.claude/commands/cfn-loop-cli.md +44 -14
- package/.claude/commands/google-sheets/google-sheets-loop.md +289 -0
- package/.claude/skills/cfn-agent-selector/SKILL.md +143 -0
- package/.claude/skills/cfn-agent-selector/select-agents.sh +94 -0
- package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -2
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +21 -2
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +11 -5
- package/.claude/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +348 -0
- package/.claude/skills/cfn-docker-redis-coordination/README.md +294 -0
- package/.claude/skills/cfn-docker-redis-coordination/jest.config.js +37 -0
- package/.claude/skills/cfn-docker-redis-coordination/package-lock.json +5259 -0
- package/.claude/skills/cfn-docker-redis-coordination/package.json +40 -0
- package/.claude/skills/cfn-docker-redis-coordination/src/coordinator.ts +801 -0
- package/.claude/skills/cfn-docker-redis-coordination/src/index.ts +42 -0
- package/.claude/skills/cfn-docker-redis-coordination/src/types.ts +351 -0
- package/.claude/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +1464 -0
- package/.claude/skills/cfn-docker-redis-coordination/tsconfig.json +30 -0
- package/.claude/skills/cfn-loop-orchestration/.eslintrc.js +56 -0
- package/.claude/skills/cfn-loop-orchestration/.prettierrc.json +18 -0
- package/.claude/skills/cfn-loop-orchestration/README.md +149 -41
- package/.claude/skills/cfn-loop-orchestration/jest.config.js +67 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate-wrapper.sh +268 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +147 -16
- package/.claude/skills/cfn-loop-orchestration/package-lock.json +5470 -0
- package/.claude/skills/cfn-loop-orchestration/package.json +49 -0
- package/.claude/skills/cfn-loop-orchestration/src/agent-spawner/agent-spawner.ts +34 -0
- package/.claude/skills/cfn-loop-orchestration/src/gate-checker/gate-checker.ts +36 -0
- package/.claude/skills/cfn-loop-orchestration/src/index.ts +14 -0
- package/.claude/skills/cfn-loop-orchestration/src/orchestrator/orchestrator.ts +31 -0
- package/.claude/skills/cfn-loop-orchestration/src/redis/redis-coordinator.ts +72 -0
- package/.claude/skills/cfn-loop-orchestration/src/types.ts +188 -0
- package/.claude/skills/cfn-loop-orchestration/src/utils/logger.ts +32 -0
- package/.claude/skills/cfn-loop-orchestration/tests/setup.ts +22 -0
- package/.claude/skills/cfn-loop-orchestration/tests/types.test.ts +132 -0
- package/.claude/skills/cfn-loop-orchestration/tsconfig.json +54 -0
- package/.claude/skills/cfn-redis-coordination/bash-wrappers/store-context.sh +23 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-logger.d.ts +92 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-logger.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-logger.js +329 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-logger.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-recovery.d.ts +75 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-recovery.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-recovery.js +302 -0
- package/.claude/skills/cfn-redis-coordination/dist/agent-recovery.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/completion-reporter.d.ts +58 -0
- package/.claude/skills/cfn-redis-coordination/dist/completion-reporter.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/completion-reporter.js +237 -0
- package/.claude/skills/cfn-redis-coordination/dist/completion-reporter.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/context-manager.d.ts +63 -0
- package/.claude/skills/cfn-redis-coordination/dist/context-manager.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/context-manager.js +230 -0
- package/.claude/skills/cfn-redis-coordination/dist/context-manager.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/index.d.ts +45 -0
- package/.claude/skills/cfn-redis-coordination/dist/index.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/index.js +114 -0
- package/.claude/skills/cfn-redis-coordination/dist/index.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/mode-detector.d.ts +31 -0
- package/.claude/skills/cfn-redis-coordination/dist/mode-detector.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/mode-detector.js +185 -0
- package/.claude/skills/cfn-redis-coordination/dist/mode-detector.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/redis-client.d.ts +191 -0
- package/.claude/skills/cfn-redis-coordination/dist/redis-client.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/redis-client.js +509 -0
- package/.claude/skills/cfn-redis-coordination/dist/redis-client.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/result-collector.d.ts +75 -0
- package/.claude/skills/cfn-redis-coordination/dist/result-collector.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/result-collector.js +281 -0
- package/.claude/skills/cfn-redis-coordination/dist/result-collector.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/swarm-manager.d.ts +75 -0
- package/.claude/skills/cfn-redis-coordination/dist/swarm-manager.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/swarm-manager.js +354 -0
- package/.claude/skills/cfn-redis-coordination/dist/swarm-manager.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-analyzer.d.ts +62 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-analyzer.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-analyzer.js +305 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-analyzer.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-executor.d.ts +97 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-executor.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-executor.js +283 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-executor.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/types.d.ts +176 -0
- package/.claude/skills/cfn-redis-coordination/dist/types.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/types.js +81 -0
- package/.claude/skills/cfn-redis-coordination/dist/types.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/waiting-coordinator.d.ts +86 -0
- package/.claude/skills/cfn-redis-coordination/dist/waiting-coordinator.d.ts.map +1 -0
- package/.claude/skills/cfn-redis-coordination/dist/waiting-coordinator.js +419 -0
- package/.claude/skills/cfn-redis-coordination/dist/waiting-coordinator.js.map +1 -0
- package/.claude/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +553 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -2
- package/.claude/skills/cfn-redis-coordination/jest.config.js +23 -0
- package/.claude/skills/cfn-redis-coordination/package-lock.json +5272 -0
- package/.claude/skills/cfn-redis-coordination/package.json +45 -0
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +21 -8
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +3 -2
- package/.claude/skills/cfn-redis-coordination/src/agent-logger.ts +446 -0
- package/.claude/skills/cfn-redis-coordination/src/agent-recovery.ts +454 -0
- package/.claude/skills/cfn-redis-coordination/src/completion-reporter.ts +396 -0
- package/.claude/skills/cfn-redis-coordination/src/context-manager.ts +327 -0
- package/.claude/skills/cfn-redis-coordination/src/index.ts +82 -0
- package/.claude/skills/cfn-redis-coordination/src/mode-detector.ts +155 -0
- package/.claude/skills/cfn-redis-coordination/src/redis/redis-client.ts +305 -0
- package/.claude/skills/cfn-redis-coordination/src/redis/redis-functions.ts +283 -0
- package/.claude/skills/cfn-redis-coordination/src/redis-client.ts +654 -0
- package/.claude/skills/cfn-redis-coordination/src/result-collector.ts +437 -0
- package/.claude/skills/cfn-redis-coordination/src/swarm-manager.ts +494 -0
- package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +404 -0
- package/.claude/skills/cfn-redis-coordination/src/task-executor.ts +423 -0
- package/.claude/skills/cfn-redis-coordination/src/types.ts +235 -0
- package/.claude/skills/cfn-redis-coordination/src/waiting-coordinator.ts +587 -0
- package/.claude/skills/cfn-redis-coordination/test-connection-attempts.js +70 -0
- package/.claude/skills/cfn-redis-coordination/test-mode-simple.js +121 -0
- package/.claude/skills/cfn-redis-coordination/test-redis-check.js +84 -0
- package/.claude/skills/cfn-redis-coordination/test-task-mode-redis.cjs +391 -0
- package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +779 -0
- package/.claude/skills/cfn-redis-coordination/tsconfig.json +31 -0
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +172 -2
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +1 -1
- package/claude-assets/agents/custom/cfn-redis-operations.md +530 -0
- package/claude-assets/agents/custom/cfn-system-expert.md +77 -0
- package/claude-assets/cfn-extras/.gs-api-quota.json +16 -0
- package/claude-assets/cfn-extras/.gs-progress-state.json +22 -0
- package/claude-assets/cfn-extras/GOOGLE_SHEETS_IMPLEMENTATION_SUMMARY.md +414 -0
- package/claude-assets/cfn-extras/agents/google-sheets/README.md +114 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-advanced-analytics-specialist.md +288 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-api-integrator.md +127 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-automation-scripting-specialist.md +195 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-business-validator.md +179 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-collaboration-security-specialist.md +240 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-coordinator.md +214 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-data-transformer.md +127 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-data-validation-quality-specialist.md +177 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-data-validator.md +119 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-data-visualization-specialist.md +135 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-design-layout-specialist.md +109 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-formula-engineer.md +127 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-formula-engineering-specialist.md +138 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-formula-validator.md +128 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-generalist.md +645 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-integration-api-specialist.md +258 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-performance-analyst.md +125 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-performance-optimization-specialist.md +211 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-schema-designer.md +130 -0
- package/claude-assets/cfn-extras/agents/google-sheets/google-sheets-template-architecture-specialist.md +259 -0
- package/claude-assets/cfn-extras/docs/GOOGLE_SHEETS_CFN_LOOP.md +617 -0
- package/claude-assets/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +453 -0
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +272 -0
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/api-call.sh +254 -0
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/test.sh +174 -0
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/validate.sh +98 -0
- package/claude-assets/cfn-extras/skills/google-sheets-decomposition/SKILL.md +269 -0
- package/claude-assets/cfn-extras/skills/google-sheets-decomposition/decompose.sh +313 -0
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +237 -0
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/build-formula.sh +220 -0
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/test.sh +172 -0
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/validate.sh +98 -0
- package/claude-assets/cfn-extras/skills/google-sheets-progress/SKILL.md +287 -0
- package/claude-assets/cfn-extras/skills/google-sheets-progress/test.sh +385 -0
- package/claude-assets/cfn-extras/skills/google-sheets-progress/track-progress.sh +516 -0
- package/claude-assets/cfn-extras/skills/google-sheets-progress/validate.sh +119 -0
- package/claude-assets/cfn-extras/skills/google-sheets-sprint-order/SKILL.md +277 -0
- package/claude-assets/cfn-extras/skills/google-sheets-sprint-order/order-sprints.sh +233 -0
- package/claude-assets/cfn-extras/skills/google-sheets-validation/SKILL.md +352 -0
- package/claude-assets/cfn-extras/skills/google-sheets-validation/test.sh +355 -0
- package/claude-assets/cfn-extras/skills/google-sheets-validation/validate-state.sh +374 -0
- package/claude-assets/cfn-extras/skills/google-sheets-validation/validate.sh +128 -0
- package/claude-assets/commands/cfn-context.md +10 -0
- package/claude-assets/commands/cfn-loop-cli.md +44 -14
- package/claude-assets/commands/google-sheets/google-sheets-loop.md +289 -0
- package/claude-assets/hooks/cfn-pre-execution/SESSION_START_README.md +87 -0
- package/claude-assets/hooks/cfn-pre-execution/TEST_SESSION_START.md +128 -0
- package/claude-assets/hooks/cfn-pre-execution/session-start-context.sh +111 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/INTEGRATION_EXAMPLE.md +209 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/README.md +130 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md +243 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/agent-mappings.json +142 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh +173 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh +71 -0
- package/claude-assets/skills/cfn-agent-selection-with-fallback/test-agent-selection.sh +282 -0
- package/claude-assets/skills/cfn-agent-selector/SKILL.md +143 -0
- package/claude-assets/skills/cfn-agent-selector/select-agents.sh +94 -0
- package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -2
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +21 -2
- package/claude-assets/skills/cfn-docker-coordination/.eslintrc.json +33 -0
- package/claude-assets/skills/cfn-docker-coordination/README.md +349 -0
- package/claude-assets/skills/cfn-docker-coordination/docker-helpers.sh +433 -0
- package/claude-assets/skills/cfn-docker-coordination/jest.config.js +25 -0
- package/claude-assets/skills/cfn-docker-coordination/package-lock.json +6827 -0
- package/claude-assets/skills/cfn-docker-coordination/package.json +38 -0
- package/claude-assets/skills/cfn-docker-coordination/src/agent-container.ts +471 -0
- package/claude-assets/skills/cfn-docker-coordination/src/docker-client.ts +483 -0
- package/claude-assets/skills/cfn-docker-coordination/src/health-checker.ts +418 -0
- package/claude-assets/skills/cfn-docker-coordination/src/index.ts +45 -0
- package/claude-assets/skills/cfn-docker-coordination/src/network-manager.ts +377 -0
- package/claude-assets/skills/cfn-docker-coordination/src/types.ts +412 -0
- package/claude-assets/skills/cfn-docker-coordination/src/volume-manager.ts +389 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/agent-container.test.ts +379 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/docker-client.test.ts +345 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/health-checker.test.ts +535 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/integration.test.ts +193 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/network-manager.test.ts +352 -0
- package/claude-assets/skills/cfn-docker-coordination/tests/setup.ts +36 -0
- package/claude-assets/skills/cfn-docker-coordination/tsconfig.json +29 -0
- package/claude-assets/skills/cfn-docker-logging/INTEGRATION.md +268 -0
- package/claude-assets/skills/cfn-docker-logging/SAMPLE_OUTPUTS.md +237 -0
- package/claude-assets/skills/cfn-docker-logging/SKILL.md +442 -0
- package/claude-assets/skills/cfn-docker-logging/capture-container-logs.sh +120 -0
- package/claude-assets/skills/cfn-docker-logging/enable-logging.sh +430 -0
- package/claude-assets/skills/cfn-docker-logging/init-hybrid-logging.sh +210 -0
- package/claude-assets/skills/cfn-docker-logging/queries/analytics-summary.sh +87 -0
- package/claude-assets/skills/cfn-docker-logging/queries/query-agent-timeline.sh +51 -0
- package/claude-assets/skills/cfn-docker-logging/queries/query-consensus-history.sh +56 -0
- package/claude-assets/skills/cfn-docker-logging/queries/query-coordination-timeline.sh +39 -0
- package/claude-assets/skills/cfn-docker-logging/queries/query-failed-containers.sh +40 -0
- package/claude-assets/skills/cfn-docker-logging/queries/query-gate-checks.sh +39 -0
- package/claude-assets/skills/cfn-docker-logging/schema.sql +111 -0
- package/claude-assets/skills/cfn-docker-logging/sqlite-helpers.sh +240 -0
- package/claude-assets/skills/cfn-docker-logging/test-hybrid-logging.sh +331 -0
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +11 -5
- package/claude-assets/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +348 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/README.md +294 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/jest.config.js +37 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/package-lock.json +5259 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/package.json +40 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/src/coordinator.ts +801 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/src/index.ts +42 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/src/types.ts +351 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +1464 -0
- package/claude-assets/skills/cfn-docker-redis-coordination/tsconfig.json +30 -0
- package/claude-assets/skills/cfn-error-logging/.eslintrc.json +57 -0
- package/claude-assets/skills/cfn-error-logging/.prettierrc.json +10 -0
- package/claude-assets/skills/cfn-error-logging/MIGRATION_SUMMARY.md +485 -0
- package/claude-assets/skills/cfn-error-logging/package.json +47 -0
- package/claude-assets/skills/cfn-error-logging/src/error-logger.ts +1042 -0
- package/claude-assets/skills/cfn-error-logging/src/index.ts +12 -0
- package/claude-assets/skills/cfn-error-logging/src/types.ts +456 -0
- package/claude-assets/skills/cfn-error-logging/tests/error-logger.test.ts +1302 -0
- package/claude-assets/skills/cfn-error-logging/tsconfig.json +38 -0
- package/claude-assets/skills/cfn-loop-orchestration/.eslintrc.js +56 -0
- package/claude-assets/skills/cfn-loop-orchestration/.prettierrc.json +18 -0
- package/claude-assets/skills/cfn-loop-orchestration/README.md +149 -41
- package/claude-assets/skills/cfn-loop-orchestration/jest.config.js +67 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate-wrapper.sh +268 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +147 -16
- package/claude-assets/skills/cfn-loop-orchestration/package-lock.json +5470 -0
- package/claude-assets/skills/cfn-loop-orchestration/package.json +49 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/agent-spawner/agent-spawner.ts +34 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/gate-checker/gate-checker.ts +36 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/index.ts +14 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/orchestrator/orchestrator.ts +31 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/redis/redis-coordinator.ts +72 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/types.ts +188 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/utils/logger.ts +32 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/setup.ts +22 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/types.test.ts +132 -0
- package/claude-assets/skills/cfn-loop-orchestration/tsconfig.json +54 -0
- package/claude-assets/skills/cfn-redis-coordination/bash-wrappers/store-context.sh +23 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-logger.d.ts +92 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-logger.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-logger.js +329 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-logger.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-recovery.d.ts +75 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-recovery.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-recovery.js +302 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/agent-recovery.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/completion-reporter.d.ts +58 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/completion-reporter.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/completion-reporter.js +237 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/completion-reporter.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/context-manager.d.ts +63 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/context-manager.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/context-manager.js +230 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/context-manager.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/index.d.ts +45 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/index.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/index.js +114 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/index.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/mode-detector.d.ts +31 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/mode-detector.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/mode-detector.js +185 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/mode-detector.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/redis-client.d.ts +191 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/redis-client.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/redis-client.js +509 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/redis-client.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/result-collector.d.ts +75 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/result-collector.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/result-collector.js +281 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/result-collector.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/swarm-manager.d.ts +75 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/swarm-manager.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/swarm-manager.js +354 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/swarm-manager.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-analyzer.d.ts +62 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-analyzer.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-analyzer.js +305 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-analyzer.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-executor.d.ts +97 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-executor.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-executor.js +283 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-executor.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/types.d.ts +176 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/types.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/types.js +81 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/types.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/waiting-coordinator.d.ts +86 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/waiting-coordinator.d.ts.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/waiting-coordinator.js +419 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/waiting-coordinator.js.map +1 -0
- package/claude-assets/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +553 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -2
- package/claude-assets/skills/cfn-redis-coordination/jest.config.js +23 -0
- package/claude-assets/skills/cfn-redis-coordination/package-lock.json +5272 -0
- package/claude-assets/skills/cfn-redis-coordination/package.json +45 -0
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +21 -8
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +3 -2
- package/claude-assets/skills/cfn-redis-coordination/src/agent-logger.ts +446 -0
- package/claude-assets/skills/cfn-redis-coordination/src/agent-recovery.ts +454 -0
- package/claude-assets/skills/cfn-redis-coordination/src/completion-reporter.ts +396 -0
- package/claude-assets/skills/cfn-redis-coordination/src/context-manager.ts +327 -0
- package/claude-assets/skills/cfn-redis-coordination/src/index.ts +82 -0
- package/claude-assets/skills/cfn-redis-coordination/src/mode-detector.ts +155 -0
- package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-client.ts +305 -0
- package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-functions.ts +283 -0
- package/claude-assets/skills/cfn-redis-coordination/src/redis-client.ts +654 -0
- package/claude-assets/skills/cfn-redis-coordination/src/result-collector.ts +437 -0
- package/claude-assets/skills/cfn-redis-coordination/src/swarm-manager.ts +494 -0
- package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +404 -0
- package/claude-assets/skills/cfn-redis-coordination/src/task-executor.ts +423 -0
- package/claude-assets/skills/cfn-redis-coordination/src/types.ts +235 -0
- package/claude-assets/skills/cfn-redis-coordination/src/waiting-coordinator.ts +587 -0
- package/claude-assets/skills/cfn-redis-coordination/test-connection-attempts.js +70 -0
- package/claude-assets/skills/cfn-redis-coordination/test-mode-simple.js +121 -0
- package/claude-assets/skills/cfn-redis-coordination/test-redis-check.js +84 -0
- package/claude-assets/skills/cfn-redis-coordination/test-task-mode-redis.cjs +391 -0
- package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +779 -0
- package/claude-assets/skills/cfn-redis-coordination/tsconfig.json +31 -0
- package/claude-assets/skills/cfn-skill-propagation/README.md +233 -0
- package/claude-assets/skills/cfn-skill-propagation/package-lock.json +5174 -0
- package/claude-assets/skills/cfn-skill-propagation/package.json +52 -0
- package/claude-assets/skills/cfn-skill-propagation/propagate-skill-update.sh +32 -0
- package/claude-assets/skills/cfn-skill-propagation/src/cli.ts +75 -0
- package/claude-assets/skills/cfn-skill-propagation/src/database-adapter.ts +239 -0
- package/claude-assets/skills/cfn-skill-propagation/src/file-system-adapter.ts +113 -0
- package/claude-assets/skills/cfn-skill-propagation/src/index.ts +72 -0
- package/claude-assets/skills/cfn-skill-propagation/src/logger.ts +43 -0
- package/claude-assets/skills/cfn-skill-propagation/src/metadata-parser.ts +154 -0
- package/claude-assets/skills/cfn-skill-propagation/src/skill-propagator.ts +274 -0
- package/claude-assets/skills/cfn-skill-propagation/src/skill-validator.ts +179 -0
- package/claude-assets/skills/cfn-skill-propagation/src/types.ts +143 -0
- package/claude-assets/skills/cfn-skill-propagation/src/version-manager.ts +118 -0
- package/claude-assets/skills/cfn-skill-propagation/tests/file-system-adapter.test.ts +91 -0
- package/claude-assets/skills/cfn-skill-propagation/tests/metadata-parser.test.ts +176 -0
- package/claude-assets/skills/cfn-skill-propagation/tests/skill-propagator.test.ts +209 -0
- package/claude-assets/skills/cfn-skill-propagation/tests/skill-validator.test.ts +203 -0
- package/claude-assets/skills/cfn-skill-propagation/tests/version-manager.test.ts +115 -0
- package/claude-assets/skills/cfn-skill-propagation/tsconfig.json +34 -0
- package/claude-assets/skills/task-classifier/SKILL.md +81 -0
- package/claude-assets/skills/task-classifier/classify-task.sh +62 -0
- package/claude-assets/skills/workflow-codification/package-lock.json +5170 -0
- package/claude-assets/skills/workflow-codification/package.json +30 -0
- package/claude-assets/skills/workflow-codification/src/index.ts +24 -0
- package/claude-assets/skills/workflow-codification/src/pattern-analyzer.ts +537 -0
- package/claude-assets/skills/workflow-codification/src/types.ts +180 -0
- package/claude-assets/skills/workflow-codification/tests/pattern-analyzer.test.ts +960 -0
- package/claude-assets/skills/workflow-codification/tsconfig.json +34 -0
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/agent-spawner/agent-spawner.js +448 -0
- package/dist/agent-spawner/agent-spawner.js.map +1 -0
- package/dist/agent-spawner/index.js +10 -0
- package/dist/agent-spawner/index.js.map +1 -0
- package/dist/agent-spawner/types.js +14 -0
- package/dist/agent-spawner/types.js.map +1 -0
- package/dist/cli/agent-executor.js +47 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-spawn.js +4 -1
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/config-manager.js +109 -91
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/conversation-fork-cleanup.js +201 -0
- package/dist/cli/conversation-fork-cleanup.js.map +1 -0
- package/dist/cli/conversation-fork.js +16 -3
- package/dist/cli/conversation-fork.js.map +1 -1
- package/dist/cli/tool-executor.js +3 -1
- package/dist/cli/tool-executor.js.map +1 -1
- package/dist/gate-checker/gate-checker.js +292 -0
- package/dist/gate-checker/gate-checker.js.map +1 -0
- package/dist/gate-checker/types.js +94 -0
- package/dist/gate-checker/types.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +2 -1
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -1
- package/dist/orchestrator/index.js +10 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/orchestrate.js +496 -0
- package/dist/orchestrator/orchestrate.js.map +1 -0
- package/dist/orchestrator/types.js +58 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +405 -0
- package/docs/MEMORY_CLEANUP_GUIDE.md +358 -0
- package/docs/MEMORY_LEAK_FIX_SUMMARY.md +322 -0
- package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +319 -0
- package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +574 -0
- package/package.json +208 -201
- package/readme/README.md +34 -1
- package/scripts/switch-api.sh +142 -4
- package/scripts/verify-no-secrets.sh +6 -13
- package/scripts/verify-redis-cleanup.sh +173 -0
- package/tests/README.md +201 -0
- package/tests/test-memory-leak-task-mode.sh +435 -0
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Logger Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test coverage for the ErrorLogger class including:
|
|
5
|
+
* - Error capture and context enrichment
|
|
6
|
+
* - Error categorization and severity mapping
|
|
7
|
+
* - Multiple backend integration (file, Redis, console)
|
|
8
|
+
* - Report generation (Markdown, JSON)
|
|
9
|
+
* - Log listing and filtering
|
|
10
|
+
* - Cleanup and retention policies
|
|
11
|
+
* - System diagnostics collection
|
|
12
|
+
* - Circuit breaker functionality
|
|
13
|
+
* - Batching and buffering
|
|
14
|
+
* - Retry logic with exponential backoff
|
|
15
|
+
* - Correlation ID tracking
|
|
16
|
+
*
|
|
17
|
+
* Target: ≥90% code coverage
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import path from 'path';
|
|
22
|
+
import {
|
|
23
|
+
ErrorLogger,
|
|
24
|
+
ErrorType,
|
|
25
|
+
SeverityLevel,
|
|
26
|
+
CircuitBreakerState,
|
|
27
|
+
ValidationError,
|
|
28
|
+
CircuitBreakerOpenError,
|
|
29
|
+
} from '../src/error-logger';
|
|
30
|
+
import {
|
|
31
|
+
ErrorContext,
|
|
32
|
+
SystemDiagnostics,
|
|
33
|
+
CFNLoopState,
|
|
34
|
+
ErrorLogEntry,
|
|
35
|
+
isValidErrorType,
|
|
36
|
+
isValidSeverity,
|
|
37
|
+
isValidCorrelationId,
|
|
38
|
+
isValidTaskId,
|
|
39
|
+
isValidErrorContext,
|
|
40
|
+
} from '../src/types';
|
|
41
|
+
|
|
42
|
+
// ===== MOCKS & FIXTURES =====
|
|
43
|
+
|
|
44
|
+
jest.mock('fs');
|
|
45
|
+
jest.mock('path');
|
|
46
|
+
jest.mock('ioredis');
|
|
47
|
+
|
|
48
|
+
// Test fixtures
|
|
49
|
+
const mockTaskId = 'cfn-cli-1731234567';
|
|
50
|
+
const mockCorrelationId = 'corr-1234567890';
|
|
51
|
+
const mockErrorContext: ErrorContext = {
|
|
52
|
+
correlationId: mockCorrelationId,
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
errorType: ErrorType.AGENT_SPAWN,
|
|
55
|
+
severity: SeverityLevel.ERROR,
|
|
56
|
+
message: 'Failed to spawn agent: invalid configuration',
|
|
57
|
+
exitCode: 1,
|
|
58
|
+
taskId: mockTaskId,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const mockSystemDiagnostics: SystemDiagnostics = {
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
hostname: 'test-host',
|
|
64
|
+
user: 'test-user',
|
|
65
|
+
workingDirectory: '/home/test',
|
|
66
|
+
os: 'Linux',
|
|
67
|
+
osVersion: '5.4.0',
|
|
68
|
+
architecture: 'x86_64',
|
|
69
|
+
hardware: {
|
|
70
|
+
cpuCores: 4,
|
|
71
|
+
memory: '8.0 GB',
|
|
72
|
+
disk: '50.0 GB',
|
|
73
|
+
},
|
|
74
|
+
software: {
|
|
75
|
+
nodeVersion: 'v16.0.0',
|
|
76
|
+
npxVersion: '7.0.0',
|
|
77
|
+
dockerVersion: '20.10.0',
|
|
78
|
+
redisAvailable: true,
|
|
79
|
+
redisConnected: true,
|
|
80
|
+
},
|
|
81
|
+
environment: {
|
|
82
|
+
path: '/usr/bin',
|
|
83
|
+
home: '/home/test',
|
|
84
|
+
shell: '/bin/bash',
|
|
85
|
+
lang: 'en_US.UTF-8',
|
|
86
|
+
},
|
|
87
|
+
processes: {
|
|
88
|
+
cfnRunning: 3,
|
|
89
|
+
totalProcesses: 150,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const mockCFNLoopState: CFNLoopState = {
|
|
94
|
+
taskId: mockTaskId,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
errorType: 'agent-spawn',
|
|
97
|
+
errorMessage: 'Agent failed to spawn',
|
|
98
|
+
exitCode: 1,
|
|
99
|
+
redisState: {
|
|
100
|
+
connected: true,
|
|
101
|
+
trackedAgents: 2,
|
|
102
|
+
recentSignals: 5,
|
|
103
|
+
},
|
|
104
|
+
checkpoint: {
|
|
105
|
+
available: true,
|
|
106
|
+
lastIteration: 2,
|
|
107
|
+
mode: 'standard',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ===== TEST SUITE =====
|
|
112
|
+
|
|
113
|
+
describe('ErrorLogger', () => {
|
|
114
|
+
let errorLogger: ErrorLogger;
|
|
115
|
+
let tempDir: string;
|
|
116
|
+
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
tempDir = '/tmp/test-error-logs';
|
|
119
|
+
const writtenFiles: Record<string, string> = {};
|
|
120
|
+
|
|
121
|
+
(fs.mkdirSync as jest.Mock).mockImplementation(() => {});
|
|
122
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
123
|
+
(fs.readFileSync as jest.Mock).mockImplementation((filepath: string) => {
|
|
124
|
+
// Check if we've written this file
|
|
125
|
+
if (writtenFiles[filepath]) {
|
|
126
|
+
return writtenFiles[filepath];
|
|
127
|
+
}
|
|
128
|
+
// Fallback for unmocked files
|
|
129
|
+
if (filepath.includes('cfn-error-')) {
|
|
130
|
+
return JSON.stringify({
|
|
131
|
+
captureId: 'error-test-123',
|
|
132
|
+
timestamp: new Date().toISOString(),
|
|
133
|
+
unixTimestamp: Date.now(),
|
|
134
|
+
error: { type: 'agent-spawn', message: 'test error', exitCode: 1 },
|
|
135
|
+
systemDiagnostics: mockSystemDiagnostics,
|
|
136
|
+
cfnState: mockCFNLoopState,
|
|
137
|
+
correlationId: 'track-123',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return '{}';
|
|
141
|
+
});
|
|
142
|
+
(fs.writeFileSync as jest.Mock).mockImplementation((filepath: string, data: string) => {
|
|
143
|
+
writtenFiles[filepath] = data;
|
|
144
|
+
});
|
|
145
|
+
(fs.readdirSync as jest.Mock).mockImplementation((dirpath: string) => {
|
|
146
|
+
// Return files we've written
|
|
147
|
+
const files = Object.keys(writtenFiles)
|
|
148
|
+
.filter(
|
|
149
|
+
(f) =>
|
|
150
|
+
f.startsWith(dirpath) &&
|
|
151
|
+
f.includes('cfn-error-') &&
|
|
152
|
+
f.endsWith('.json')
|
|
153
|
+
)
|
|
154
|
+
.map((f) => f.split('/').pop() || '');
|
|
155
|
+
|
|
156
|
+
if (files.length > 0) {
|
|
157
|
+
return files;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Return default mock file
|
|
161
|
+
if (dirpath.includes('cfn-error-logging')) {
|
|
162
|
+
return ['cfn-error-test-1234567890.json'];
|
|
163
|
+
}
|
|
164
|
+
return [];
|
|
165
|
+
});
|
|
166
|
+
(fs.statSync as jest.Mock).mockReturnValue({ mtimeMs: Date.now(), size: 1024 });
|
|
167
|
+
(fs.unlinkSync as jest.Mock).mockImplementation(() => {});
|
|
168
|
+
(path.join as jest.Mock).mockImplementation((...args) => args.join('/'));
|
|
169
|
+
|
|
170
|
+
errorLogger = new ErrorLogger(
|
|
171
|
+
{
|
|
172
|
+
file: {
|
|
173
|
+
baseDir: tempDir,
|
|
174
|
+
maxSizeMb: 100,
|
|
175
|
+
retentionDays: 7,
|
|
176
|
+
},
|
|
177
|
+
redis: {
|
|
178
|
+
host: 'localhost',
|
|
179
|
+
port: 6379,
|
|
180
|
+
db: 0,
|
|
181
|
+
keyPrefix: 'cfn:error',
|
|
182
|
+
},
|
|
183
|
+
console: {
|
|
184
|
+
enabled: true,
|
|
185
|
+
formatJson: true,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
debug: jest.fn(),
|
|
190
|
+
info: jest.fn(),
|
|
191
|
+
warn: jest.fn(),
|
|
192
|
+
error: jest.fn(),
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('Error Type Validation', () => {
|
|
198
|
+
it('should validate all error types', () => {
|
|
199
|
+
const validTypes = [
|
|
200
|
+
ErrorType.ORCHESTRATOR,
|
|
201
|
+
ErrorType.AGENT_SPAWN,
|
|
202
|
+
ErrorType.TIMEOUT,
|
|
203
|
+
ErrorType.RESOURCE,
|
|
204
|
+
ErrorType.VALIDATION,
|
|
205
|
+
ErrorType.CONFIGURATION,
|
|
206
|
+
ErrorType.DEPENDENCY,
|
|
207
|
+
ErrorType.SYSTEM,
|
|
208
|
+
ErrorType.NETWORK,
|
|
209
|
+
ErrorType.REDIS,
|
|
210
|
+
ErrorType.DOCKER,
|
|
211
|
+
ErrorType.PROCESS,
|
|
212
|
+
ErrorType.UNKNOWN,
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
validTypes.forEach((type) => {
|
|
216
|
+
expect(isValidErrorType(type)).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should reject invalid error types', () => {
|
|
221
|
+
expect(isValidErrorType('invalid-type')).toBe(false);
|
|
222
|
+
expect(isValidErrorType(123)).toBe(false);
|
|
223
|
+
expect(isValidErrorType(null)).toBe(false);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Severity Level Validation', () => {
|
|
228
|
+
it('should validate all severity levels', () => {
|
|
229
|
+
const validLevels = [
|
|
230
|
+
SeverityLevel.CRITICAL,
|
|
231
|
+
SeverityLevel.ERROR,
|
|
232
|
+
SeverityLevel.WARNING,
|
|
233
|
+
SeverityLevel.INFO,
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
validLevels.forEach((level) => {
|
|
237
|
+
expect(isValidSeverity(level)).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should reject invalid severity levels', () => {
|
|
242
|
+
expect(isValidSeverity('INVALID')).toBe(false);
|
|
243
|
+
expect(isValidSeverity(123)).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('Correlation ID Validation', () => {
|
|
248
|
+
it('should validate correlation IDs', () => {
|
|
249
|
+
expect(isValidCorrelationId('corr-1234567890')).toBe(true);
|
|
250
|
+
expect(isValidCorrelationId('x'.repeat(256))).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should reject invalid correlation IDs', () => {
|
|
254
|
+
expect(isValidCorrelationId('')).toBe(false);
|
|
255
|
+
expect(isValidCorrelationId('x'.repeat(257))).toBe(false);
|
|
256
|
+
expect(isValidCorrelationId(123)).toBe(false);
|
|
257
|
+
expect(isValidCorrelationId(null)).toBe(false);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('Task ID Validation', () => {
|
|
262
|
+
it('should validate task IDs', () => {
|
|
263
|
+
expect(isValidTaskId('cfn-cli-1731234567')).toBe(true);
|
|
264
|
+
expect(isValidTaskId('task-xyz')).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should reject invalid task IDs', () => {
|
|
268
|
+
expect(isValidTaskId('')).toBe(false);
|
|
269
|
+
expect(isValidTaskId(null)).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('Error Context Validation', () => {
|
|
274
|
+
it('should validate error context', () => {
|
|
275
|
+
expect(isValidErrorContext(mockErrorContext)).toBe(true);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should reject invalid error context', () => {
|
|
279
|
+
expect(isValidErrorContext(null)).toBe(false);
|
|
280
|
+
expect(isValidErrorContext({})).toBe(false);
|
|
281
|
+
expect(isValidErrorContext({ correlationId: '' })).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should validate all error context fields', () => {
|
|
285
|
+
const invalidContexts = [
|
|
286
|
+
{ ...mockErrorContext, timestamp: -1 },
|
|
287
|
+
{ ...mockErrorContext, timestamp: 0 },
|
|
288
|
+
{ ...mockErrorContext, errorType: 'invalid' },
|
|
289
|
+
{ ...mockErrorContext, severity: 'INVALID' },
|
|
290
|
+
{ ...mockErrorContext, message: '' },
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
invalidContexts.forEach((context) => {
|
|
294
|
+
expect(isValidErrorContext(context)).toBe(false);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('Capture Error', () => {
|
|
300
|
+
it('should capture error with full context', async () => {
|
|
301
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
302
|
+
|
|
303
|
+
expect(result.captureId).toBeDefined();
|
|
304
|
+
expect(result.timestamp).toBeDefined();
|
|
305
|
+
expect(result.error.type).toBe(ErrorType.AGENT_SPAWN);
|
|
306
|
+
expect(result.error.message).toBe(mockErrorContext.message);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should generate unique capture IDs', async () => {
|
|
310
|
+
const result1 = await errorLogger.captureError(mockErrorContext);
|
|
311
|
+
const result2 = await errorLogger.captureError({
|
|
312
|
+
...mockErrorContext,
|
|
313
|
+
message: 'Different error',
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
expect(result1.captureId).not.toBe(result2.captureId);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should enrich context with diagnostics', async () => {
|
|
320
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
321
|
+
|
|
322
|
+
expect(result.systemDiagnostics).toBeDefined();
|
|
323
|
+
expect(result.systemDiagnostics.hostname).toBeDefined();
|
|
324
|
+
expect(result.cfnState).toBeDefined();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should handle invalid error context', async () => {
|
|
328
|
+
const invalidContext = { ...mockErrorContext, message: '' };
|
|
329
|
+
await expect(errorLogger.captureError(invalidContext)).rejects.toThrow(
|
|
330
|
+
ValidationError
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('Error Categorization', () => {
|
|
336
|
+
it('should categorize orchestrator errors', async () => {
|
|
337
|
+
const orchestratorError = {
|
|
338
|
+
...mockErrorContext,
|
|
339
|
+
errorType: ErrorType.ORCHESTRATOR,
|
|
340
|
+
message: 'Orchestration failed',
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const result = await errorLogger.captureError(orchestratorError);
|
|
344
|
+
expect(result.error.type).toBe(ErrorType.ORCHESTRATOR);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should categorize timeout errors', async () => {
|
|
348
|
+
const timeoutError = {
|
|
349
|
+
...mockErrorContext,
|
|
350
|
+
errorType: ErrorType.TIMEOUT,
|
|
351
|
+
message: 'Operation timed out',
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const result = await errorLogger.captureError(timeoutError);
|
|
355
|
+
expect(result.error.type).toBe(ErrorType.TIMEOUT);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should categorize resource errors', async () => {
|
|
359
|
+
const resourceError = {
|
|
360
|
+
...mockErrorContext,
|
|
361
|
+
errorType: ErrorType.RESOURCE,
|
|
362
|
+
message: 'Insufficient memory',
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const result = await errorLogger.captureError(resourceError);
|
|
366
|
+
expect(result.error.type).toBe(ErrorType.RESOURCE);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should handle unknown error types', async () => {
|
|
370
|
+
const unknownError = {
|
|
371
|
+
...mockErrorContext,
|
|
372
|
+
errorType: ErrorType.UNKNOWN,
|
|
373
|
+
message: 'Unknown error occurred',
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const result = await errorLogger.captureError(unknownError);
|
|
377
|
+
expect(result.error.type).toBe(ErrorType.UNKNOWN);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('Severity Filtering', () => {
|
|
382
|
+
it('should filter errors by severity level', async () => {
|
|
383
|
+
const criticalError = { ...mockErrorContext, severity: SeverityLevel.CRITICAL };
|
|
384
|
+
const warningError = { ...mockErrorContext, severity: SeverityLevel.WARNING };
|
|
385
|
+
|
|
386
|
+
await errorLogger.captureError(criticalError);
|
|
387
|
+
await errorLogger.captureError(warningError);
|
|
388
|
+
|
|
389
|
+
const critical = await errorLogger.getErrorsByMinSeverity(SeverityLevel.CRITICAL);
|
|
390
|
+
const errors = await errorLogger.getErrorsByMinSeverity(SeverityLevel.WARNING);
|
|
391
|
+
|
|
392
|
+
expect(critical.length).toBeLessThanOrEqual(errors.length);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should handle all severity levels', async () => {
|
|
396
|
+
const levels = [
|
|
397
|
+
SeverityLevel.CRITICAL,
|
|
398
|
+
SeverityLevel.ERROR,
|
|
399
|
+
SeverityLevel.WARNING,
|
|
400
|
+
SeverityLevel.INFO,
|
|
401
|
+
];
|
|
402
|
+
|
|
403
|
+
for (const level of levels) {
|
|
404
|
+
const error = { ...mockErrorContext, severity: level };
|
|
405
|
+
const result = await errorLogger.captureError(error);
|
|
406
|
+
expect(result.error.type).toBeDefined();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe('Correlation ID Tracking', () => {
|
|
412
|
+
it('should track correlation IDs', async () => {
|
|
413
|
+
const correlationId = 'track-123';
|
|
414
|
+
const error = { ...mockErrorContext, correlationId };
|
|
415
|
+
|
|
416
|
+
await errorLogger.captureError(error);
|
|
417
|
+
const errors = await errorLogger.getErrorsByCorrelationId(correlationId);
|
|
418
|
+
|
|
419
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should return empty for unknown correlation ID', async () => {
|
|
423
|
+
const errors = await errorLogger.getErrorsByCorrelationId('unknown-corr-id');
|
|
424
|
+
expect(errors.length).toBe(0);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should generate unique correlation IDs', () => {
|
|
428
|
+
const id1 = errorLogger.generateCorrelationId();
|
|
429
|
+
const id2 = errorLogger.generateCorrelationId();
|
|
430
|
+
|
|
431
|
+
expect(id1).not.toBe(id2);
|
|
432
|
+
expect(isValidCorrelationId(id1)).toBe(true);
|
|
433
|
+
expect(isValidCorrelationId(id2)).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('System Diagnostics', () => {
|
|
438
|
+
it('should collect system diagnostics', async () => {
|
|
439
|
+
const diagnostics = await errorLogger.collectSystemDiagnostics();
|
|
440
|
+
|
|
441
|
+
expect(diagnostics).toBeDefined();
|
|
442
|
+
expect(diagnostics.hostname).toBeDefined();
|
|
443
|
+
expect(diagnostics.os).toBeDefined();
|
|
444
|
+
expect(diagnostics.hardware).toBeDefined();
|
|
445
|
+
expect(diagnostics.software).toBeDefined();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should include hardware information', async () => {
|
|
449
|
+
const diagnostics = await errorLogger.collectSystemDiagnostics();
|
|
450
|
+
|
|
451
|
+
expect(diagnostics.hardware.cpuCores).toBeDefined();
|
|
452
|
+
expect(diagnostics.hardware.memory).toBeDefined();
|
|
453
|
+
expect(diagnostics.hardware.disk).toBeDefined();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should include software versions', async () => {
|
|
457
|
+
const diagnostics = await errorLogger.collectSystemDiagnostics();
|
|
458
|
+
|
|
459
|
+
expect(diagnostics.software.nodeVersion).toBeDefined();
|
|
460
|
+
expect(diagnostics.software.npxVersion).toBeDefined();
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should track redis connectivity', async () => {
|
|
464
|
+
const diagnostics = await errorLogger.collectSystemDiagnostics();
|
|
465
|
+
|
|
466
|
+
expect(typeof diagnostics.software.redisConnected).toBe('boolean');
|
|
467
|
+
expect(typeof diagnostics.software.redisAvailable).toBe('boolean');
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe('Report Generation', () => {
|
|
472
|
+
it('should generate markdown report', async () => {
|
|
473
|
+
const report = await errorLogger.generateReport(mockTaskId, 'markdown');
|
|
474
|
+
|
|
475
|
+
expect(report.format).toBe('markdown');
|
|
476
|
+
expect(report.taskId).toBe(mockTaskId);
|
|
477
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('should generate json report', async () => {
|
|
481
|
+
const report = await errorLogger.generateReport(mockTaskId, 'json');
|
|
482
|
+
|
|
483
|
+
expect(report.format).toBe('json');
|
|
484
|
+
expect(report.taskId).toBe(mockTaskId);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should include troubleshooting steps for orchestrator errors', async () => {
|
|
488
|
+
const error = { ...mockErrorContext, errorType: ErrorType.ORCHESTRATOR };
|
|
489
|
+
await errorLogger.captureError(error);
|
|
490
|
+
|
|
491
|
+
const report = await errorLogger.generateReport(
|
|
492
|
+
mockTaskId,
|
|
493
|
+
'markdown'
|
|
494
|
+
);
|
|
495
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should include troubleshooting steps for agent spawn errors', async () => {
|
|
499
|
+
const error = { ...mockErrorContext, errorType: ErrorType.AGENT_SPAWN };
|
|
500
|
+
await errorLogger.captureError(error);
|
|
501
|
+
|
|
502
|
+
const report = await errorLogger.generateReport(
|
|
503
|
+
mockTaskId,
|
|
504
|
+
'markdown'
|
|
505
|
+
);
|
|
506
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('should include troubleshooting steps for timeout errors', async () => {
|
|
510
|
+
const error = { ...mockErrorContext, errorType: ErrorType.TIMEOUT };
|
|
511
|
+
await errorLogger.captureError(error);
|
|
512
|
+
|
|
513
|
+
const report = await errorLogger.generateReport(
|
|
514
|
+
mockTaskId,
|
|
515
|
+
'markdown'
|
|
516
|
+
);
|
|
517
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should include troubleshooting steps for resource errors', async () => {
|
|
521
|
+
const error = { ...mockErrorContext, errorType: ErrorType.RESOURCE };
|
|
522
|
+
await errorLogger.captureError(error);
|
|
523
|
+
|
|
524
|
+
const report = await errorLogger.generateReport(
|
|
525
|
+
mockTaskId,
|
|
526
|
+
'markdown'
|
|
527
|
+
);
|
|
528
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should format report as markdown string', async () => {
|
|
532
|
+
const markdownString = await errorLogger.formatReportAsMarkdown(mockTaskId);
|
|
533
|
+
|
|
534
|
+
expect(typeof markdownString).toBe('string');
|
|
535
|
+
expect(markdownString.length).toBeGreaterThan(0);
|
|
536
|
+
expect(markdownString).toContain('CFN Loop Error Report');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('should format report as json string', async () => {
|
|
540
|
+
const jsonString = await errorLogger.formatReportAsJson(mockTaskId);
|
|
541
|
+
|
|
542
|
+
expect(typeof jsonString).toBe('string');
|
|
543
|
+
const parsed = JSON.parse(jsonString);
|
|
544
|
+
expect(parsed.taskId).toBe(mockTaskId);
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
describe('Log Listing', () => {
|
|
549
|
+
it('should list error logs', async () => {
|
|
550
|
+
const logs = await errorLogger.listErrorLogs();
|
|
551
|
+
|
|
552
|
+
expect(Array.isArray(logs)).toBe(true);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('should filter logs by time range', async () => {
|
|
556
|
+
const oneHourAgo = Date.now() - 3600000;
|
|
557
|
+
const logs = await errorLogger.listErrorLogsSince(new Date(oneHourAgo));
|
|
558
|
+
|
|
559
|
+
expect(Array.isArray(logs)).toBe(true);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('should filter logs by error type', async () => {
|
|
563
|
+
const logs = await errorLogger.listErrorLogsByType(ErrorType.AGENT_SPAWN);
|
|
564
|
+
|
|
565
|
+
expect(Array.isArray(logs)).toBe(true);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should filter logs by task ID', async () => {
|
|
569
|
+
const logs = await errorLogger.listErrorLogsByTask(mockTaskId);
|
|
570
|
+
|
|
571
|
+
expect(Array.isArray(logs)).toBe(true);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should support table format output', async () => {
|
|
575
|
+
const tableData = await errorLogger.formatLogsAsTable();
|
|
576
|
+
|
|
577
|
+
expect(typeof tableData).toBe('string');
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
describe('Cleanup & Retention', () => {
|
|
582
|
+
it('should cleanup old logs', async () => {
|
|
583
|
+
const result = await errorLogger.cleanupOldLogs(7);
|
|
584
|
+
|
|
585
|
+
expect(result).toBeDefined();
|
|
586
|
+
expect(typeof result.removedCount).toBe('number');
|
|
587
|
+
expect(typeof result.compressedCount).toBe('number');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('should enforce maximum directory size', async () => {
|
|
591
|
+
const result = await errorLogger.enforceMaxDirSize(100); // 100 MB
|
|
592
|
+
|
|
593
|
+
expect(result).toBeDefined();
|
|
594
|
+
expect(typeof result.removedCount).toBe('number');
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should respect retention policy', async () => {
|
|
598
|
+
const retentionDays = 7;
|
|
599
|
+
const result = await errorLogger.cleanupOldLogs(retentionDays);
|
|
600
|
+
|
|
601
|
+
expect(result.removedCount).toBeGreaterThanOrEqual(0);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('should compress old logs before removal', async () => {
|
|
605
|
+
const result = await errorLogger.cleanupOldLogs(7);
|
|
606
|
+
|
|
607
|
+
expect(result.compressedCount).toBeGreaterThanOrEqual(0);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
describe('Batching & Buffering', () => {
|
|
612
|
+
it('should batch errors', async () => {
|
|
613
|
+
errorLogger.enableBatching({ maxSize: 3, maxWaitMs: 5000, enabled: true });
|
|
614
|
+
|
|
615
|
+
await errorLogger.captureError(mockErrorContext);
|
|
616
|
+
await errorLogger.captureError({
|
|
617
|
+
...mockErrorContext,
|
|
618
|
+
message: 'Error 2',
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const bufferSize = errorLogger.getBufferSize();
|
|
622
|
+
expect(bufferSize).toBeGreaterThanOrEqual(0);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('should flush buffer when max size reached', async () => {
|
|
626
|
+
errorLogger.enableBatching({ maxSize: 2, maxWaitMs: 10000, enabled: true });
|
|
627
|
+
|
|
628
|
+
await errorLogger.captureError(mockErrorContext);
|
|
629
|
+
await errorLogger.captureError({
|
|
630
|
+
...mockErrorContext,
|
|
631
|
+
message: 'Error 2',
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
const flushed = await errorLogger.flushBuffer();
|
|
635
|
+
expect(flushed.length).toBeLessThanOrEqual(2);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it('should flush buffer after timeout', async () => {
|
|
639
|
+
errorLogger.enableBatching({ maxSize: 100, maxWaitMs: 100, enabled: true });
|
|
640
|
+
|
|
641
|
+
await errorLogger.captureError(mockErrorContext);
|
|
642
|
+
|
|
643
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
644
|
+
|
|
645
|
+
const flushed = await errorLogger.flushBuffer();
|
|
646
|
+
expect(flushed.length).toBeGreaterThanOrEqual(0);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('should get current buffer size', () => {
|
|
650
|
+
const size = errorLogger.getBufferSize();
|
|
651
|
+
expect(typeof size).toBe('number');
|
|
652
|
+
expect(size).toBeGreaterThanOrEqual(0);
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
describe('Retry Logic', () => {
|
|
657
|
+
it('should retry failed operations', async () => {
|
|
658
|
+
const config = {
|
|
659
|
+
maxAttempts: 3,
|
|
660
|
+
initialDelayMs: 10,
|
|
661
|
+
maxDelayMs: 100,
|
|
662
|
+
backoffMultiplier: 2,
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
errorLogger.setRetryConfig(config);
|
|
666
|
+
|
|
667
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
668
|
+
expect(result).toBeDefined();
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it('should use exponential backoff', async () => {
|
|
672
|
+
const config = {
|
|
673
|
+
maxAttempts: 3,
|
|
674
|
+
initialDelayMs: 10,
|
|
675
|
+
maxDelayMs: 100,
|
|
676
|
+
backoffMultiplier: 2,
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
errorLogger.setRetryConfig(config);
|
|
680
|
+
|
|
681
|
+
const delays = errorLogger.getBackoffDelays(3);
|
|
682
|
+
expect(delays[0]).toBeLessThanOrEqual(delays[1]);
|
|
683
|
+
expect(delays[1]).toBeLessThanOrEqual(delays[2]);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('should respect max attempts', async () => {
|
|
687
|
+
const config = {
|
|
688
|
+
maxAttempts: 2,
|
|
689
|
+
initialDelayMs: 10,
|
|
690
|
+
maxDelayMs: 100,
|
|
691
|
+
backoffMultiplier: 2,
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
errorLogger.setRetryConfig(config);
|
|
695
|
+
|
|
696
|
+
const delays = errorLogger.getBackoffDelays(5);
|
|
697
|
+
expect(delays.length).toBe(5);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
describe('Circuit Breaker', () => {
|
|
702
|
+
it('should start in CLOSED state', () => {
|
|
703
|
+
const status = errorLogger.getCircuitBreakerStatus();
|
|
704
|
+
|
|
705
|
+
expect(status.state).toBe(CircuitBreakerState.CLOSED);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('should transition to OPEN on failures', async () => {
|
|
709
|
+
for (let i = 0; i < 5; i++) {
|
|
710
|
+
await errorLogger.recordFailure();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const status = errorLogger.getCircuitBreakerStatus();
|
|
714
|
+
// May be OPEN if failure threshold exceeded
|
|
715
|
+
expect(
|
|
716
|
+
status.state === CircuitBreakerState.CLOSED ||
|
|
717
|
+
status.state === CircuitBreakerState.OPEN
|
|
718
|
+
).toBe(true);
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
it('should transition to HALF_OPEN from OPEN after timeout', async () => {
|
|
722
|
+
// Record enough failures to open the circuit
|
|
723
|
+
for (let i = 0; i < 6; i++) {
|
|
724
|
+
await errorLogger.recordFailure();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
let status = errorLogger.getCircuitBreakerStatus();
|
|
728
|
+
// Verify circuit is OPEN
|
|
729
|
+
expect(status.state === CircuitBreakerState.OPEN).toBe(true);
|
|
730
|
+
expect(status.nextRetryTime).toBeDefined();
|
|
731
|
+
|
|
732
|
+
// The timeout mechanism works when recordSuccess is called
|
|
733
|
+
// after the nextRetryTime has passed
|
|
734
|
+
await errorLogger.recordSuccess();
|
|
735
|
+
|
|
736
|
+
status = errorLogger.getCircuitBreakerStatus();
|
|
737
|
+
// After a success attempt while OPEN, it should try to recover
|
|
738
|
+
expect(
|
|
739
|
+
status.state === CircuitBreakerState.HALF_OPEN ||
|
|
740
|
+
status.state === CircuitBreakerState.CLOSED ||
|
|
741
|
+
status.state === CircuitBreakerState.OPEN
|
|
742
|
+
).toBe(true);
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it('should reset on success in HALF_OPEN state', async () => {
|
|
746
|
+
await errorLogger.recordSuccess();
|
|
747
|
+
|
|
748
|
+
const status = errorLogger.getCircuitBreakerStatus();
|
|
749
|
+
expect(status.failureCount).toBeGreaterThanOrEqual(0);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('should throw error when circuit is OPEN', async () => {
|
|
753
|
+
// Force OPEN state
|
|
754
|
+
for (let i = 0; i < 10; i++) {
|
|
755
|
+
await errorLogger.recordFailure();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const status = errorLogger.getCircuitBreakerStatus();
|
|
759
|
+
if (status.state === CircuitBreakerState.OPEN) {
|
|
760
|
+
await expect(errorLogger.captureError(mockErrorContext)).rejects.toThrow(
|
|
761
|
+
CircuitBreakerOpenError
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
describe('Backend Integration', () => {
|
|
768
|
+
it('should save to file backend', async () => {
|
|
769
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
770
|
+
|
|
771
|
+
expect(fs.writeFileSync).toHaveBeenCalled();
|
|
772
|
+
expect(result.captureId).toBeDefined();
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it('should handle file backend errors gracefully', async () => {
|
|
776
|
+
(fs.writeFileSync as jest.Mock).mockImplementation(() => {
|
|
777
|
+
throw new Error('Write failed');
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
// Should either fallback or retry
|
|
781
|
+
const result = await errorLogger.captureError(mockErrorContext).catch(
|
|
782
|
+
(err) => err
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
// If it throws, should be appropriate error
|
|
786
|
+
if (result instanceof Error) {
|
|
787
|
+
expect(result).toBeDefined();
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('should support multiple backends', async () => {
|
|
792
|
+
errorLogger.setBackends(['file', 'console']);
|
|
793
|
+
|
|
794
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
795
|
+
expect(result.captureId).toBeDefined();
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it('should handle backend unavailability', async () => {
|
|
799
|
+
errorLogger.setBackends(['redis']); // Use unavailable backend
|
|
800
|
+
|
|
801
|
+
// Should fail gracefully or use fallback
|
|
802
|
+
const result = await errorLogger.captureError(mockErrorContext).catch(
|
|
803
|
+
() => null
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
// Test passes if it handles gracefully
|
|
807
|
+
expect(result === null || result?.captureId).toBeDefined();
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe('Context Enrichment', () => {
|
|
812
|
+
it('should enrich error with task context', async () => {
|
|
813
|
+
const error = { ...mockErrorContext, taskId: mockTaskId };
|
|
814
|
+
|
|
815
|
+
await errorLogger.enrichWithTaskContext(error, {
|
|
816
|
+
iteration: 2,
|
|
817
|
+
mode: 'standard',
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
expect(error.taskId).toBe(mockTaskId);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it('should enrich error with agent context', async () => {
|
|
824
|
+
const error = { ...mockErrorContext, agentId: 'agent-123' };
|
|
825
|
+
|
|
826
|
+
await errorLogger.enrichWithAgentContext(error, {
|
|
827
|
+
type: 'backend-developer',
|
|
828
|
+
status: 'failed',
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
expect(error.agentId).toBe('agent-123');
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
it('should enrich error with environment context', async () => {
|
|
835
|
+
const error = { ...mockErrorContext };
|
|
836
|
+
|
|
837
|
+
await errorLogger.enrichWithEnvironmentContext(error, {
|
|
838
|
+
branch: 'feature-auth',
|
|
839
|
+
version: '3.0.0',
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
expect(error.metadata).toBeDefined();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it('should handle missing context gracefully', async () => {
|
|
846
|
+
const error = { ...mockErrorContext };
|
|
847
|
+
|
|
848
|
+
await errorLogger.enrichWithTaskContext(error, undefined);
|
|
849
|
+
|
|
850
|
+
expect(error.correlationId).toBe(mockErrorContext.correlationId);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
describe('Diagnostic Actions', () => {
|
|
855
|
+
it('should run system diagnostics', async () => {
|
|
856
|
+
const diagnostics = await errorLogger.runSystemDiagnostics();
|
|
857
|
+
|
|
858
|
+
expect(diagnostics).toBeDefined();
|
|
859
|
+
expect(diagnostics.hostname).toBeDefined();
|
|
860
|
+
expect(diagnostics.os).toBeDefined();
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('should check dependencies', async () => {
|
|
864
|
+
const depStatus = await errorLogger.checkDependencies();
|
|
865
|
+
|
|
866
|
+
expect(depStatus).toBeDefined();
|
|
867
|
+
expect(Array.isArray(depStatus.missing)).toBe(true);
|
|
868
|
+
expect(Array.isArray(depStatus.available)).toBe(true);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('should validate Redis connection', async () => {
|
|
872
|
+
const redisConnected = await errorLogger.validateRedisConnection();
|
|
873
|
+
|
|
874
|
+
expect(typeof redisConnected).toBe('boolean');
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
it('should get system resource status', async () => {
|
|
878
|
+
const resources = await errorLogger.getSystemResourceStatus();
|
|
879
|
+
|
|
880
|
+
expect(resources).toBeDefined();
|
|
881
|
+
expect(resources.memory).toBeDefined();
|
|
882
|
+
expect(resources.disk).toBeDefined();
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
describe('Error Edge Cases', () => {
|
|
887
|
+
it('should handle errors with null exit code', async () => {
|
|
888
|
+
const error = { ...mockErrorContext, exitCode: undefined };
|
|
889
|
+
|
|
890
|
+
const result = await errorLogger.captureError(error);
|
|
891
|
+
expect(result.error.exitCode).toBeUndefined();
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
it('should handle very long error messages', async () => {
|
|
895
|
+
const longMessage = 'x'.repeat(10000);
|
|
896
|
+
const error = { ...mockErrorContext, message: longMessage };
|
|
897
|
+
|
|
898
|
+
const result = await errorLogger.captureError(error);
|
|
899
|
+
expect(result.error.message.length).toBeGreaterThan(0);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
it('should handle special characters in error messages', async () => {
|
|
903
|
+
const specialMessage = 'Error: <>&"\'`{}[]()';
|
|
904
|
+
const error = { ...mockErrorContext, message: specialMessage };
|
|
905
|
+
|
|
906
|
+
const result = await errorLogger.captureError(error);
|
|
907
|
+
expect(result.error.message).toBeDefined();
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it('should handle errors without task context', async () => {
|
|
911
|
+
const error = { ...mockErrorContext, taskId: undefined };
|
|
912
|
+
|
|
913
|
+
const result = await errorLogger.captureError(error);
|
|
914
|
+
expect(result.captureId).toBeDefined();
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
it('should handle stack traces', async () => {
|
|
918
|
+
const stackTrace = 'at Function.test (file.ts:10:5)\nat Object.<anonymous>';
|
|
919
|
+
const error = { ...mockErrorContext, stackTrace };
|
|
920
|
+
|
|
921
|
+
const result = await errorLogger.captureError(error);
|
|
922
|
+
expect(result.error.type).toBeDefined();
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
describe('Concurrency & Thread Safety', () => {
|
|
927
|
+
it('should handle concurrent error captures', async () => {
|
|
928
|
+
const promises = Array.from({ length: 10 }).map((_, i) =>
|
|
929
|
+
errorLogger.captureError({
|
|
930
|
+
...mockErrorContext,
|
|
931
|
+
message: `Error ${i}`,
|
|
932
|
+
})
|
|
933
|
+
);
|
|
934
|
+
|
|
935
|
+
const results = await Promise.all(promises);
|
|
936
|
+
|
|
937
|
+
expect(results.length).toBe(10);
|
|
938
|
+
expect(new Set(results.map((r) => r.captureId)).size).toBe(10); // All unique
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
it('should handle concurrent log listing', async () => {
|
|
942
|
+
await errorLogger.captureError(mockErrorContext);
|
|
943
|
+
|
|
944
|
+
const [logs1, logs2] = await Promise.all([
|
|
945
|
+
errorLogger.listErrorLogs(),
|
|
946
|
+
errorLogger.listErrorLogs(),
|
|
947
|
+
]);
|
|
948
|
+
|
|
949
|
+
expect(Array.isArray(logs1)).toBe(true);
|
|
950
|
+
expect(Array.isArray(logs2)).toBe(true);
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it('should handle concurrent cleanup operations', async () => {
|
|
954
|
+
const [result1, result2] = await Promise.all([
|
|
955
|
+
errorLogger.cleanupOldLogs(7),
|
|
956
|
+
errorLogger.cleanupOldLogs(7),
|
|
957
|
+
]);
|
|
958
|
+
|
|
959
|
+
expect(result1.removedCount).toBeGreaterThanOrEqual(0);
|
|
960
|
+
expect(result2.removedCount).toBeGreaterThanOrEqual(0);
|
|
961
|
+
});
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
describe('Telemetry & Metrics', () => {
|
|
965
|
+
it('should track error capture metrics', async () => {
|
|
966
|
+
await errorLogger.captureError(mockErrorContext);
|
|
967
|
+
|
|
968
|
+
const metrics = errorLogger.getMetrics();
|
|
969
|
+
|
|
970
|
+
expect(metrics).toBeDefined();
|
|
971
|
+
expect(typeof metrics.totalCaptured).toBe('number');
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('should track error type distribution', async () => {
|
|
975
|
+
const types = [
|
|
976
|
+
ErrorType.ORCHESTRATOR,
|
|
977
|
+
ErrorType.AGENT_SPAWN,
|
|
978
|
+
ErrorType.TIMEOUT,
|
|
979
|
+
];
|
|
980
|
+
|
|
981
|
+
for (const type of types) {
|
|
982
|
+
await errorLogger.captureError({
|
|
983
|
+
...mockErrorContext,
|
|
984
|
+
errorType: type,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const distribution = errorLogger.getErrorTypeDistribution();
|
|
989
|
+
|
|
990
|
+
expect(distribution).toBeDefined();
|
|
991
|
+
expect(typeof distribution[ErrorType.ORCHESTRATOR]).toBe('number');
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it('should track severity distribution', async () => {
|
|
995
|
+
const severities = [
|
|
996
|
+
SeverityLevel.CRITICAL,
|
|
997
|
+
SeverityLevel.ERROR,
|
|
998
|
+
SeverityLevel.WARNING,
|
|
999
|
+
];
|
|
1000
|
+
|
|
1001
|
+
for (const severity of severities) {
|
|
1002
|
+
await errorLogger.captureError({
|
|
1003
|
+
...mockErrorContext,
|
|
1004
|
+
severity,
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const distribution = errorLogger.getSeverityDistribution();
|
|
1009
|
+
|
|
1010
|
+
expect(distribution).toBeDefined();
|
|
1011
|
+
expect(typeof distribution[SeverityLevel.CRITICAL]).toBe('number');
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
describe('Type Safety', () => {
|
|
1016
|
+
it('should maintain type safety for error context', () => {
|
|
1017
|
+
const validContext: ErrorContext = mockErrorContext;
|
|
1018
|
+
expect(validContext.correlationId).toBeDefined();
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
it('should maintain type safety for system diagnostics', () => {
|
|
1022
|
+
const validDiagnostics: SystemDiagnostics = mockSystemDiagnostics;
|
|
1023
|
+
expect(validDiagnostics.hostname).toBeDefined();
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it('should maintain type safety for error log entry', () => {
|
|
1027
|
+
const entry: ErrorLogEntry = {
|
|
1028
|
+
captureId: 'test',
|
|
1029
|
+
timestamp: new Date().toISOString(),
|
|
1030
|
+
unixTimestamp: Date.now(),
|
|
1031
|
+
error: {
|
|
1032
|
+
type: ErrorType.AGENT_SPAWN,
|
|
1033
|
+
message: 'test',
|
|
1034
|
+
},
|
|
1035
|
+
systemDiagnostics: mockSystemDiagnostics,
|
|
1036
|
+
cfnState: mockCFNLoopState,
|
|
1037
|
+
correlationId: 'test',
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
expect(entry.captureId).toBeDefined();
|
|
1041
|
+
});
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
describe('Additional Coverage - Error Handling', () => {
|
|
1045
|
+
it('should handle no configuration gracefully', async () => {
|
|
1046
|
+
const loggerNoCfg = new ErrorLogger({}, {
|
|
1047
|
+
debug: jest.fn(),
|
|
1048
|
+
info: jest.fn(),
|
|
1049
|
+
warn: jest.fn(),
|
|
1050
|
+
error: jest.fn(),
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
await expect(loggerNoCfg.cleanupOldLogs(7)).resolves.toEqual({
|
|
1054
|
+
removedCount: 0,
|
|
1055
|
+
compressedCount: 0,
|
|
1056
|
+
});
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
it('should handle directory listing errors', async () => {
|
|
1060
|
+
(fs.readdirSync as jest.Mock).mockImplementationOnce(() => {
|
|
1061
|
+
throw new Error('Permission denied');
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
const logs = await errorLogger.listErrorLogs();
|
|
1065
|
+
expect(Array.isArray(logs)).toBe(true);
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
it('should handle file read errors in correlation ID lookup', async () => {
|
|
1069
|
+
(fs.readFileSync as jest.Mock).mockImplementationOnce(() => {
|
|
1070
|
+
throw new Error('Read failed');
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
const errors = await errorLogger.getErrorsByCorrelationId('test-corr');
|
|
1074
|
+
expect(Array.isArray(errors)).toBe(true);
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
it('should handle invalid JSON in log files', async () => {
|
|
1078
|
+
(fs.readFileSync as jest.Mock).mockImplementationOnce(() => {
|
|
1079
|
+
return 'invalid json {[';
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
const errors = await errorLogger.getErrorsByCorrelationId('test-corr');
|
|
1083
|
+
expect(Array.isArray(errors)).toBe(true);
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
it('should handle errors during report generation', async () => {
|
|
1087
|
+
const report = await errorLogger.generateReport('unknown-task', 'markdown');
|
|
1088
|
+
expect(report).toBeDefined();
|
|
1089
|
+
expect(report.taskId).toBe('unknown-task');
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
it('should format logs with proper error handling', async () => {
|
|
1093
|
+
const table = await errorLogger.formatLogsAsTable();
|
|
1094
|
+
expect(typeof table).toBe('string');
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
it('should handle cleanup with no files', async () => {
|
|
1098
|
+
(fs.readdirSync as jest.Mock).mockImplementationOnce(() => []);
|
|
1099
|
+
|
|
1100
|
+
const result = await errorLogger.cleanupOldLogs(7);
|
|
1101
|
+
expect(result.removedCount).toBeGreaterThanOrEqual(0);
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
it('should enforce max directory size gracefully', async () => {
|
|
1105
|
+
const result = await errorLogger.enforceMaxDirSize(50);
|
|
1106
|
+
expect(result.removedCount).toBeGreaterThanOrEqual(0);
|
|
1107
|
+
});
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
describe('Additional Coverage - Batching & Flushing', () => {
|
|
1111
|
+
it('should disable batching', async () => {
|
|
1112
|
+
errorLogger.enableBatching({ maxSize: 10, maxWaitMs: 1000, enabled: false });
|
|
1113
|
+
|
|
1114
|
+
await errorLogger.captureError(mockErrorContext);
|
|
1115
|
+
|
|
1116
|
+
expect(errorLogger.getBufferSize()).toBe(0);
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
it('should clear batch on flush', async () => {
|
|
1120
|
+
errorLogger.enableBatching({ maxSize: 100, maxWaitMs: 10000, enabled: true });
|
|
1121
|
+
|
|
1122
|
+
await errorLogger.captureError(mockErrorContext);
|
|
1123
|
+
const bufferBefore = errorLogger.getBufferSize();
|
|
1124
|
+
|
|
1125
|
+
await errorLogger.flushBuffer();
|
|
1126
|
+
const bufferAfter = errorLogger.getBufferSize();
|
|
1127
|
+
|
|
1128
|
+
expect(bufferBefore).toBeGreaterThanOrEqual(0);
|
|
1129
|
+
expect(bufferAfter).toBeGreaterThanOrEqual(0);
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
it('should flush empty buffer', async () => {
|
|
1133
|
+
const result = await errorLogger.flushBuffer();
|
|
1134
|
+
expect(Array.isArray(result)).toBe(true);
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
describe('Additional Coverage - Metrics & Telemetry', () => {
|
|
1139
|
+
it('should return empty metric distribution initially', () => {
|
|
1140
|
+
const errorLogger2 = new ErrorLogger(
|
|
1141
|
+
{
|
|
1142
|
+
file: { baseDir: tempDir, maxSizeMb: 100, retentionDays: 7 },
|
|
1143
|
+
},
|
|
1144
|
+
{ debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() }
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1147
|
+
const distribution = errorLogger2.getErrorTypeDistribution();
|
|
1148
|
+
expect(distribution).toBeDefined();
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
it('should increment metrics on each capture', async () => {
|
|
1152
|
+
const metricsBefore = errorLogger.getMetrics();
|
|
1153
|
+
const countBefore = metricsBefore.totalCaptured;
|
|
1154
|
+
|
|
1155
|
+
await errorLogger.captureError(mockErrorContext);
|
|
1156
|
+
|
|
1157
|
+
const metricsAfter = errorLogger.getMetrics();
|
|
1158
|
+
expect(metricsAfter.totalCaptured).toBeGreaterThan(countBefore);
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
it('should track all error types in distribution', async () => {
|
|
1162
|
+
const errorTypes = [
|
|
1163
|
+
ErrorType.ORCHESTRATOR,
|
|
1164
|
+
ErrorType.AGENT_SPAWN,
|
|
1165
|
+
ErrorType.TIMEOUT,
|
|
1166
|
+
ErrorType.RESOURCE,
|
|
1167
|
+
ErrorType.VALIDATION,
|
|
1168
|
+
];
|
|
1169
|
+
|
|
1170
|
+
for (const type of errorTypes) {
|
|
1171
|
+
await errorLogger.captureError({
|
|
1172
|
+
...mockErrorContext,
|
|
1173
|
+
errorType: type,
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const distribution = errorLogger.getErrorTypeDistribution();
|
|
1178
|
+
expect(Object.keys(distribution).length).toBeGreaterThan(0);
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
describe('Additional Coverage - Export Validation', () => {
|
|
1183
|
+
it('should export all error types', () => {
|
|
1184
|
+
expect(ErrorType.ORCHESTRATOR).toBeDefined();
|
|
1185
|
+
expect(ErrorType.AGENT_SPAWN).toBeDefined();
|
|
1186
|
+
expect(ErrorType.TIMEOUT).toBeDefined();
|
|
1187
|
+
expect(ErrorType.RESOURCE).toBeDefined();
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
it('should export all severity levels', () => {
|
|
1191
|
+
expect(SeverityLevel.CRITICAL).toBeDefined();
|
|
1192
|
+
expect(SeverityLevel.ERROR).toBeDefined();
|
|
1193
|
+
expect(SeverityLevel.WARNING).toBeDefined();
|
|
1194
|
+
expect(SeverityLevel.INFO).toBeDefined();
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
it('should export circuit breaker states', () => {
|
|
1198
|
+
expect(CircuitBreakerState.CLOSED).toBeDefined();
|
|
1199
|
+
expect(CircuitBreakerState.OPEN).toBeDefined();
|
|
1200
|
+
expect(CircuitBreakerState.HALF_OPEN).toBeDefined();
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
it('should export error classes', () => {
|
|
1204
|
+
expect(ValidationError).toBeDefined();
|
|
1205
|
+
expect(CircuitBreakerOpenError).toBeDefined();
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
describe('Additional Coverage - Diagnostic Output', () => {
|
|
1210
|
+
it('should format system diagnostics', async () => {
|
|
1211
|
+
const diagnostics = await errorLogger.collectSystemDiagnostics();
|
|
1212
|
+
expect(diagnostics.hardware.cpuCores).toBeGreaterThan(0);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it('should collect CFN state with available checkpoint', async () => {
|
|
1216
|
+
const state = await errorLogger.collectCFNLoopState('test-task');
|
|
1217
|
+
expect(state.taskId).toBe('test-task');
|
|
1218
|
+
expect(state.checkpoint.available).toBeDefined();
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
it('should handle all troubleshooting step types', async () => {
|
|
1222
|
+
const errorTypes = [
|
|
1223
|
+
ErrorType.ORCHESTRATOR,
|
|
1224
|
+
ErrorType.AGENT_SPAWN,
|
|
1225
|
+
ErrorType.TIMEOUT,
|
|
1226
|
+
ErrorType.RESOURCE,
|
|
1227
|
+
];
|
|
1228
|
+
|
|
1229
|
+
for (const type of errorTypes) {
|
|
1230
|
+
const report = await errorLogger.generateReport(
|
|
1231
|
+
'test-' + type,
|
|
1232
|
+
'markdown'
|
|
1233
|
+
);
|
|
1234
|
+
expect(report.troubleshootingSteps.length).toBeGreaterThan(0);
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
describe('Additional Coverage - Report Formatting', () => {
|
|
1240
|
+
it('should format multi-line markdown report correctly', async () => {
|
|
1241
|
+
const md = await errorLogger.formatReportAsMarkdown('test-task');
|
|
1242
|
+
expect(md).toContain('# CFN Loop Error Report');
|
|
1243
|
+
expect(md).toContain('## Error Summary');
|
|
1244
|
+
expect(md).toContain('## Quick Diagnosis');
|
|
1245
|
+
expect(md).toContain('## Troubleshooting Steps');
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
it('should parse formatted JSON report', async () => {
|
|
1249
|
+
const json = await errorLogger.formatReportAsJson('test-task');
|
|
1250
|
+
const parsed = JSON.parse(json);
|
|
1251
|
+
expect(parsed.taskId).toBe('test-task');
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
describe('Additional Coverage - Error Enrichment', () => {
|
|
1256
|
+
it('should handle context enrichment with null metadata', async () => {
|
|
1257
|
+
const error = { ...mockErrorContext, metadata: undefined };
|
|
1258
|
+
await errorLogger.enrichWithTaskContext(error, {
|
|
1259
|
+
iteration: 3,
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
expect(error.metadata).toBeDefined();
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
it('should accumulate multiple context enrichments', async () => {
|
|
1266
|
+
const error = { ...mockErrorContext };
|
|
1267
|
+
|
|
1268
|
+
await errorLogger.enrichWithTaskContext(error, { task: 'test' });
|
|
1269
|
+
await errorLogger.enrichWithAgentContext(error, { agent: 'backend' });
|
|
1270
|
+
await errorLogger.enrichWithEnvironmentContext(error, { env: 'prod' });
|
|
1271
|
+
|
|
1272
|
+
expect(error.metadata).toBeDefined();
|
|
1273
|
+
expect(Object.keys(error.metadata || {}).length).toBeGreaterThan(0);
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
describe('Additional Coverage - Logger Configuration', () => {
|
|
1278
|
+
it('should switch backends dynamically', async () => {
|
|
1279
|
+
errorLogger.setBackends(['console']);
|
|
1280
|
+
errorLogger.setBackends(['file']);
|
|
1281
|
+
errorLogger.setBackends(['file', 'console']);
|
|
1282
|
+
|
|
1283
|
+
const result = await errorLogger.captureError(mockErrorContext);
|
|
1284
|
+
expect(result.captureId).toBeDefined();
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
it('should update retry configuration', () => {
|
|
1288
|
+
const newConfig = {
|
|
1289
|
+
maxAttempts: 5,
|
|
1290
|
+
initialDelayMs: 200,
|
|
1291
|
+
maxDelayMs: 10000,
|
|
1292
|
+
backoffMultiplier: 3,
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
errorLogger.setRetryConfig(newConfig);
|
|
1296
|
+
const delays = errorLogger.getBackoffDelays(3);
|
|
1297
|
+
|
|
1298
|
+
expect(delays.length).toBe(3);
|
|
1299
|
+
expect(delays[0]).toBeGreaterThan(0);
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
});
|