popeye-cli 2.1.0 → 2.7.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/dist/adapters/gemini.d.ts +14 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +41 -6
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +14 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +42 -6
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +10 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +44 -5
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.js +1 -1
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +328 -21
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +25 -2
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +21 -6
- package/dist/generators/doc-parser.d.ts.map +1 -1
- package/dist/generators/doc-parser.js +55 -4
- package/dist/generators/doc-parser.js.map +1 -1
- package/dist/generators/templates/fullstack.js +1 -1
- package/dist/generators/templates/website-components.js +1 -1
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-config.d.ts +4 -1
- package/dist/generators/templates/website-config.d.ts.map +1 -1
- package/dist/generators/templates/website-config.js +17 -11
- package/dist/generators/templates/website-config.js.map +1 -1
- package/dist/generators/templates/website-conversion.js +1 -1
- package/dist/generators/templates/website-conversion.js.map +1 -1
- package/dist/generators/templates/website-landing.js +1 -1
- package/dist/generators/templates/website-landing.js.map +1 -1
- package/dist/generators/templates/website-layout.d.ts +36 -4
- package/dist/generators/templates/website-layout.d.ts.map +1 -1
- package/dist/generators/templates/website-layout.js +466 -23
- package/dist/generators/templates/website-layout.js.map +1 -1
- package/dist/generators/templates/website-pricing.js +1 -1
- package/dist/generators/templates/website-pricing.js.map +1 -1
- package/dist/generators/templates/website-sections.js +1 -1
- package/dist/generators/templates/website-sections.js.map +1 -1
- package/dist/generators/templates/website-seo.d.ts.map +1 -1
- package/dist/generators/templates/website-seo.js +4 -1
- package/dist/generators/templates/website-seo.js.map +1 -1
- package/dist/generators/templates/website.d.ts +1 -1
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +1 -1
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-ai.d.ts +52 -0
- package/dist/generators/website-content-ai.d.ts.map +1 -0
- package/dist/generators/website-content-ai.js +141 -0
- package/dist/generators/website-content-ai.js.map +1 -0
- package/dist/generators/website-content-scanner.d.ts +1 -1
- package/dist/generators/website-content-scanner.d.ts.map +1 -1
- package/dist/generators/website-content-scanner.js +98 -1
- package/dist/generators/website-content-scanner.js.map +1 -1
- package/dist/generators/website-context.d.ts +34 -1
- package/dist/generators/website-context.d.ts.map +1 -1
- package/dist/generators/website-context.js +131 -9
- package/dist/generators/website-context.js.map +1 -1
- package/dist/generators/website-debug.d.ts +12 -0
- package/dist/generators/website-debug.d.ts.map +1 -1
- package/dist/generators/website-debug.js +16 -0
- package/dist/generators/website-debug.js.map +1 -1
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +26 -4
- package/dist/generators/website.js.map +1 -1
- package/dist/pipeline/artifact-manager.d.ts.map +1 -1
- package/dist/pipeline/artifact-manager.js +3 -0
- package/dist/pipeline/artifact-manager.js.map +1 -1
- package/dist/pipeline/auto-recovery.d.ts +56 -0
- package/dist/pipeline/auto-recovery.d.ts.map +1 -0
- package/dist/pipeline/auto-recovery.js +185 -0
- package/dist/pipeline/auto-recovery.js.map +1 -0
- package/dist/pipeline/change-request.d.ts +39 -0
- package/dist/pipeline/change-request.d.ts.map +1 -1
- package/dist/pipeline/change-request.js +40 -1
- package/dist/pipeline/change-request.js.map +1 -1
- package/dist/pipeline/check-runner.d.ts +30 -1
- package/dist/pipeline/check-runner.d.ts.map +1 -1
- package/dist/pipeline/check-runner.js +122 -1
- package/dist/pipeline/check-runner.js.map +1 -1
- package/dist/pipeline/command-resolver.d.ts.map +1 -1
- package/dist/pipeline/command-resolver.js +33 -2
- package/dist/pipeline/command-resolver.js.map +1 -1
- package/dist/pipeline/consensus/arbitrator-query.d.ts +22 -0
- package/dist/pipeline/consensus/arbitrator-query.d.ts.map +1 -0
- package/dist/pipeline/consensus/arbitrator-query.js +70 -0
- package/dist/pipeline/consensus/arbitrator-query.js.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts +131 -7
- package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -1
- package/dist/pipeline/consensus/consensus-runner.js +809 -35
- package/dist/pipeline/consensus/consensus-runner.js.map +1 -1
- package/dist/pipeline/cr-lifecycle.d.ts +42 -0
- package/dist/pipeline/cr-lifecycle.d.ts.map +1 -0
- package/dist/pipeline/cr-lifecycle.js +89 -0
- package/dist/pipeline/cr-lifecycle.js.map +1 -0
- package/dist/pipeline/gate-engine.d.ts +1 -0
- package/dist/pipeline/gate-engine.d.ts.map +1 -1
- package/dist/pipeline/gate-engine.js +27 -8
- package/dist/pipeline/gate-engine.js.map +1 -1
- package/dist/pipeline/migration.d.ts.map +1 -1
- package/dist/pipeline/migration.js +3 -26
- package/dist/pipeline/migration.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +311 -16
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.d.ts +15 -4
- package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.js +29 -17
- package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -1
- package/dist/pipeline/phases/architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/architecture.js +5 -3
- package/dist/pipeline/phases/architecture.js.map +1 -1
- package/dist/pipeline/phases/audit.d.ts.map +1 -1
- package/dist/pipeline/phases/audit.js +5 -3
- package/dist/pipeline/phases/audit.js.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.js +10 -1
- package/dist/pipeline/phases/consensus-architecture.js.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.js +10 -3
- package/dist/pipeline/phases/consensus-master-plan.js.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.js +10 -1
- package/dist/pipeline/phases/consensus-role-plans.js.map +1 -1
- package/dist/pipeline/phases/done.d.ts.map +1 -1
- package/dist/pipeline/phases/done.js +9 -4
- package/dist/pipeline/phases/done.js.map +1 -1
- package/dist/pipeline/phases/intake.d.ts +1 -0
- package/dist/pipeline/phases/intake.d.ts.map +1 -1
- package/dist/pipeline/phases/intake.js +56 -13
- package/dist/pipeline/phases/intake.js.map +1 -1
- package/dist/pipeline/phases/phase-context.d.ts +2 -0
- package/dist/pipeline/phases/phase-context.d.ts.map +1 -1
- package/dist/pipeline/phases/phase-context.js +3 -1
- package/dist/pipeline/phases/phase-context.js.map +1 -1
- package/dist/pipeline/phases/production-gate.d.ts.map +1 -1
- package/dist/pipeline/phases/production-gate.js +28 -3
- package/dist/pipeline/phases/production-gate.js.map +1 -1
- package/dist/pipeline/phases/qa-validation.d.ts.map +1 -1
- package/dist/pipeline/phases/qa-validation.js +38 -5
- package/dist/pipeline/phases/qa-validation.js.map +1 -1
- package/dist/pipeline/phases/recovery-loop.d.ts +2 -0
- package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -1
- package/dist/pipeline/phases/recovery-loop.js +200 -6
- package/dist/pipeline/phases/recovery-loop.js.map +1 -1
- package/dist/pipeline/phases/review.d.ts.map +1 -1
- package/dist/pipeline/phases/review.js +58 -28
- package/dist/pipeline/phases/review.js.map +1 -1
- package/dist/pipeline/phases/role-planning.d.ts.map +1 -1
- package/dist/pipeline/phases/role-planning.js +20 -5
- package/dist/pipeline/phases/role-planning.js.map +1 -1
- package/dist/pipeline/phases/stuck.d.ts.map +1 -1
- package/dist/pipeline/phases/stuck.js +10 -0
- package/dist/pipeline/phases/stuck.js.map +1 -1
- package/dist/pipeline/repo-snapshot.d.ts.map +1 -1
- package/dist/pipeline/repo-snapshot.js +3 -0
- package/dist/pipeline/repo-snapshot.js.map +1 -1
- package/dist/pipeline/role-execution-adapter.d.ts +2 -1
- package/dist/pipeline/role-execution-adapter.d.ts.map +1 -1
- package/dist/pipeline/role-execution-adapter.js +22 -7
- package/dist/pipeline/role-execution-adapter.js.map +1 -1
- package/dist/pipeline/skill-loader.d.ts +19 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -1
- package/dist/pipeline/skill-loader.js +22 -0
- package/dist/pipeline/skill-loader.js.map +1 -1
- package/dist/pipeline/skills/constitution-generator.d.ts +51 -0
- package/dist/pipeline/skills/constitution-generator.d.ts.map +1 -0
- package/dist/pipeline/skills/constitution-generator.js +210 -0
- package/dist/pipeline/skills/constitution-generator.js.map +1 -0
- package/dist/pipeline/skills/coverage-gate.d.ts +44 -0
- package/dist/pipeline/skills/coverage-gate.d.ts.map +1 -0
- package/dist/pipeline/skills/coverage-gate.js +143 -0
- package/dist/pipeline/skills/coverage-gate.js.map +1 -0
- package/dist/pipeline/skills/generator.d.ts +65 -0
- package/dist/pipeline/skills/generator.d.ts.map +1 -0
- package/dist/pipeline/skills/generator.js +221 -0
- package/dist/pipeline/skills/generator.js.map +1 -0
- package/dist/pipeline/skills/role-map.d.ts +38 -0
- package/dist/pipeline/skills/role-map.d.ts.map +1 -0
- package/dist/pipeline/skills/role-map.js +234 -0
- package/dist/pipeline/skills/role-map.js.map +1 -0
- package/dist/pipeline/skills/types.d.ts +47 -0
- package/dist/pipeline/skills/types.d.ts.map +1 -0
- package/dist/pipeline/skills/types.js +5 -0
- package/dist/pipeline/skills/types.js.map +1 -0
- package/dist/pipeline/skills/usage-registry.d.ts +48 -0
- package/dist/pipeline/skills/usage-registry.d.ts.map +1 -0
- package/dist/pipeline/skills/usage-registry.js +55 -0
- package/dist/pipeline/skills/usage-registry.js.map +1 -0
- package/dist/pipeline/strategy-context.d.ts +20 -0
- package/dist/pipeline/strategy-context.d.ts.map +1 -0
- package/dist/pipeline/strategy-context.js +55 -0
- package/dist/pipeline/strategy-context.js.map +1 -0
- package/dist/pipeline/type-defs/artifacts.d.ts +30 -5
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
- package/dist/pipeline/type-defs/artifacts.js +5 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -1
- package/dist/pipeline/type-defs/audit.d.ts +28 -13
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.d.ts +19 -8
- package/dist/pipeline/type-defs/checks.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.js +4 -0
- package/dist/pipeline/type-defs/checks.js.map +1 -1
- package/dist/pipeline/type-defs/packets.d.ts +119 -18
- package/dist/pipeline/type-defs/packets.d.ts.map +1 -1
- package/dist/pipeline/type-defs/packets.js +17 -1
- package/dist/pipeline/type-defs/packets.js.map +1 -1
- package/dist/pipeline/type-defs/state.d.ts +165 -16
- package/dist/pipeline/type-defs/state.d.ts.map +1 -1
- package/dist/pipeline/type-defs/state.js +26 -1
- package/dist/pipeline/type-defs/state.js.map +1 -1
- package/dist/shared/text-utils.d.ts +23 -0
- package/dist/shared/text-utils.d.ts.map +1 -0
- package/dist/shared/text-utils.js +66 -0
- package/dist/shared/text-utils.js.map +1 -0
- package/dist/shared/website-strategy-format.d.ts +18 -0
- package/dist/shared/website-strategy-format.d.ts.map +1 -0
- package/dist/shared/website-strategy-format.js +47 -0
- package/dist/shared/website-strategy-format.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +57 -8
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +1 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +1 -1
- package/dist/types/workflow.d.ts +447 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +3 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +6 -3
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +1 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts.map +1 -1
- package/dist/workflow/website-strategy.js +2 -29
- package/dist/workflow/website-strategy.js.map +1 -1
- package/dist/workflow/website-updater.d.ts.map +1 -1
- package/dist/workflow/website-updater.js +3 -2
- package/dist/workflow/website-updater.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +51 -6
- package/src/adapters/grok.ts +51 -6
- package/src/adapters/openai.ts +53 -5
- package/src/cli/commands/create.ts +1 -1
- package/src/cli/interactive.ts +337 -20
- package/src/generators/all.ts +25 -2
- package/src/generators/doc-parser.ts +75 -15
- package/src/generators/templates/fullstack.ts +1 -1
- package/src/generators/templates/website-components.ts +1 -1
- package/src/generators/templates/website-config.ts +23 -11
- package/src/generators/templates/website-conversion.ts +1 -1
- package/src/generators/templates/website-landing.ts +1 -1
- package/src/generators/templates/website-layout.ts +491 -23
- package/src/generators/templates/website-pricing.ts +1 -1
- package/src/generators/templates/website-sections.ts +1 -1
- package/src/generators/templates/website-seo.ts +4 -1
- package/src/generators/templates/website.ts +3 -0
- package/src/generators/website-content-ai.ts +186 -0
- package/src/generators/website-content-scanner.ts +113 -1
- package/src/generators/website-context.ts +151 -12
- package/src/generators/website-debug.ts +26 -0
- package/src/generators/website.ts +28 -3
- package/src/pipeline/artifact-manager.ts +3 -0
- package/src/pipeline/auto-recovery.ts +283 -0
- package/src/pipeline/change-request.ts +63 -1
- package/src/pipeline/check-runner.ts +141 -2
- package/src/pipeline/command-resolver.ts +34 -2
- package/src/pipeline/consensus/arbitrator-query.ts +101 -0
- package/src/pipeline/consensus/consensus-runner.ts +1099 -42
- package/src/pipeline/cr-lifecycle.ts +103 -0
- package/src/pipeline/gate-engine.ts +36 -8
- package/src/pipeline/migration.ts +5 -30
- package/src/pipeline/orchestrator.ts +367 -16
- package/src/pipeline/packets/consensus-packet-builder.ts +44 -18
- package/src/pipeline/phases/architecture.ts +6 -3
- package/src/pipeline/phases/audit.ts +6 -3
- package/src/pipeline/phases/consensus-architecture.ts +10 -1
- package/src/pipeline/phases/consensus-master-plan.ts +10 -3
- package/src/pipeline/phases/consensus-role-plans.ts +10 -1
- package/src/pipeline/phases/done.ts +15 -4
- package/src/pipeline/phases/intake.ts +67 -14
- package/src/pipeline/phases/phase-context.ts +6 -1
- package/src/pipeline/phases/production-gate.ts +41 -3
- package/src/pipeline/phases/qa-validation.ts +51 -5
- package/src/pipeline/phases/recovery-loop.ts +229 -7
- package/src/pipeline/phases/review.ts +73 -30
- package/src/pipeline/phases/role-planning.ts +23 -5
- package/src/pipeline/phases/stuck.ts +10 -0
- package/src/pipeline/repo-snapshot.ts +3 -0
- package/src/pipeline/role-execution-adapter.ts +30 -4
- package/src/pipeline/skill-loader.ts +33 -0
- package/src/pipeline/skills/constitution-generator.ts +236 -0
- package/src/pipeline/skills/coverage-gate.ts +199 -0
- package/src/pipeline/skills/generator.ts +287 -0
- package/src/pipeline/skills/role-map.ts +248 -0
- package/src/pipeline/skills/types.ts +53 -0
- package/src/pipeline/skills/usage-registry.ts +87 -0
- package/src/pipeline/strategy-context.ts +60 -0
- package/src/pipeline/type-defs/artifacts.ts +5 -0
- package/src/pipeline/type-defs/checks.ts +4 -0
- package/src/pipeline/type-defs/packets.ts +18 -1
- package/src/pipeline/type-defs/state.ts +26 -1
- package/src/shared/text-utils.ts +70 -0
- package/src/shared/website-strategy-format.ts +56 -0
- package/src/state/index.ts +60 -8
- package/src/types/consensus.ts +1 -0
- package/src/types/workflow.ts +6 -0
- package/src/upgrade/handlers.ts +9 -3
- package/src/workflow/consensus.ts +1 -0
- package/src/workflow/website-strategy.ts +2 -36
- package/src/workflow/website-updater.ts +4 -2
- package/tests/adapters/gemini.test.ts +165 -0
- package/tests/adapters/grok.test.ts +137 -0
- package/tests/adapters/openai.test.ts +128 -0
- package/tests/generators/doc-parser.test.ts +88 -9
- package/tests/generators/quality-gate.test.ts +19 -3
- package/tests/generators/website-components.test.ts +34 -0
- package/tests/generators/website-content-ai.test.ts +308 -0
- package/tests/generators/website-content-scanner.test.ts +86 -0
- package/tests/generators/website-context.test.ts +3 -2
- package/tests/integration/smokestack-scaffold.test.ts +385 -0
- package/tests/pipeline/auto-recovery.test.ts +337 -0
- package/tests/pipeline/change-request.test.ts +70 -0
- package/tests/pipeline/command-resolver.test.ts +42 -0
- package/tests/pipeline/consensus/arbitrator-query.test.ts +107 -0
- package/tests/pipeline/consensus-runner.test.ts +1333 -10
- package/tests/pipeline/consensus-scoring.test.ts +602 -18
- package/tests/pipeline/gate-engine.test.ts +34 -0
- package/tests/pipeline/install-check.test.ts +261 -0
- package/tests/pipeline/migration.test.ts +4 -3
- package/tests/pipeline/orchestrator.test.ts +1506 -15
- package/tests/pipeline/packets/builders.test.ts +29 -6
- package/tests/pipeline/phases/role-planning.strategy.test.ts +204 -0
- package/tests/pipeline/pipeline-persistence.test.ts +230 -0
- package/tests/pipeline/recovery-loop-guidance.test.ts +280 -0
- package/tests/pipeline/role-execution-adapter.test.ts +88 -0
- package/tests/pipeline/skills/constitution-generator.test.ts +201 -0
- package/tests/pipeline/skills/coverage-gate.test.ts +370 -0
- package/tests/pipeline/skills/generator.test.ts +213 -0
- package/tests/pipeline/skills/role-map.test.ts +198 -0
- package/tests/pipeline/skills/usage-registry.test.ts +114 -0
- package/tests/pipeline/strategy-context.test.ts +148 -0
- package/tests/shared/text-utils.test.ts +155 -0
- package/tests/state/progress-analysis.test.ts +375 -0
- package/tests/upgrade/handlers.test.ts +33 -2
- package/tests/workflow/consensus.test.ts +6 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Gemini adapter - parseConsensusResponse
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { parseConsensusResponse } from '../../src/adapters/gemini.js';
|
|
7
|
+
|
|
8
|
+
describe('Gemini parseConsensusResponse', () => {
|
|
9
|
+
it('should parse a complete response with blocking issues', () => {
|
|
10
|
+
const response = `
|
|
11
|
+
ANALYSIS:
|
|
12
|
+
Well-structured plan with clear goals.
|
|
13
|
+
|
|
14
|
+
STRENGTHS:
|
|
15
|
+
- Good module separation
|
|
16
|
+
- Clear API design
|
|
17
|
+
|
|
18
|
+
CONCERNS:
|
|
19
|
+
- Consider adding rate limiting
|
|
20
|
+
|
|
21
|
+
BLOCKING_ISSUES:
|
|
22
|
+
- Missing authentication middleware
|
|
23
|
+
- No input validation strategy
|
|
24
|
+
|
|
25
|
+
RECOMMENDATIONS:
|
|
26
|
+
- Add rate limiting middleware
|
|
27
|
+
|
|
28
|
+
CONSENSUS: 72%
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const result = parseConsensusResponse(response);
|
|
32
|
+
|
|
33
|
+
expect(result.score).toBe(72);
|
|
34
|
+
expect(result.approved).toBe(false);
|
|
35
|
+
expect(result.concerns).toEqual(['Consider adding rate limiting']);
|
|
36
|
+
expect(result.blockingIssues).toEqual([
|
|
37
|
+
'Missing authentication middleware',
|
|
38
|
+
'No input validation strategy',
|
|
39
|
+
]);
|
|
40
|
+
expect(result.recommendations).toEqual(['Add rate limiting middleware']);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return empty blocking issues when "None"', () => {
|
|
44
|
+
const response = `
|
|
45
|
+
ANALYSIS:
|
|
46
|
+
Excellent plan.
|
|
47
|
+
|
|
48
|
+
STRENGTHS:
|
|
49
|
+
- Comprehensive coverage
|
|
50
|
+
|
|
51
|
+
CONCERNS:
|
|
52
|
+
- Minor naming inconsistencies
|
|
53
|
+
|
|
54
|
+
BLOCKING_ISSUES:
|
|
55
|
+
- None
|
|
56
|
+
|
|
57
|
+
RECOMMENDATIONS:
|
|
58
|
+
- Standardize naming conventions
|
|
59
|
+
|
|
60
|
+
CONSENSUS: 96%
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const result = parseConsensusResponse(response);
|
|
64
|
+
|
|
65
|
+
expect(result.score).toBe(96);
|
|
66
|
+
expect(result.approved).toBe(true);
|
|
67
|
+
expect(result.blockingIssues).toEqual([]);
|
|
68
|
+
expect(result.concerns).toHaveLength(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should handle missing BLOCKING_ISSUES section (backward compat)', () => {
|
|
72
|
+
const response = `
|
|
73
|
+
ANALYSIS:
|
|
74
|
+
Good plan overall.
|
|
75
|
+
|
|
76
|
+
STRENGTHS:
|
|
77
|
+
- Solid architecture design
|
|
78
|
+
|
|
79
|
+
CONCERNS:
|
|
80
|
+
- Some performance issue to watch
|
|
81
|
+
|
|
82
|
+
RECOMMENDATIONS:
|
|
83
|
+
- Optimize database queries
|
|
84
|
+
|
|
85
|
+
CONSENSUS: 88%
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
const result = parseConsensusResponse(response);
|
|
89
|
+
|
|
90
|
+
expect(result.blockingIssues).toEqual([]);
|
|
91
|
+
expect(result.score).toBe(88);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should filter none-variant blocking issues like "No blocking issues found"', () => {
|
|
95
|
+
const response = `
|
|
96
|
+
ANALYSIS:
|
|
97
|
+
Good plan with solid foundation.
|
|
98
|
+
|
|
99
|
+
STRENGTHS:
|
|
100
|
+
- Clean design
|
|
101
|
+
|
|
102
|
+
CONCERNS:
|
|
103
|
+
- Minor performance concern
|
|
104
|
+
|
|
105
|
+
BLOCKING_ISSUES:
|
|
106
|
+
- No blocking issues found
|
|
107
|
+
|
|
108
|
+
RECOMMENDATIONS:
|
|
109
|
+
- Optimize queries
|
|
110
|
+
|
|
111
|
+
CONSENSUS: 91%
|
|
112
|
+
`;
|
|
113
|
+
const result = parseConsensusResponse(response);
|
|
114
|
+
expect(result.blockingIssues).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should filter "None identified" from blocking issues', () => {
|
|
118
|
+
const response = `
|
|
119
|
+
ANALYSIS:
|
|
120
|
+
Thorough review complete.
|
|
121
|
+
|
|
122
|
+
STRENGTHS:
|
|
123
|
+
- Well-structured code
|
|
124
|
+
|
|
125
|
+
CONCERNS:
|
|
126
|
+
- Could use more tests
|
|
127
|
+
|
|
128
|
+
BLOCKING_ISSUES:
|
|
129
|
+
- None identified
|
|
130
|
+
|
|
131
|
+
RECOMMENDATIONS:
|
|
132
|
+
- Add integration tests
|
|
133
|
+
|
|
134
|
+
CONSENSUS: 89%
|
|
135
|
+
`;
|
|
136
|
+
const result = parseConsensusResponse(response);
|
|
137
|
+
expect(result.blockingIssues).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle ALL-CAPS format for blocking issues', () => {
|
|
141
|
+
const response = `
|
|
142
|
+
ANALYSIS:
|
|
143
|
+
Detailed review of the plan.
|
|
144
|
+
|
|
145
|
+
STRENGTHS:
|
|
146
|
+
- Good structure
|
|
147
|
+
|
|
148
|
+
CONCERNS:
|
|
149
|
+
- Minor issue noted
|
|
150
|
+
|
|
151
|
+
BLOCKING_ISSUES:
|
|
152
|
+
- Critical security flaw
|
|
153
|
+
|
|
154
|
+
RECOMMENDATIONS:
|
|
155
|
+
- Fix security
|
|
156
|
+
|
|
157
|
+
CONSENSUS: 60%
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
const result = parseConsensusResponse(response);
|
|
161
|
+
|
|
162
|
+
expect(result.blockingIssues).toEqual(['Critical security flaw']);
|
|
163
|
+
expect(result.recommendations).toEqual(['Fix security']);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Grok adapter - parseConsensusResponse
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { parseConsensusResponse } from '../../src/adapters/grok.js';
|
|
7
|
+
|
|
8
|
+
describe('Grok parseConsensusResponse', () => {
|
|
9
|
+
it('should parse a complete response with blocking issues', () => {
|
|
10
|
+
const response = `
|
|
11
|
+
ANALYSIS:
|
|
12
|
+
Solid plan with room for improvement.
|
|
13
|
+
|
|
14
|
+
STRENGTHS:
|
|
15
|
+
- Clean API design
|
|
16
|
+
- Good test coverage plan
|
|
17
|
+
|
|
18
|
+
CONCERNS:
|
|
19
|
+
- Consider adding monitoring
|
|
20
|
+
|
|
21
|
+
BLOCKING_ISSUES:
|
|
22
|
+
- Missing database migration strategy
|
|
23
|
+
- No rollback plan defined
|
|
24
|
+
|
|
25
|
+
RECOMMENDATIONS:
|
|
26
|
+
- Add observability layer
|
|
27
|
+
|
|
28
|
+
CONSENSUS: 70%
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const result = parseConsensusResponse(response);
|
|
32
|
+
|
|
33
|
+
expect(result.score).toBe(70);
|
|
34
|
+
expect(result.approved).toBe(false);
|
|
35
|
+
expect(result.concerns).toEqual(['Consider adding monitoring']);
|
|
36
|
+
expect(result.blockingIssues).toEqual([
|
|
37
|
+
'Missing database migration strategy',
|
|
38
|
+
'No rollback plan defined',
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return empty blocking issues when "None"', () => {
|
|
43
|
+
const response = `
|
|
44
|
+
ANALYSIS:
|
|
45
|
+
Great plan.
|
|
46
|
+
|
|
47
|
+
STRENGTHS:
|
|
48
|
+
- Well-structured
|
|
49
|
+
|
|
50
|
+
CONCERNS:
|
|
51
|
+
- Minor style inconsistencies
|
|
52
|
+
|
|
53
|
+
BLOCKING_ISSUES:
|
|
54
|
+
- None
|
|
55
|
+
|
|
56
|
+
RECOMMENDATIONS:
|
|
57
|
+
- Apply consistent formatting
|
|
58
|
+
|
|
59
|
+
CONSENSUS: 95%
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const result = parseConsensusResponse(response);
|
|
63
|
+
|
|
64
|
+
expect(result.score).toBe(95);
|
|
65
|
+
expect(result.approved).toBe(true);
|
|
66
|
+
expect(result.blockingIssues).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should filter none-variant blocking issues like "No blocking issues found"', () => {
|
|
70
|
+
const response = `
|
|
71
|
+
ANALYSIS:
|
|
72
|
+
Solid implementation plan.
|
|
73
|
+
|
|
74
|
+
STRENGTHS:
|
|
75
|
+
- Good architecture
|
|
76
|
+
|
|
77
|
+
CONCERNS:
|
|
78
|
+
- Consider monitoring
|
|
79
|
+
|
|
80
|
+
BLOCKING_ISSUES:
|
|
81
|
+
- No blocking issues found
|
|
82
|
+
|
|
83
|
+
RECOMMENDATIONS:
|
|
84
|
+
- Add monitoring
|
|
85
|
+
|
|
86
|
+
CONSENSUS: 93%
|
|
87
|
+
`;
|
|
88
|
+
const result = parseConsensusResponse(response);
|
|
89
|
+
expect(result.blockingIssues).toEqual([]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should filter "None identified" from blocking issues', () => {
|
|
93
|
+
const response = `
|
|
94
|
+
ANALYSIS:
|
|
95
|
+
Well-considered approach.
|
|
96
|
+
|
|
97
|
+
STRENGTHS:
|
|
98
|
+
- Comprehensive coverage
|
|
99
|
+
|
|
100
|
+
CONCERNS:
|
|
101
|
+
- Minor style issues
|
|
102
|
+
|
|
103
|
+
BLOCKING_ISSUES:
|
|
104
|
+
- None identified
|
|
105
|
+
|
|
106
|
+
RECOMMENDATIONS:
|
|
107
|
+
- Standardize formatting
|
|
108
|
+
|
|
109
|
+
CONSENSUS: 91%
|
|
110
|
+
`;
|
|
111
|
+
const result = parseConsensusResponse(response);
|
|
112
|
+
expect(result.blockingIssues).toEqual([]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should handle missing BLOCKING_ISSUES section (backward compat)', () => {
|
|
116
|
+
const response = `
|
|
117
|
+
ANALYSIS:
|
|
118
|
+
Reasonable plan.
|
|
119
|
+
|
|
120
|
+
STRENGTHS:
|
|
121
|
+
- Good overall approach
|
|
122
|
+
|
|
123
|
+
CONCERNS:
|
|
124
|
+
- Needs more detail
|
|
125
|
+
|
|
126
|
+
RECOMMENDATIONS:
|
|
127
|
+
- Expand on implementation details
|
|
128
|
+
|
|
129
|
+
CONSENSUS: 82%
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
const result = parseConsensusResponse(response);
|
|
133
|
+
|
|
134
|
+
expect(result.blockingIssues).toEqual([]);
|
|
135
|
+
expect(result.score).toBe(82);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -22,6 +22,9 @@ CONCERNS:
|
|
|
22
22
|
- No deployment plan
|
|
23
23
|
- Security considerations not addressed
|
|
24
24
|
|
|
25
|
+
BLOCKING_ISSUES:
|
|
26
|
+
- None
|
|
27
|
+
|
|
25
28
|
RECOMMENDATIONS:
|
|
26
29
|
- Add error handling middleware
|
|
27
30
|
- Include deployment configuration
|
|
@@ -37,6 +40,7 @@ CONSENSUS: 85%
|
|
|
37
40
|
expect(result.analysis).toContain('well-structured plan');
|
|
38
41
|
expect(result.strengths).toContain('Clear project structure');
|
|
39
42
|
expect(result.concerns).toContain('Missing error handling strategy');
|
|
43
|
+
expect(result.blockingIssues).toEqual([]);
|
|
40
44
|
expect(result.recommendations).toContain('Add error handling middleware');
|
|
41
45
|
expect(result.rawResponse).toBe(response);
|
|
42
46
|
});
|
|
@@ -52,6 +56,9 @@ STRENGTHS:
|
|
|
52
56
|
CONCERNS:
|
|
53
57
|
- None
|
|
54
58
|
|
|
59
|
+
BLOCKING_ISSUES:
|
|
60
|
+
- None
|
|
61
|
+
|
|
55
62
|
RECOMMENDATIONS:
|
|
56
63
|
- None needed
|
|
57
64
|
|
|
@@ -62,6 +69,7 @@ CONSENSUS: 98%
|
|
|
62
69
|
|
|
63
70
|
expect(result.score).toBe(98);
|
|
64
71
|
expect(result.approved).toBe(true);
|
|
72
|
+
expect(result.blockingIssues).toEqual([]);
|
|
65
73
|
});
|
|
66
74
|
|
|
67
75
|
it('should handle missing sections gracefully', () => {
|
|
@@ -78,6 +86,7 @@ CONSENSUS: 70%
|
|
|
78
86
|
expect(result.analysis).toBe('');
|
|
79
87
|
expect(result.strengths).toEqual([]);
|
|
80
88
|
expect(result.concerns).toEqual([]);
|
|
89
|
+
expect(result.blockingIssues).toEqual([]);
|
|
81
90
|
});
|
|
82
91
|
|
|
83
92
|
it('should handle missing score', () => {
|
|
@@ -110,6 +119,9 @@ CONCERNS:
|
|
|
110
119
|
1. First concern
|
|
111
120
|
2. Second concern
|
|
112
121
|
|
|
122
|
+
BLOCKING_ISSUES:
|
|
123
|
+
- None
|
|
124
|
+
|
|
113
125
|
RECOMMENDATIONS:
|
|
114
126
|
- Recommendation one
|
|
115
127
|
- Recommendation two
|
|
@@ -121,6 +133,7 @@ CONSENSUS: 80%
|
|
|
121
133
|
|
|
122
134
|
expect(result.strengths).toHaveLength(4);
|
|
123
135
|
expect(result.concerns).toHaveLength(2);
|
|
136
|
+
expect(result.blockingIssues).toEqual([]);
|
|
124
137
|
expect(result.recommendations).toHaveLength(2);
|
|
125
138
|
});
|
|
126
139
|
|
|
@@ -142,4 +155,119 @@ CONSENSUS: 80%
|
|
|
142
155
|
expect(parseConsensusResponse('CONSENSUS: 0%').score).toBe(0);
|
|
143
156
|
expect(parseConsensusResponse('CONSENSUS: 100%').score).toBe(100);
|
|
144
157
|
});
|
|
158
|
+
|
|
159
|
+
it('should parse blocking issues as separate field', () => {
|
|
160
|
+
const response = `
|
|
161
|
+
ANALYSIS: Good plan.
|
|
162
|
+
|
|
163
|
+
STRENGTHS:
|
|
164
|
+
- Solid architecture
|
|
165
|
+
|
|
166
|
+
CONCERNS:
|
|
167
|
+
- Consider adding caching
|
|
168
|
+
|
|
169
|
+
BLOCKING_ISSUES:
|
|
170
|
+
- Missing authentication flow
|
|
171
|
+
- No database migration strategy
|
|
172
|
+
|
|
173
|
+
RECOMMENDATIONS:
|
|
174
|
+
- Add caching layer
|
|
175
|
+
|
|
176
|
+
CONSENSUS: 72%
|
|
177
|
+
`;
|
|
178
|
+
const result = parseConsensusResponse(response);
|
|
179
|
+
expect(result.blockingIssues).toEqual([
|
|
180
|
+
'Missing authentication flow',
|
|
181
|
+
'No database migration strategy',
|
|
182
|
+
]);
|
|
183
|
+
expect(result.concerns).toEqual(['Consider adding caching']);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should return empty blocking issues when "None"', () => {
|
|
187
|
+
const response = `
|
|
188
|
+
ANALYSIS: Good plan.
|
|
189
|
+
|
|
190
|
+
STRENGTHS:
|
|
191
|
+
- Solid
|
|
192
|
+
|
|
193
|
+
CONCERNS:
|
|
194
|
+
- Minor naming issues in the module structure
|
|
195
|
+
|
|
196
|
+
BLOCKING_ISSUES:
|
|
197
|
+
- None
|
|
198
|
+
|
|
199
|
+
RECOMMENDATIONS:
|
|
200
|
+
- Improve module names
|
|
201
|
+
|
|
202
|
+
CONSENSUS: 95%
|
|
203
|
+
`;
|
|
204
|
+
const result = parseConsensusResponse(response);
|
|
205
|
+
expect(result.blockingIssues).toEqual([]);
|
|
206
|
+
expect(result.approved).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should filter none-variant blocking issues like "No blocking issues found"', () => {
|
|
210
|
+
const response = `
|
|
211
|
+
ANALYSIS: Good plan.
|
|
212
|
+
|
|
213
|
+
STRENGTHS:
|
|
214
|
+
- Solid architecture
|
|
215
|
+
|
|
216
|
+
CONCERNS:
|
|
217
|
+
- Minor naming issues
|
|
218
|
+
|
|
219
|
+
BLOCKING_ISSUES:
|
|
220
|
+
- No blocking issues found
|
|
221
|
+
|
|
222
|
+
RECOMMENDATIONS:
|
|
223
|
+
- Improve naming
|
|
224
|
+
|
|
225
|
+
CONSENSUS: 92%
|
|
226
|
+
`;
|
|
227
|
+
const result = parseConsensusResponse(response);
|
|
228
|
+
expect(result.blockingIssues).toEqual([]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should filter "None identified" from blocking issues', () => {
|
|
232
|
+
const response = `
|
|
233
|
+
ANALYSIS: Solid plan overall.
|
|
234
|
+
|
|
235
|
+
STRENGTHS:
|
|
236
|
+
- Good design choices
|
|
237
|
+
|
|
238
|
+
CONCERNS:
|
|
239
|
+
- Consider caching
|
|
240
|
+
|
|
241
|
+
BLOCKING_ISSUES:
|
|
242
|
+
- None identified
|
|
243
|
+
|
|
244
|
+
RECOMMENDATIONS:
|
|
245
|
+
- Add caching layer
|
|
246
|
+
|
|
247
|
+
CONSENSUS: 90%
|
|
248
|
+
`;
|
|
249
|
+
const result = parseConsensusResponse(response);
|
|
250
|
+
expect(result.blockingIssues).toEqual([]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle missing BLOCKING_ISSUES section (backward compat)', () => {
|
|
254
|
+
// Old-format response without BLOCKING_ISSUES section
|
|
255
|
+
const response = `
|
|
256
|
+
ANALYSIS: Good plan.
|
|
257
|
+
|
|
258
|
+
STRENGTHS:
|
|
259
|
+
- Fine overall structure
|
|
260
|
+
|
|
261
|
+
CONCERNS:
|
|
262
|
+
- Some concern about performance
|
|
263
|
+
|
|
264
|
+
RECOMMENDATIONS:
|
|
265
|
+
- A recommendation for optimization
|
|
266
|
+
|
|
267
|
+
CONSENSUS: 90%
|
|
268
|
+
`;
|
|
269
|
+
const result = parseConsensusResponse(response);
|
|
270
|
+
expect(result.blockingIssues).toEqual([]);
|
|
271
|
+
expect(result.concerns).toHaveLength(1);
|
|
272
|
+
});
|
|
145
273
|
});
|
|
@@ -104,18 +104,97 @@ describe('isSuspiciousProductName', () => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
describe('extractPricing', () => {
|
|
107
|
-
it('extracts pricing tiers from markdown table', () => {
|
|
107
|
+
it('extracts pricing tiers from markdown table with provenance', () => {
|
|
108
108
|
const docs = `## Pricing\n| Plan | Price |\n|---|---|\n| Free | Free |\n| Pro | $99/month minimum |\n| Enterprise | Custom pricing |`;
|
|
109
|
-
const
|
|
110
|
-
expect(
|
|
111
|
-
expect(tiers
|
|
112
|
-
expect(tiers
|
|
113
|
-
expect(tiers
|
|
114
|
-
expect(tiers
|
|
109
|
+
const result = extractPricing(docs);
|
|
110
|
+
expect(result.source).toBe('docs');
|
|
111
|
+
expect(result.tiers.length).toBe(3);
|
|
112
|
+
expect(result.tiers[0].name).toContain('Free');
|
|
113
|
+
expect(result.tiers[1].price).toBe('$99');
|
|
114
|
+
expect(result.tiers[2].price).toBe('Custom');
|
|
115
|
+
expect(result.evidence).toBeDefined();
|
|
116
|
+
expect(result.evidence!.extractionMethod).toBe('known_plan_names');
|
|
117
|
+
expect(result.evidence!.matchedRows).toBe(3);
|
|
115
118
|
});
|
|
116
119
|
|
|
117
|
-
it('returns
|
|
120
|
+
it('returns source none when no pricing found', () => {
|
|
118
121
|
const docs = '# Product\nJust a product.';
|
|
119
|
-
|
|
122
|
+
const result = extractPricing(docs);
|
|
123
|
+
expect(result.source).toBe('none');
|
|
124
|
+
expect(result.tiers).toEqual([]);
|
|
125
|
+
expect(result.evidence).toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('extracts Gateco-style pricing with emoji-prefixed rows and <br> tags', () => {
|
|
129
|
+
const docs = [
|
|
130
|
+
'## Pricing Overview',
|
|
131
|
+
'| Plan | Price | Users |',
|
|
132
|
+
'|---|---|---|',
|
|
133
|
+
'| Free | Free | Up to 5 |',
|
|
134
|
+
'| Pro | $99/month<br>minimum | Unlimited |',
|
|
135
|
+
'| Enterprise | Custom | Unlimited |',
|
|
136
|
+
].join('\n');
|
|
137
|
+
const result = extractPricing(docs);
|
|
138
|
+
expect(result.source).toBe('docs');
|
|
139
|
+
expect(result.tiers.length).toBe(3);
|
|
140
|
+
expect(result.tiers[0].name).toBe('Free');
|
|
141
|
+
expect(result.tiers[0].price).toBe('Free');
|
|
142
|
+
expect(result.tiers[1].name).toBe('Pro');
|
|
143
|
+
expect(result.tiers[1].price).toBe('$99');
|
|
144
|
+
expect(result.tiers[2].name).toBe('Enterprise');
|
|
145
|
+
expect(result.tiers[2].price).toBe('Custom');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('falls back to table_fallback for nonstandard plan names', () => {
|
|
149
|
+
const docs = [
|
|
150
|
+
'## Pricing',
|
|
151
|
+
'| Plan | Price |',
|
|
152
|
+
'|---|---|',
|
|
153
|
+
'| Hobby | Free |',
|
|
154
|
+
'| Scale | $49/mo |',
|
|
155
|
+
'| Organization | Custom |',
|
|
156
|
+
].join('\n');
|
|
157
|
+
const result = extractPricing(docs);
|
|
158
|
+
expect(result.source).toBe('docs');
|
|
159
|
+
expect(result.tiers.length).toBe(3);
|
|
160
|
+
expect(result.evidence!.extractionMethod).toBe('table_fallback');
|
|
161
|
+
expect(result.tiers[0].name).toBe('Hobby');
|
|
162
|
+
expect(result.tiers[1].name).toBe('Scale');
|
|
163
|
+
expect(result.tiers[2].name).toBe('Organization');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('detects price in 3rd column via header scan', () => {
|
|
167
|
+
const docs = [
|
|
168
|
+
'## Pricing',
|
|
169
|
+
'| Plan | Features | Price |',
|
|
170
|
+
'|---|---|---|',
|
|
171
|
+
'| Hobby | 5 projects | Free |',
|
|
172
|
+
'| Scale | Unlimited | $49/mo |',
|
|
173
|
+
].join('\n');
|
|
174
|
+
const result = extractPricing(docs);
|
|
175
|
+
expect(result.source).toBe('docs');
|
|
176
|
+
expect(result.tiers.length).toBe(2);
|
|
177
|
+
expect(result.tiers[0].price).toBe('Free');
|
|
178
|
+
expect(result.tiers[1].price).toBe('$49');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('skips separator rows and header-like rows in fallback', () => {
|
|
182
|
+
const docs = [
|
|
183
|
+
'## Pricing',
|
|
184
|
+
'| Plan | Price |',
|
|
185
|
+
'|---|---|',
|
|
186
|
+
'| Plan | $10 |', // header-like row, should skip
|
|
187
|
+
'| Hobby | Free |',
|
|
188
|
+
].join('\n');
|
|
189
|
+
const result = extractPricing(docs);
|
|
190
|
+
// "Plan" row should be skipped by the /^(Plan|Tier|Name|Feature)/i filter
|
|
191
|
+
expect(result.tiers.every((t) => t.name !== 'Plan')).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('returns empty tiers for pricing section with no table', () => {
|
|
195
|
+
const docs = '## Pricing\nContact us for pricing details.';
|
|
196
|
+
const result = extractPricing(docs);
|
|
197
|
+
expect(result.source).toBe('none');
|
|
198
|
+
expect(result.tiers).toEqual([]);
|
|
120
199
|
});
|
|
121
200
|
});
|
|
@@ -75,7 +75,7 @@ describe('validateWebsiteContextOrThrow', () => {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
it('still throws when passed is false from validateWebsiteContext', () => {
|
|
78
|
-
// Context with multiple blocking issues
|
|
78
|
+
// Context with multiple blocking issues (strategy absence is not a factor)
|
|
79
79
|
const context = makeContext({
|
|
80
80
|
productName: 'my-app',
|
|
81
81
|
rawDocs: '',
|
|
@@ -87,6 +87,13 @@ describe('validateWebsiteContextOrThrow', () => {
|
|
|
87
87
|
/Website generation blocked/
|
|
88
88
|
);
|
|
89
89
|
});
|
|
90
|
+
|
|
91
|
+
it('does not throw on missing strategy alone', () => {
|
|
92
|
+
// Strategy absence should never cause validation failure
|
|
93
|
+
const context = makeContext({ strategy: undefined });
|
|
94
|
+
|
|
95
|
+
expect(() => validateWebsiteContextOrThrow(context, 'gateco')).not.toThrow();
|
|
96
|
+
});
|
|
90
97
|
});
|
|
91
98
|
|
|
92
99
|
describe('validateWebsiteContext (soft mode)', () => {
|
|
@@ -168,7 +175,7 @@ describe('validateWebsiteContext (soft mode)', () => {
|
|
|
168
175
|
expect(result.warnings.some((w) => /description/i.test(w))).toBe(true);
|
|
169
176
|
});
|
|
170
177
|
|
|
171
|
-
it('clamps score to
|
|
178
|
+
it('clamps score to floor when everything is wrong', () => {
|
|
172
179
|
const context: WebsiteContentContext = {
|
|
173
180
|
productName: 'my-app',
|
|
174
181
|
features: [],
|
|
@@ -177,7 +184,16 @@ describe('validateWebsiteContext (soft mode)', () => {
|
|
|
177
184
|
};
|
|
178
185
|
const result = validateWebsiteContext(context, 'my-app');
|
|
179
186
|
|
|
180
|
-
|
|
187
|
+
// Score is low but no longer 0 since strategy penalty (-15) was removed
|
|
188
|
+
expect(result.contentScore).toBeLessThanOrEqual(15);
|
|
181
189
|
expect(result.passed).toBe(false);
|
|
182
190
|
});
|
|
191
|
+
|
|
192
|
+
it('does not emit strategy-related issues when strategy is undefined', () => {
|
|
193
|
+
const context = makeContext({ strategy: undefined });
|
|
194
|
+
const result = validateWebsiteContext(context, 'gateco');
|
|
195
|
+
|
|
196
|
+
// Strategy absence should not produce any issues or warnings about strategy
|
|
197
|
+
expect(result.issues.some((i) => /strategy/i.test(i))).toBe(false);
|
|
198
|
+
});
|
|
183
199
|
});
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
generateWebsiteFooter,
|
|
10
10
|
generateWebsiteNavigation,
|
|
11
11
|
} from '../../src/generators/templates/website-components.js';
|
|
12
|
+
import { getWebsiteProjectFiles } from '../../src/generators/website.js';
|
|
12
13
|
import type { WebsiteContentContext } from '../../src/generators/website-context.js';
|
|
13
14
|
import type { WebsiteStrategyDocument } from '../../src/types/website-strategy.js';
|
|
14
15
|
|
|
@@ -132,6 +133,39 @@ describe('generateWebsiteFooter', () => {
|
|
|
132
133
|
expect(footer).toContain('Resources');
|
|
133
134
|
expect(footer).toContain('Legal');
|
|
134
135
|
});
|
|
136
|
+
|
|
137
|
+
it('all default footer hrefs have corresponding generated pages', () => {
|
|
138
|
+
const footer = generateWebsiteFooter('deploy-ai', contextNoLogo);
|
|
139
|
+
const generatedFiles = getWebsiteProjectFiles('deploy-ai');
|
|
140
|
+
|
|
141
|
+
// Extract all href values from the default footer
|
|
142
|
+
const hrefPattern = /href:\s*'([^']+)'/g;
|
|
143
|
+
let match;
|
|
144
|
+
const hrefs: string[] = [];
|
|
145
|
+
while ((match = hrefPattern.exec(footer)) !== null) {
|
|
146
|
+
hrefs.push(match[1]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Build set of generated page routes from file paths
|
|
150
|
+
const pageRoutes = new Set<string>();
|
|
151
|
+
for (const file of generatedFiles) {
|
|
152
|
+
const pageMatch = file.match(/^src\/app(.*)\/page\.tsx$/);
|
|
153
|
+
if (pageMatch) {
|
|
154
|
+
pageRoutes.add(pageMatch[1] || '/');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Also add root route
|
|
158
|
+
if (generatedFiles.includes('src/app/page.tsx')) {
|
|
159
|
+
pageRoutes.add('/');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Every non-anchor internal href should correspond to a generated page
|
|
163
|
+
for (const href of hrefs) {
|
|
164
|
+
if (href.startsWith('/#')) continue; // Anchor links are fine
|
|
165
|
+
if (!href.startsWith('/')) continue; // External links are fine
|
|
166
|
+
expect(pageRoutes.has(href)).toBe(true);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
135
169
|
});
|
|
136
170
|
|
|
137
171
|
describe('generateWebsiteNavigation', () => {
|