@howlil/ez-agents 3.5.0 → 4.0.0
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/README.md +735 -537
- package/agents/ez-architect-agent.md +267 -0
- package/agents/ez-backend-agent.md +303 -0
- package/agents/ez-chief-strategist.md +271 -0
- package/agents/ez-codebase-mapper.md +770 -770
- package/agents/ez-context-manager.md +319 -0
- package/agents/ez-debugger.md +1255 -1255
- package/agents/ez-design-expert.md +347 -0
- package/agents/ez-devops-agent.md +331 -0
- package/agents/ez-executor.md +487 -487
- package/agents/ez-frontend-agent.md +322 -0
- package/agents/ez-phase-researcher.md +553 -553
- package/agents/ez-planner.md +1307 -1307
- package/agents/ez-product-engineer.md +435 -0
- package/agents/ez-project-researcher.md +629 -629
- package/agents/ez-qa-agent.md +320 -0
- package/agents/ez-release-agent.md +333 -333
- package/agents/ez-requirements-agent.md +377 -377
- package/agents/ez-roadmapper.md +650 -650
- package/agents/ez-technical-writer.md +551 -0
- package/agents/ez-ux-expert.md +393 -0
- package/agents/ez-verifier.md +579 -579
- package/bin/guards/autonomy-guard.cjs +346 -0
- package/bin/guards/context-budget-guard.cjs +278 -0
- package/bin/guards/hallucination-guard.cjs +380 -0
- package/bin/guards/hidden-state-guard.cjs +182 -0
- package/bin/guards/team-overhead-guard.cjs +266 -0
- package/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/bin/lib/archetype-detector.cjs +289 -0
- package/bin/lib/assistant-adapter.cjs +361 -0
- package/bin/lib/audit-exec.cjs +175 -0
- package/bin/lib/auth.cjs +176 -0
- package/bin/lib/backup-service.cjs +422 -0
- package/bin/lib/bdd-validator.cjs +622 -0
- package/bin/lib/business-flow-mapper.cjs +429 -0
- package/bin/lib/circuit-breaker.cjs +276 -0
- package/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/bin/lib/codebase-analyzer.cjs +241 -0
- package/bin/lib/commands.cjs +691 -0
- package/bin/lib/config.cjs +236 -0
- package/bin/lib/constraint-extractor.cjs +526 -0
- package/bin/lib/content-scanner.cjs +238 -0
- package/bin/lib/context-cache.cjs +154 -0
- package/bin/lib/context-compressor.cjs +102 -0
- package/bin/lib/context-deduplicator.cjs +105 -0
- package/bin/lib/context-errors.cjs +78 -0
- package/bin/lib/context-manager.cjs +338 -0
- package/bin/lib/context-metadata-tracker.cjs +140 -0
- package/bin/lib/context-relevance-scorer.cjs +99 -0
- package/bin/lib/core.cjs +507 -0
- package/bin/lib/cost-alerts.cjs +174 -0
- package/bin/lib/cost-tracker.cjs +275 -0
- package/bin/lib/crash-recovery.cjs +220 -0
- package/bin/lib/dependency-graph.cjs +319 -0
- package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/bin/lib/deploy/deploy-status.cjs +74 -0
- package/bin/lib/discussion-synthesizer.cjs +439 -0
- package/bin/lib/error-cache.cjs +114 -0
- package/bin/lib/error-registry.cjs +177 -0
- package/bin/lib/file-access.cjs +207 -0
- package/bin/lib/file-lock.cjs +236 -0
- package/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/bin/lib/finops/cost-reporter.cjs +132 -0
- package/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/bin/lib/finops/spot-manager.cjs +118 -0
- package/bin/lib/framework-detector.cjs +396 -0
- package/bin/lib/frontmatter.cjs +313 -0
- package/bin/lib/fs-utils.cjs +153 -0
- package/bin/lib/gate-executor.cjs +272 -0
- package/bin/lib/gates/README.md +374 -0
- package/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/bin/lib/gates/gate-03-code.cjs +635 -0
- package/bin/lib/gates/gate-04-security.cjs +829 -0
- package/bin/lib/git-errors.cjs +83 -0
- package/bin/lib/git-utils.cjs +321 -0
- package/bin/lib/git-workflow-engine.cjs +1157 -0
- package/bin/lib/health-check.cjs +227 -0
- package/bin/lib/index.cjs +279 -0
- package/bin/lib/init.cjs +725 -0
- package/bin/lib/lock-logger.cjs +194 -0
- package/bin/lib/lock-state.cjs +263 -0
- package/bin/lib/lockfile-validator.cjs +227 -0
- package/bin/lib/log-rotation.cjs +71 -0
- package/bin/lib/logger.cjs +125 -0
- package/bin/lib/memory-compression.cjs +256 -0
- package/bin/lib/milestone.cjs +247 -0
- package/bin/lib/model-provider.cjs +241 -0
- package/bin/lib/package-manager-detector.cjs +203 -0
- package/bin/lib/package-manager-executor.cjs +385 -0
- package/bin/lib/package-manager-service.cjs +216 -0
- package/bin/lib/perf/api-monitor.cjs +88 -0
- package/bin/lib/perf/db-optimizer.cjs +78 -0
- package/bin/lib/perf/frontend-performance.cjs +56 -0
- package/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/bin/lib/perf/perf-baseline.cjs +102 -0
- package/bin/lib/perf/perf-reporter.cjs +117 -0
- package/bin/lib/perf/regression-detector.cjs +92 -0
- package/bin/lib/phase.cjs +963 -0
- package/bin/lib/planning-write.cjs +123 -0
- package/bin/lib/project-reporter.cjs +565 -0
- package/bin/lib/quality-gate.cjs +332 -0
- package/bin/lib/quality-metrics.cjs +324 -0
- package/bin/lib/recovery-manager.cjs +98 -0
- package/bin/lib/release-validator.cjs +617 -0
- package/bin/lib/retry.cjs +119 -0
- package/bin/lib/roadmap.cjs +309 -0
- package/bin/lib/safe-exec.cjs +173 -0
- package/bin/lib/safe-path.cjs +130 -0
- package/bin/lib/security-errors.cjs +62 -0
- package/bin/lib/session-chain.cjs +304 -0
- package/bin/lib/session-errors.cjs +81 -0
- package/bin/lib/session-export.cjs +251 -0
- package/bin/lib/session-import.cjs +262 -0
- package/bin/lib/session-manager.cjs +280 -0
- package/bin/lib/skill-context.cjs +148 -0
- package/bin/lib/skill-matcher.cjs +236 -0
- package/bin/lib/skill-registry.cjs +360 -0
- package/bin/lib/skill-resolver.cjs +449 -0
- package/bin/lib/skill-triggers.cjs +90 -0
- package/bin/lib/skill-validator.cjs +270 -0
- package/bin/lib/skill-versioning.cjs +355 -0
- package/bin/lib/stack-detector.cjs +399 -0
- package/bin/lib/state.cjs +736 -0
- package/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/bin/lib/temp-file.cjs +239 -0
- package/bin/lib/template.cjs +223 -0
- package/bin/lib/test-file-lock.cjs +112 -0
- package/bin/lib/test-graceful.cjs +93 -0
- package/bin/lib/test-logger.cjs +60 -0
- package/bin/lib/test-safe-exec.cjs +38 -0
- package/bin/lib/test-safe-path.cjs +33 -0
- package/bin/lib/test-temp-file.cjs +125 -0
- package/bin/lib/tier-manager.cjs +428 -0
- package/bin/lib/timeout-exec.cjs +63 -0
- package/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/bin/lib/url-fetch.cjs +170 -0
- package/bin/lib/verify.cjs +863 -0
- package/bin/update.js +217 -214
- package/commands/deploy.cjs +53 -0
- package/commands/ez/add-tests.md +41 -41
- package/commands/ez/audit-milestone.md +36 -36
- package/commands/ez/complete-milestone.md +136 -136
- package/commands/ez/discuss-phase.md +90 -90
- package/commands/ez/execute-phase.md +52 -52
- package/commands/ez/help.md +22 -22
- package/commands/ez/map-codebase.md +71 -71
- package/commands/ez/new-milestone.md +44 -44
- package/commands/ez/new-project.md +51 -42
- package/commands/ez/plan-phase.md +53 -53
- package/commands/ez/progress.md +36 -36
- package/commands/ez/quick.md +45 -45
- package/commands/ez/resume-work.md +40 -40
- package/commands/ez/run-phase.md +580 -0
- package/commands/ez/settings.md +36 -36
- package/commands/ez/update.md +37 -37
- package/commands/ez/verify-work.md +402 -38
- package/commands/health-check.cjs +44 -0
- package/commands/rollback.cjs +47 -0
- package/ez-agents/bin/ez-tools.cjs +599 -2
- package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
- package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
- package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
- package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
- package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
- package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
- package/ez-agents/bin/lib/audit-exec.cjs +166 -167
- package/ez-agents/bin/lib/auth.cjs +176 -176
- package/ez-agents/bin/lib/backup-service.cjs +422 -0
- package/ez-agents/bin/lib/bdd-validator.cjs +622 -622
- package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
- package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
- package/ez-agents/bin/lib/commands.cjs +685 -685
- package/ez-agents/bin/lib/config.cjs +41 -1
- package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
- package/ez-agents/bin/lib/content-scanner.cjs +238 -238
- package/ez-agents/bin/lib/context-cache.cjs +154 -154
- package/ez-agents/bin/lib/context-errors.cjs +71 -71
- package/ez-agents/bin/lib/context-manager.cjs +220 -220
- package/ez-agents/bin/lib/core.cjs +507 -512
- package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
- package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
- package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
- package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
- package/ez-agents/bin/lib/file-access.cjs +207 -207
- package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
- package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
- package/ez-agents/bin/lib/framework-detector.cjs +396 -0
- package/ez-agents/bin/lib/frontmatter.cjs +3 -1
- package/ez-agents/bin/lib/gates/README.md +374 -0
- package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
- package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
- package/ez-agents/bin/lib/git-errors.cjs +83 -83
- package/ez-agents/bin/lib/git-utils.cjs +321 -321
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -1157
- package/ez-agents/bin/lib/health-check.cjs +162 -162
- package/ez-agents/bin/lib/index.cjs +2 -8
- package/ez-agents/bin/lib/init.cjs +0 -2
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -227
- package/ez-agents/bin/lib/log-rotation.cjs +71 -0
- package/ez-agents/bin/lib/logger.cjs +22 -47
- package/ez-agents/bin/lib/memory-compression.cjs +256 -256
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -203
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -385
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -216
- package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
- package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
- package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
- package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
- package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
- package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
- package/ez-agents/bin/lib/project-reporter.cjs +502 -0
- package/ez-agents/bin/lib/quality-gate.cjs +332 -0
- package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
- package/ez-agents/bin/lib/release-validator.cjs +617 -614
- package/ez-agents/bin/lib/security-errors.cjs +62 -0
- package/ez-agents/bin/lib/session-chain.cjs +304 -304
- package/ez-agents/bin/lib/session-errors.cjs +81 -81
- package/ez-agents/bin/lib/session-export.cjs +251 -251
- package/ez-agents/bin/lib/session-import.cjs +262 -262
- package/ez-agents/bin/lib/session-manager.cjs +280 -280
- package/ez-agents/bin/lib/skill-context.cjs +148 -0
- package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
- package/ez-agents/bin/lib/skill-registry.cjs +341 -0
- package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
- package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
- package/ez-agents/bin/lib/skill-validator.cjs +270 -0
- package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
- package/ez-agents/bin/lib/stack-detector.cjs +399 -0
- package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/ez-agents/bin/lib/tier-manager.cjs +428 -428
- package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/ez-agents/bin/lib/url-fetch.cjs +170 -170
- package/ez-agents/bin/lib/verify.cjs +863 -863
- package/ez-agents/references/decimal-phase-calculation.md +65 -65
- package/ez-agents/references/git-integration.md +248 -248
- package/ez-agents/references/git-planning-commit.md +38 -38
- package/ez-agents/references/metrics-schema.md +118 -118
- package/ez-agents/references/model-profile-resolution.md +34 -34
- package/ez-agents/references/model-profiles.md +93 -93
- package/ez-agents/references/phase-argument-parsing.md +61 -61
- package/ez-agents/references/planning-config.md +340 -340
- package/ez-agents/references/tier-strategy.md +103 -103
- package/ez-agents/references/ui-brand.md +160 -160
- package/ez-agents/references/verification-patterns.md +612 -612
- package/ez-agents/templates/DEBUG.md +164 -164
- package/ez-agents/templates/UAT.md +247 -247
- package/ez-agents/templates/agent-output-format.md +404 -0
- package/ez-agents/templates/bdd-feature.md +173 -173
- package/ez-agents/templates/codebase/architecture.md +255 -255
- package/ez-agents/templates/codebase/structure.md +285 -285
- package/ez-agents/templates/copilot-instructions.md +7 -7
- package/ez-agents/templates/debug-subagent-prompt.md +91 -91
- package/ez-agents/templates/discovery.md +146 -146
- package/ez-agents/templates/discussion.md +68 -68
- package/ez-agents/templates/handoff-protocol.md +294 -0
- package/ez-agents/templates/incident-runbook.md +205 -205
- package/ez-agents/templates/mode-workflow-templates.md +301 -0
- package/ez-agents/templates/phase-prompt.md +610 -610
- package/ez-agents/templates/planner-subagent-prompt.md +117 -117
- package/ez-agents/templates/project.md +184 -184
- package/ez-agents/templates/release-checklist.md +136 -133
- package/ez-agents/templates/research.md +552 -552
- package/ez-agents/templates/rollback-plan.md +201 -201
- package/ez-agents/templates/security-user-setup.md +244 -0
- package/ez-agents/templates/skill-validation-rules.md +476 -0
- package/ez-agents/templates/state.md +180 -176
- package/ez-agents/templates/summary-complex.md +59 -59
- package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
- package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
- package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
- package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
- package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
- package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
- package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
- package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
- package/ez-agents/workflows/audit-milestone.md +1 -1
- package/ez-agents/workflows/autonomous.md +844 -844
- package/ez-agents/workflows/complete-milestone.md +1 -1
- package/ez-agents/workflows/discuss-phase.md +1 -1
- package/ez-agents/workflows/execute-phase.md +124 -3
- package/ez-agents/workflows/help.md +42 -181
- package/ez-agents/workflows/hotfix.md +291 -291
- package/ez-agents/workflows/new-milestone.md +713 -713
- package/ez-agents/workflows/new-project.md +1089 -1107
- package/ez-agents/workflows/plan-phase.md +0 -40
- package/ez-agents/workflows/release.md +253 -253
- package/ez-agents/workflows/resume-session.md +215 -215
- package/ez-agents/workflows/run-phase.md +531 -0
- package/ez-agents/workflows/settings.md +2 -35
- package/hooks/dist/ez-check-update.js +81 -81
- package/hooks/dist/ez-context-monitor.js +148 -141
- package/hooks/dist/ez-statusline.js +115 -115
- package/package.json +78 -71
- package/scripts/fix-qwen-installation.js +144 -144
- package/agents/ez-integration-checker.md +0 -443
- package/agents/ez-nyquist-auditor.md +0 -176
- package/agents/ez-observer-agent.md +0 -260
- package/agents/ez-plan-checker.md +0 -706
- package/agents/ez-research-synthesizer.md +0 -247
- package/agents/ez-scrum-master-agent.md +0 -242
- package/agents/ez-tech-lead-agent.md +0 -267
- package/agents/ez-ui-auditor.md +0 -439
- package/agents/ez-ui-checker.md +0 -300
- package/agents/ez-ui-researcher.md +0 -353
- package/commands/ez/add-phase.md +0 -43
- package/commands/ez/add-todo.md +0 -47
- package/commands/ez/arch-review.md +0 -102
- package/commands/ez/auth.md +0 -87
- package/commands/ez/autonomous.md +0 -41
- package/commands/ez/check-todos.md +0 -45
- package/commands/ez/cleanup.md +0 -18
- package/commands/ez/debug.md +0 -168
- package/commands/ez/export-session.md +0 -79
- package/commands/ez/gather-requirements.md +0 -117
- package/commands/ez/git-workflow.md +0 -72
- package/commands/ez/health.md +0 -22
- package/commands/ez/hotfix.md +0 -120
- package/commands/ez/import-session.md +0 -82
- package/commands/ez/insert-phase.md +0 -32
- package/commands/ez/join-discord.md +0 -18
- package/commands/ez/list-phase-assumptions.md +0 -46
- package/commands/ez/list-sessions.md +0 -96
- package/commands/ez/package-manager.md +0 -316
- package/commands/ez/pause-work.md +0 -38
- package/commands/ez/plan-milestone-gaps.md +0 -34
- package/commands/ez/preflight.md +0 -79
- package/commands/ez/reapply-patches.md +0 -124
- package/commands/ez/release.md +0 -153
- package/commands/ez/remove-phase.md +0 -31
- package/commands/ez/research-phase.md +0 -190
- package/commands/ez/resume.md +0 -107
- package/commands/ez/set-profile.md +0 -34
- package/commands/ez/standup.md +0 -85
- package/commands/ez/stats.md +0 -18
- package/commands/ez/ui-phase.md +0 -34
- package/commands/ez/ui-review.md +0 -32
- package/commands/ez/validate-phase.md +0 -35
- package/ez-agents/bin/lib/metrics-tracker.cjs +0 -406
- package/ez-agents/templates/UI-SPEC.md +0 -100
- package/ez-agents/templates/VALIDATION.md +0 -76
- package/ez-agents/templates/context.md +0 -352
- package/ez-agents/templates/verification-report.md +0 -322
- package/ez-agents/workflows/arch-review.md +0 -54
- package/ez-agents/workflows/export-session.md +0 -255
- package/ez-agents/workflows/gather-requirements.md +0 -206
- package/ez-agents/workflows/import-session.md +0 -303
- package/ez-agents/workflows/research-phase.md +0 -74
- package/ez-agents/workflows/standup.md +0 -64
- package/ez-agents/workflows/ui-phase.md +0 -290
- package/ez-agents/workflows/ui-review.md +0 -157
- package/ez-agents/workflows/validate-phase.md +0 -167
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate 1-2 Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for Requirement Completeness (Gate 1) and Architecture Review (Gate 2)
|
|
5
|
+
*
|
|
6
|
+
* Test cases:
|
|
7
|
+
* Gate 1:
|
|
8
|
+
* 1. Gate 1 passes when all REQ-IDs mapped
|
|
9
|
+
* 2. Gate 1 fails with errors for unmapped REQ-IDs
|
|
10
|
+
* 3. Gate 1 passes when acceptance criteria in Given-When-Then
|
|
11
|
+
* 4. Gate 1 fails when acceptance criteria missing
|
|
12
|
+
*
|
|
13
|
+
* Gate 2:
|
|
14
|
+
* 5. Gate 2 passes when structure matches skill recommendations
|
|
15
|
+
* 6. Gate 2 fails when structure deviates without justification
|
|
16
|
+
* 7. Gate 2 counts abstraction layers correctly
|
|
17
|
+
* 8. Gate 2 flags >3 abstraction layers as warning
|
|
18
|
+
* 9. Gate 2 detects premature repository pattern
|
|
19
|
+
* 10. Gate 2 detects unnecessary CQRS for simple CRUD
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { describe, it, beforeEach } = require('vitest');
|
|
23
|
+
const { strict: assert } = require('assert');
|
|
24
|
+
|
|
25
|
+
// Import Gate 1
|
|
26
|
+
const {
|
|
27
|
+
executeGate1,
|
|
28
|
+
checkGivenWhenThenFormat,
|
|
29
|
+
extractRequirementIds,
|
|
30
|
+
buildRequirementTaskMap,
|
|
31
|
+
buildRequirementPhaseMap,
|
|
32
|
+
} = require('../../ez-agents/bin/lib/gates/gate-01-requirement.cjs');
|
|
33
|
+
|
|
34
|
+
// Import Gate 2
|
|
35
|
+
const {
|
|
36
|
+
executeGate2,
|
|
37
|
+
countAbstractionLayers,
|
|
38
|
+
detectPrematureRepository,
|
|
39
|
+
detectUnnecessaryCQRS,
|
|
40
|
+
detectPrematureEventBus,
|
|
41
|
+
detectPrematureMicroservices,
|
|
42
|
+
checkSkillAlignment,
|
|
43
|
+
ABSTRACTION_THRESHOLDS,
|
|
44
|
+
} = require('../../ez-agents/bin/lib/gates/gate-02-architecture.cjs');
|
|
45
|
+
|
|
46
|
+
// Import QualityGate for integration test
|
|
47
|
+
const { QualityGate } = require('../../ez-agents/bin/lib/quality-gate.cjs');
|
|
48
|
+
const { registerGate1 } = require('../../ez-agents/bin/lib/gates/gate-01-requirement.cjs');
|
|
49
|
+
const { registerGate2 } = require('../../ez-agents/bin/lib/gates/gate-02-architecture.cjs');
|
|
50
|
+
|
|
51
|
+
describe('Gate 1: Requirement Completeness', () => {
|
|
52
|
+
describe('executeGate1', () => {
|
|
53
|
+
it('Gate 1 passes when all REQ-IDs mapped', async () => {
|
|
54
|
+
const context = {
|
|
55
|
+
requirements: [
|
|
56
|
+
{
|
|
57
|
+
id: 'REQ-001',
|
|
58
|
+
description: 'User can log in',
|
|
59
|
+
acceptanceCriteria: [
|
|
60
|
+
'Given valid credentials, When login form submitted, Then user is authenticated',
|
|
61
|
+
],
|
|
62
|
+
mappedTasks: ['task-001'],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'REQ-002',
|
|
66
|
+
description: 'User can log out',
|
|
67
|
+
acceptanceCriteria: [
|
|
68
|
+
'Given authenticated session, When logout clicked, Then session is terminated',
|
|
69
|
+
],
|
|
70
|
+
mappedPhases: ['phase-01'],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
tasks: [
|
|
74
|
+
{ id: 'task-001', name: 'Implement login', requirements: ['REQ-001'] },
|
|
75
|
+
],
|
|
76
|
+
phases: [
|
|
77
|
+
{ id: 'phase-01', name: 'Authentication', requirements: ['REQ-002'] },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const result = await executeGate1(context);
|
|
82
|
+
|
|
83
|
+
assert.strictEqual(result.passed, true);
|
|
84
|
+
assert.strictEqual(result.errors.length, 0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('Gate 1 fails with errors for unmapped REQ-IDs', async () => {
|
|
88
|
+
const context = {
|
|
89
|
+
requirements: [
|
|
90
|
+
{
|
|
91
|
+
id: 'REQ-001',
|
|
92
|
+
description: 'User can log in',
|
|
93
|
+
acceptanceCriteria: [
|
|
94
|
+
'Given valid credentials, When login form submitted, Then user is authenticated',
|
|
95
|
+
],
|
|
96
|
+
// No mappedTasks or mappedPhases
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'REQ-002',
|
|
100
|
+
description: 'User can log out',
|
|
101
|
+
acceptanceCriteria: [
|
|
102
|
+
'Given authenticated session, When logout clicked, Then session is terminated',
|
|
103
|
+
],
|
|
104
|
+
mappedTasks: ['task-001'],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
tasks: [
|
|
108
|
+
{ id: 'task-001', name: 'Implement logout', requirements: ['REQ-002'] },
|
|
109
|
+
],
|
|
110
|
+
// No phases
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const result = await executeGate1(context);
|
|
114
|
+
|
|
115
|
+
assert.strictEqual(result.passed, false);
|
|
116
|
+
assert.strictEqual(result.errors.length, 1);
|
|
117
|
+
assert.strictEqual(result.errors[0].path, 'requirements[0].id');
|
|
118
|
+
assert.ok(result.errors[0].message.includes('REQ-001'));
|
|
119
|
+
assert.ok(result.errors[0].message.includes('not mapped'));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('Gate 1 passes when acceptance criteria in Given-When-Then', async () => {
|
|
123
|
+
const context = {
|
|
124
|
+
requirements: [
|
|
125
|
+
{
|
|
126
|
+
id: 'REQ-001',
|
|
127
|
+
description: 'User can reset password',
|
|
128
|
+
acceptanceCriteria: [
|
|
129
|
+
'Given user has forgotten password, When reset link requested, Then email is sent',
|
|
130
|
+
],
|
|
131
|
+
mappedTasks: ['task-001'],
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
tasks: [
|
|
135
|
+
{ id: 'task-001', name: 'Implement password reset', requirements: ['REQ-001'] },
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = await executeGate1(context);
|
|
140
|
+
|
|
141
|
+
assert.strictEqual(result.passed, true);
|
|
142
|
+
assert.strictEqual(result.errors.length, 0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('Gate 1 fails when acceptance criteria missing Given-When-Then', async () => {
|
|
146
|
+
const context = {
|
|
147
|
+
requirements: [
|
|
148
|
+
{
|
|
149
|
+
id: 'REQ-001',
|
|
150
|
+
description: 'User can reset password',
|
|
151
|
+
acceptanceCriteria: [
|
|
152
|
+
'Password reset works', // Not in Given-When-Then format
|
|
153
|
+
],
|
|
154
|
+
mappedTasks: ['task-001'],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
tasks: [
|
|
158
|
+
{ id: 'task-001', name: 'Implement password reset', requirements: ['REQ-001'] },
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = await executeGate1(context);
|
|
163
|
+
|
|
164
|
+
assert.strictEqual(result.passed, false);
|
|
165
|
+
assert.strictEqual(result.errors.length, 1);
|
|
166
|
+
assert.strictEqual(result.errors[0].path, 'requirements[0].acceptanceCriteria');
|
|
167
|
+
assert.ok(result.errors[0].message.includes('Given-When-Then'));
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('checkGivenWhenThenFormat', () => {
|
|
172
|
+
it('should validate proper Given-When-Then format', () => {
|
|
173
|
+
const result = checkGivenWhenThenFormat([
|
|
174
|
+
'Given valid credentials, When login submitted, Then user authenticated',
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
assert.strictEqual(result.valid, true);
|
|
178
|
+
assert.strictEqual(result.missingComponents.length, 0);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should detect missing Given component', () => {
|
|
182
|
+
const result = checkGivenWhenThenFormat([
|
|
183
|
+
'When login submitted, Then user authenticated',
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
assert.strictEqual(result.valid, false);
|
|
187
|
+
assert.ok(result.missingComponents.includes('Given'));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should detect missing When component', () => {
|
|
191
|
+
const result = checkGivenWhenThenFormat([
|
|
192
|
+
'Given valid credentials, Then user authenticated',
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
assert.strictEqual(result.valid, false);
|
|
196
|
+
assert.ok(result.missingComponents.includes('When'));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should detect missing Then component', () => {
|
|
200
|
+
const result = checkGivenWhenThenFormat([
|
|
201
|
+
'Given valid credentials, When login submitted',
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
assert.strictEqual(result.valid, false);
|
|
205
|
+
assert.ok(result.missingComponents.includes('Then'));
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should handle multi-line format', () => {
|
|
209
|
+
const result = checkGivenWhenThenFormat([
|
|
210
|
+
'Given user is on login page',
|
|
211
|
+
'When user enters valid credentials',
|
|
212
|
+
'Then user is redirected to dashboard',
|
|
213
|
+
]);
|
|
214
|
+
|
|
215
|
+
assert.strictEqual(result.valid, true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle format with colons', () => {
|
|
219
|
+
const result = checkGivenWhenThenFormat([
|
|
220
|
+
'Given: user has valid session',
|
|
221
|
+
'When: user clicks logout',
|
|
222
|
+
'Then: session is destroyed',
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
assert.strictEqual(result.valid, true);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('extractRequirementIds', () => {
|
|
230
|
+
it('should extract REQ-IDs from text', () => {
|
|
231
|
+
const text = 'This implements REQ-001 and REQ-002 for the login feature';
|
|
232
|
+
const ids = extractRequirementIds(text);
|
|
233
|
+
|
|
234
|
+
assert.deepStrictEqual(ids, ['REQ-001', 'REQ-002']);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should extract various ID formats', () => {
|
|
238
|
+
const text = 'Covers GRAPH-01, GATE-02, and REQ-100';
|
|
239
|
+
const ids = extractRequirementIds(text);
|
|
240
|
+
|
|
241
|
+
assert.deepStrictEqual(ids.sort(), ['GATE-02', 'GRAPH-01', 'REQ-100'].sort());
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should return empty array for no matches', () => {
|
|
245
|
+
const text = 'No requirement IDs here';
|
|
246
|
+
const ids = extractRequirementIds(text);
|
|
247
|
+
|
|
248
|
+
assert.deepStrictEqual(ids, []);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should deduplicate IDs', () => {
|
|
252
|
+
const text = 'REQ-001 is related to REQ-001';
|
|
253
|
+
const ids = extractRequirementIds(text);
|
|
254
|
+
|
|
255
|
+
assert.deepStrictEqual(ids, ['REQ-001']);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('buildRequirementTaskMap', () => {
|
|
260
|
+
it('should build map from task requirements', () => {
|
|
261
|
+
const tasks = [
|
|
262
|
+
{ id: 'task-001', name: 'Login', requirements: ['REQ-001', 'REQ-002'] },
|
|
263
|
+
{ id: 'task-002', name: 'Logout', requirements: ['REQ-003'] },
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
const map = buildRequirementTaskMap(tasks);
|
|
267
|
+
|
|
268
|
+
assert.ok(map.has('REQ-001'));
|
|
269
|
+
assert.ok(map.has('REQ-002'));
|
|
270
|
+
assert.ok(map.has('REQ-003'));
|
|
271
|
+
assert.strictEqual(map.get('REQ-001').has('task-001'), true);
|
|
272
|
+
assert.strictEqual(map.get('REQ-003').has('task-002'), true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should handle tasks without requirements', () => {
|
|
276
|
+
const tasks = [
|
|
277
|
+
{ id: 'task-001', name: 'Setup' },
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const map = buildRequirementTaskMap(tasks);
|
|
281
|
+
|
|
282
|
+
assert.strictEqual(map.size, 0);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should handle empty task array', () => {
|
|
286
|
+
const map = buildRequirementTaskMap([]);
|
|
287
|
+
assert.strictEqual(map.size, 0);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should handle null/undefined input', () => {
|
|
291
|
+
assert.strictEqual(buildRequirementTaskMap(null).size, 0);
|
|
292
|
+
assert.strictEqual(buildRequirementTaskMap(undefined).size, 0);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('buildRequirementPhaseMap', () => {
|
|
297
|
+
it('should build map from phase requirements', () => {
|
|
298
|
+
const phases = [
|
|
299
|
+
{ id: 'phase-01', name: 'Auth', requirements: ['REQ-001', 'REQ-002'] },
|
|
300
|
+
{ id: 'phase-02', name: 'Dashboard', requirements: ['REQ-003'] },
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
const map = buildRequirementPhaseMap(phases);
|
|
304
|
+
|
|
305
|
+
assert.ok(map.has('REQ-001'));
|
|
306
|
+
assert.ok(map.has('REQ-003'));
|
|
307
|
+
assert.strictEqual(map.get('REQ-001').has('phase-01'), true);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('Gate 2: Architecture Review', () => {
|
|
313
|
+
describe('executeGate2', () => {
|
|
314
|
+
it('Gate 2 passes when structure matches skill recommendations', async () => {
|
|
315
|
+
const context = {
|
|
316
|
+
projectTier: 'mvp',
|
|
317
|
+
architecture: {
|
|
318
|
+
abstractionLayers: 2,
|
|
319
|
+
patterns: ['controller', 'service'],
|
|
320
|
+
},
|
|
321
|
+
skillRecommendations: [
|
|
322
|
+
{
|
|
323
|
+
skillName: 'nodejs-mvp',
|
|
324
|
+
recommendedStructure: ['controller', 'service'],
|
|
325
|
+
bestPractices: ['Keep it simple'],
|
|
326
|
+
antiPatterns: [],
|
|
327
|
+
maxAbstractionLayers: 2,
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const result = await executeGate2(context);
|
|
333
|
+
|
|
334
|
+
assert.strictEqual(result.passed, true);
|
|
335
|
+
assert.strictEqual(result.errors.length, 0);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('Gate 2 fails when structure deviates without justification', async () => {
|
|
339
|
+
const context = {
|
|
340
|
+
projectTier: 'mvp',
|
|
341
|
+
architecture: {
|
|
342
|
+
abstractionLayers: 4,
|
|
343
|
+
patterns: ['controller', 'service', 'repository', 'use-case', 'domain'],
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const result = await executeGate2(context);
|
|
348
|
+
|
|
349
|
+
assert.strictEqual(result.passed, false);
|
|
350
|
+
assert.ok(result.errors.length > 0);
|
|
351
|
+
assert.ok(result.errors.some(e => e.path === 'architecture.abstractionLayers'));
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('Gate 2 counts abstraction layers correctly', async () => {
|
|
355
|
+
const context = {
|
|
356
|
+
projectTier: 'medium',
|
|
357
|
+
files: [
|
|
358
|
+
{ path: '/controllers/UserController.js', type: 'controller' },
|
|
359
|
+
{ path: '/services/UserService.js', type: 'service' },
|
|
360
|
+
{ path: '/repositories/UserRepository.js', type: 'repository' },
|
|
361
|
+
{ path: '/models/User.js', type: 'model' },
|
|
362
|
+
],
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const result = await executeGate2(context);
|
|
366
|
+
|
|
367
|
+
// 4 layers for medium tier (max 3) should fail
|
|
368
|
+
assert.strictEqual(result.passed, false);
|
|
369
|
+
assert.ok(result.errors.some(e => e.message.includes('abstraction layers')));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('Gate 2 flags >3 abstraction layers as warning', async () => {
|
|
373
|
+
const context = {
|
|
374
|
+
projectTier: 'enterprise',
|
|
375
|
+
architecture: {
|
|
376
|
+
abstractionLayers: 4,
|
|
377
|
+
patterns: ['controller', 'service', 'repository', 'domain'],
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const result = await executeGate2(context);
|
|
382
|
+
|
|
383
|
+
// Enterprise allows 4 layers, so it should pass but may have warnings
|
|
384
|
+
assert.strictEqual(result.passed, true);
|
|
385
|
+
// Check for warning about >3 layers
|
|
386
|
+
assert.ok(
|
|
387
|
+
result.warnings.some(w => w.includes('abstraction layers')) ||
|
|
388
|
+
result.errors.length === 0
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('Gate 2 detects premature repository pattern', async () => {
|
|
393
|
+
const context = {
|
|
394
|
+
projectTier: 'mvp',
|
|
395
|
+
hasRepositoryPattern: true,
|
|
396
|
+
crudOperationCount: 5,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const result = await executeGate2(context);
|
|
400
|
+
|
|
401
|
+
assert.strictEqual(result.passed, false);
|
|
402
|
+
assert.ok(result.errors.some(e => e.message.includes('Repository pattern')));
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('Gate 2 detects unnecessary CQRS for simple CRUD', async () => {
|
|
406
|
+
const context = {
|
|
407
|
+
projectTier: 'mvp',
|
|
408
|
+
hasCQRS: true,
|
|
409
|
+
crudOperationCount: 8,
|
|
410
|
+
eventCount: 2,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const result = await executeGate2(context);
|
|
414
|
+
|
|
415
|
+
assert.strictEqual(result.passed, false);
|
|
416
|
+
assert.ok(result.errors.some(e => e.message.includes('CQRS')));
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe('countAbstractionLayers', () => {
|
|
421
|
+
it('should count layers from file types', () => {
|
|
422
|
+
const files = [
|
|
423
|
+
{ path: '/controllers/UserController.js', type: 'controller' },
|
|
424
|
+
{ path: '/services/UserService.js', type: 'service' },
|
|
425
|
+
{ path: '/models/User.js', type: 'model' },
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
const layers = countAbstractionLayers(files);
|
|
429
|
+
assert.strictEqual(layers, 3);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should count layers from path inference', () => {
|
|
433
|
+
const files = [
|
|
434
|
+
{ path: '/controllers/UserController.js' },
|
|
435
|
+
{ path: '/services/UserService.js' },
|
|
436
|
+
{ path: '/repositories/UserRepository.js' },
|
|
437
|
+
{ path: '/models/User.js' },
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
const layers = countAbstractionLayers(files);
|
|
441
|
+
assert.strictEqual(layers, 4);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should use explicit layer property', () => {
|
|
445
|
+
const files = [
|
|
446
|
+
{ path: '/file1.js', layer: 0 },
|
|
447
|
+
{ path: '/file2.js', layer: 1 },
|
|
448
|
+
{ path: '/file3.js', layer: 2 },
|
|
449
|
+
{ path: '/file4.js', layer: 3 },
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
const layers = countAbstractionLayers(files);
|
|
453
|
+
assert.strictEqual(layers, 3);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should return 0 for empty array', () => {
|
|
457
|
+
const layers = countAbstractionLayers([]);
|
|
458
|
+
assert.strictEqual(layers, 0);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should handle middleware layer', () => {
|
|
462
|
+
const files = [
|
|
463
|
+
{ path: '/middleware/AuthMiddleware.js', type: 'middleware' },
|
|
464
|
+
{ path: '/controllers/UserController.js', type: 'controller' },
|
|
465
|
+
{ path: '/services/UserService.js', type: 'service' },
|
|
466
|
+
];
|
|
467
|
+
|
|
468
|
+
const layers = countAbstractionLayers(files);
|
|
469
|
+
assert.strictEqual(layers, 3);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
describe('detectPrematureRepository', () => {
|
|
474
|
+
it('should detect repository pattern in MVP with simple CRUD', () => {
|
|
475
|
+
const context = {
|
|
476
|
+
projectTier: 'mvp',
|
|
477
|
+
hasRepositoryPattern: true,
|
|
478
|
+
crudOperationCount: 5,
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const result = detectPrematureRepository(context);
|
|
482
|
+
|
|
483
|
+
assert.strictEqual(result.detected, true);
|
|
484
|
+
assert.ok(result.reason.includes('Repository pattern'));
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should not detect when CRUD count is high', () => {
|
|
488
|
+
const context = {
|
|
489
|
+
projectTier: 'mvp',
|
|
490
|
+
hasRepositoryPattern: true,
|
|
491
|
+
crudOperationCount: 15,
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const result = detectPrematureRepository(context);
|
|
495
|
+
|
|
496
|
+
assert.strictEqual(result.detected, false);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should allow with justification', () => {
|
|
500
|
+
const context = {
|
|
501
|
+
projectTier: 'mvp',
|
|
502
|
+
hasRepositoryPattern: true,
|
|
503
|
+
crudOperationCount: 5,
|
|
504
|
+
architecture: {
|
|
505
|
+
justifications: {
|
|
506
|
+
repository: 'Multiple data sources require abstraction',
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const result = detectPrematureRepository(context);
|
|
512
|
+
|
|
513
|
+
assert.strictEqual(result.detected, false);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should not detect for medium tier', () => {
|
|
517
|
+
const context = {
|
|
518
|
+
projectTier: 'medium',
|
|
519
|
+
hasRepositoryPattern: true,
|
|
520
|
+
crudOperationCount: 8,
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const result = detectPrematureRepository(context);
|
|
524
|
+
|
|
525
|
+
assert.strictEqual(result.detected, false);
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
describe('detectUnnecessaryCQRS', () => {
|
|
530
|
+
it('should detect CQRS for simple CRUD', () => {
|
|
531
|
+
const context = {
|
|
532
|
+
projectTier: 'mvp',
|
|
533
|
+
hasCQRS: true,
|
|
534
|
+
crudOperationCount: 8,
|
|
535
|
+
eventCount: 2,
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const result = detectUnnecessaryCQRS(context);
|
|
539
|
+
|
|
540
|
+
assert.strictEqual(result.detected, true);
|
|
541
|
+
assert.ok(result.reason.includes('CQRS'));
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should not detect CQRS with high event count', () => {
|
|
545
|
+
const context = {
|
|
546
|
+
projectTier: 'medium',
|
|
547
|
+
hasCQRS: true,
|
|
548
|
+
crudOperationCount: 20,
|
|
549
|
+
eventCount: 15,
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const result = detectUnnecessaryCQRS(context);
|
|
553
|
+
|
|
554
|
+
assert.strictEqual(result.detected, false);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('should allow CQRS with justification', () => {
|
|
558
|
+
const context = {
|
|
559
|
+
projectTier: 'mvp',
|
|
560
|
+
hasCQRS: true,
|
|
561
|
+
crudOperationCount: 8,
|
|
562
|
+
eventCount: 2,
|
|
563
|
+
architecture: {
|
|
564
|
+
justifications: {
|
|
565
|
+
cqrs: 'Complex read/write separation required for reporting',
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const result = detectUnnecessaryCQRS(context);
|
|
571
|
+
|
|
572
|
+
assert.strictEqual(result.detected, false);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
describe('detectPrematureEventBus', () => {
|
|
577
|
+
it('should detect event bus with few events', () => {
|
|
578
|
+
const context = {
|
|
579
|
+
hasEventBus: true,
|
|
580
|
+
eventCount: 2,
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const result = detectPrematureEventBus(context);
|
|
584
|
+
|
|
585
|
+
assert.strictEqual(result.detected, true);
|
|
586
|
+
assert.ok(result.reason.includes('Event bus'));
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('should not detect event bus with many events', () => {
|
|
590
|
+
const context = {
|
|
591
|
+
hasEventBus: true,
|
|
592
|
+
eventCount: 10,
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const result = detectPrematureEventBus(context);
|
|
596
|
+
|
|
597
|
+
assert.strictEqual(result.detected, false);
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
describe('detectPrematureMicroservices', () => {
|
|
602
|
+
it('should detect microservices in MVP', () => {
|
|
603
|
+
const context = {
|
|
604
|
+
projectTier: 'mvp',
|
|
605
|
+
hasMicroservices: true,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const result = detectPrematureMicroservices(context);
|
|
609
|
+
|
|
610
|
+
assert.strictEqual(result.detected, true);
|
|
611
|
+
assert.ok(result.reason.includes('Microservices'));
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('should allow microservices with justification', () => {
|
|
615
|
+
const context = {
|
|
616
|
+
projectTier: 'mvp',
|
|
617
|
+
hasMicroservices: true,
|
|
618
|
+
architecture: {
|
|
619
|
+
justifications: {
|
|
620
|
+
microservices: 'Existing domain boundaries from legacy system',
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const result = detectPrematureMicroservices(context);
|
|
626
|
+
|
|
627
|
+
assert.strictEqual(result.detected, false);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it('should not detect for enterprise tier', () => {
|
|
631
|
+
const context = {
|
|
632
|
+
projectTier: 'enterprise',
|
|
633
|
+
hasMicroservices: true,
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const result = detectPrematureMicroservices(context);
|
|
637
|
+
|
|
638
|
+
// Enterprise without justification still warns
|
|
639
|
+
assert.strictEqual(result.detected, true);
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
describe('checkSkillAlignment', () => {
|
|
644
|
+
it('should return empty deviations when aligned', () => {
|
|
645
|
+
const skillRecommendations = [
|
|
646
|
+
{
|
|
647
|
+
skillName: 'nodejs-mvp',
|
|
648
|
+
recommendedStructure: ['controller', 'service'],
|
|
649
|
+
antiPatterns: ['repository'],
|
|
650
|
+
},
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
const architecture = {
|
|
654
|
+
patterns: ['controller', 'service'],
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
const result = checkSkillAlignment(skillRecommendations, architecture);
|
|
658
|
+
|
|
659
|
+
assert.strictEqual(result.deviations.length, 0);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it('should detect anti-pattern usage', () => {
|
|
663
|
+
const skillRecommendations = [
|
|
664
|
+
{
|
|
665
|
+
skillName: 'nodejs-mvp',
|
|
666
|
+
recommendedStructure: ['controller', 'service'],
|
|
667
|
+
antiPatterns: ['repository', 'cqrs'],
|
|
668
|
+
bestPractices: ['Keep it simple'],
|
|
669
|
+
},
|
|
670
|
+
];
|
|
671
|
+
|
|
672
|
+
const architecture = {
|
|
673
|
+
patterns: ['controller', 'service', 'repository'],
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const result = checkSkillAlignment(skillRecommendations, architecture);
|
|
677
|
+
|
|
678
|
+
assert.strictEqual(result.deviations.length, 1);
|
|
679
|
+
assert.strictEqual(result.deviations[0].skill, 'nodejs-mvp');
|
|
680
|
+
assert.ok(result.deviations[0].deviation.includes('repository'));
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('should detect abstraction layer violations', () => {
|
|
684
|
+
const skillRecommendations = [
|
|
685
|
+
{
|
|
686
|
+
skillName: 'nodejs-mvp',
|
|
687
|
+
maxAbstractionLayers: 2,
|
|
688
|
+
},
|
|
689
|
+
];
|
|
690
|
+
|
|
691
|
+
const architecture = {
|
|
692
|
+
abstractionLayers: 4,
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
const result = checkSkillAlignment(skillRecommendations, architecture);
|
|
696
|
+
|
|
697
|
+
assert.strictEqual(result.deviations.length, 1);
|
|
698
|
+
assert.ok(result.deviations[0].deviation.includes('Abstraction layers'));
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it('should handle empty skill recommendations', () => {
|
|
702
|
+
const result = checkSkillAlignment([], { patterns: ['controller'] });
|
|
703
|
+
|
|
704
|
+
assert.strictEqual(result.deviations.length, 0);
|
|
705
|
+
assert.strictEqual(result.matched.length, 0);
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
describe('ABSTRACTION_THRESHOLDS', () => {
|
|
710
|
+
it('should have correct thresholds for each tier', () => {
|
|
711
|
+
assert.strictEqual(ABSTRACTION_THRESHOLDS.mvp, 2);
|
|
712
|
+
assert.strictEqual(ABSTRACTION_THRESHOLDS.medium, 3);
|
|
713
|
+
assert.strictEqual(ABSTRACTION_THRESHOLDS.enterprise, 4);
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
describe('Gate Integration Tests', () => {
|
|
719
|
+
let gates;
|
|
720
|
+
|
|
721
|
+
beforeEach(() => {
|
|
722
|
+
gates = new QualityGate();
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('Gate 1 registers with QualityGate coordinator', () => {
|
|
726
|
+
registerGate1(gates);
|
|
727
|
+
|
|
728
|
+
const registeredGates = gates.getRegisteredGates();
|
|
729
|
+
assert.ok(registeredGates.includes('gate-01-requirement'));
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('Gate 2 registers with QualityGate coordinator', () => {
|
|
733
|
+
registerGate2(gates);
|
|
734
|
+
|
|
735
|
+
const registeredGates = gates.getRegisteredGates();
|
|
736
|
+
assert.ok(registeredGates.includes('gate-02-architecture'));
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it('Both gates execute successfully through coordinator', async () => {
|
|
740
|
+
registerGate1(gates);
|
|
741
|
+
registerGate2(gates);
|
|
742
|
+
|
|
743
|
+
const gate1Context = {
|
|
744
|
+
requirements: [
|
|
745
|
+
{
|
|
746
|
+
id: 'REQ-001',
|
|
747
|
+
description: 'User can log in',
|
|
748
|
+
acceptanceCriteria: [
|
|
749
|
+
'Given valid credentials, When login submitted, Then authenticated',
|
|
750
|
+
],
|
|
751
|
+
mappedTasks: ['task-001'],
|
|
752
|
+
},
|
|
753
|
+
],
|
|
754
|
+
tasks: [
|
|
755
|
+
{ id: 'task-001', name: 'Implement login', requirements: ['REQ-001'] },
|
|
756
|
+
],
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
const gate2Context = {
|
|
760
|
+
projectTier: 'mvp',
|
|
761
|
+
architecture: {
|
|
762
|
+
abstractionLayers: 2,
|
|
763
|
+
patterns: ['controller', 'service'],
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
const gate1Result = await gates.executeGate('gate-01-requirement', gate1Context);
|
|
768
|
+
const gate2Result = await gates.executeGate('gate-02-architecture', gate2Context);
|
|
769
|
+
|
|
770
|
+
assert.strictEqual(gate1Result.passed, true);
|
|
771
|
+
assert.strictEqual(gate2Result.passed, true);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('Gate 1 fails through coordinator for unmapped requirements', async () => {
|
|
775
|
+
registerGate1(gates);
|
|
776
|
+
|
|
777
|
+
const context = {
|
|
778
|
+
requirements: [
|
|
779
|
+
{
|
|
780
|
+
id: 'REQ-001',
|
|
781
|
+
description: 'User can log in',
|
|
782
|
+
acceptanceCriteria: [
|
|
783
|
+
'Given valid credentials, When login submitted, Then authenticated',
|
|
784
|
+
],
|
|
785
|
+
// No mapping
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const result = await gates.executeGate('gate-01-requirement', context);
|
|
791
|
+
|
|
792
|
+
assert.strictEqual(result.passed, false);
|
|
793
|
+
assert.ok(result.errors.length > 0);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it('Gate 2 fails through coordinator for overengineering', async () => {
|
|
797
|
+
registerGate2(gates);
|
|
798
|
+
|
|
799
|
+
const context = {
|
|
800
|
+
projectTier: 'mvp',
|
|
801
|
+
hasRepositoryPattern: true,
|
|
802
|
+
hasCQRS: true,
|
|
803
|
+
crudOperationCount: 5,
|
|
804
|
+
eventCount: 2,
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const result = await gates.executeGate('gate-02-architecture', context);
|
|
808
|
+
|
|
809
|
+
assert.strictEqual(result.passed, false);
|
|
810
|
+
assert.ok(result.errors.length > 0);
|
|
811
|
+
});
|
|
812
|
+
});
|