api-tests-coverage 1.0.21 → 1.0.23

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.
Files changed (169) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-BKGHUeDJ.js +1 -0
  2. package/dist/dashboard/dist/assets/_basePickBy-BamgEusj.js +1 -0
  3. package/dist/dashboard/dist/assets/_baseUniq-BBhq12Ja.js +1 -0
  4. package/dist/dashboard/dist/assets/_baseUniq-CtA-DQF7.js +1 -0
  5. package/dist/dashboard/dist/assets/arc-CbXP3Mc9.js +1 -0
  6. package/dist/dashboard/dist/assets/arc-Dh-qL1ea.js +1 -0
  7. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-BxQ_anmt.js +36 -0
  8. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-n7QxasMM.js +36 -0
  9. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-Krm3lc7z.js +122 -0
  10. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-MXnGwKRn.js +122 -0
  11. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B3yZ5P9k.js +10 -0
  12. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-Cfd4OeWg.js +10 -0
  13. package/dist/dashboard/dist/assets/channel-C7QwX7U8.js +1 -0
  14. package/dist/dashboard/dist/assets/channel-a4t2URTe.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-Dw3El5KF.js +1 -0
  16. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-ljBQ5lHA.js +1 -0
  17. package/dist/dashboard/dist/assets/chunk-55IACEB6-Cikrdc3Q.js +1 -0
  18. package/dist/dashboard/dist/assets/chunk-55IACEB6-T8Jf00IL.js +1 -0
  19. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-DAJalKYJ.js +165 -0
  20. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-dtHgbkmj.js +165 -0
  21. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-Cv3hm2Ke.js +220 -0
  22. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-p7o_KuDF.js +220 -0
  23. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-B0wUAfQR.js +15 -0
  24. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-Ds1_OqKH.js +15 -0
  25. package/dist/dashboard/dist/assets/chunk-QN33PNHL-B6zkzIAo.js +1 -0
  26. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BTFwTEw8.js +1 -0
  27. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-C7xuA6tl.js +1 -0
  28. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CNXHnjkC.js +1 -0
  29. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BIVywBYp.js +1 -0
  30. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-D_ea_wdP.js +1 -0
  31. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-B6SxXE6T.js +1 -0
  32. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dkb-G0UK.js +1 -0
  33. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-B6SxXE6T.js +1 -0
  34. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dkb-G0UK.js +1 -0
  35. package/dist/dashboard/dist/assets/clone-BSdXhKmH.js +1 -0
  36. package/dist/dashboard/dist/assets/clone-kcKg1tUH.js +1 -0
  37. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DasyAK5c.js +1 -0
  38. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DuALhY5a.js +1 -0
  39. package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
  40. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-m-5bs635.js +4 -0
  41. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-w6endfy9.js +4 -0
  42. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-BDnUwPQC.js +24 -0
  43. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-CYFwwEdy.js +24 -0
  44. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-Cbb7ctAo.js +43 -0
  45. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-m4Fda1GA.js +43 -0
  46. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-76ascveh.js +24 -0
  47. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-BDVk4AKU.js +24 -0
  48. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-3-jAbxQ6.js +60 -0
  49. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BQQnVF0J.js +60 -0
  50. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Cfv1hkQB.js +162 -0
  51. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-dKukiSxD.js +162 -0
  52. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-B7GZPGck.js +267 -0
  53. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BqnazZuZ.js +267 -0
  54. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BUFcnXCj.js +65 -0
  55. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-K8X-_4av.js +65 -0
  56. package/dist/dashboard/dist/assets/graph-CGQIvL3r.js +1 -0
  57. package/dist/dashboard/dist/assets/graph-CfuGK9GG.js +1 -0
  58. package/dist/dashboard/dist/assets/index-CadOHtae.css +1 -0
  59. package/dist/dashboard/dist/assets/index-DS-KIxwV.js +777 -0
  60. package/dist/dashboard/dist/assets/index-DwqfA4mc.js +777 -0
  61. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BdylFPLI.js +2 -0
  62. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-CaIaIUhT.js +2 -0
  63. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BzJTBkhp.js +139 -0
  64. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-D6dwPswq.js +139 -0
  65. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-CERyhhrH.js +89 -0
  66. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-StKomgio.js +89 -0
  67. package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
  68. package/dist/dashboard/dist/assets/layout-BnsX73QS.js +1 -0
  69. package/dist/dashboard/dist/assets/layout-v7cCi3Fl.js +1 -0
  70. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-BvNtTz8N.js +68 -0
  71. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-HqFRhVFX.js +68 -0
  72. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-2I2tFH0C.js +30 -0
  73. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Cjg80C_b.js +30 -0
  74. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-PlELkdBW.js +7 -0
  75. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-YtFFUYGD.js +7 -0
  76. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DLV2LTE5.js +64 -0
  77. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-_9eLQNcZ.js +64 -0
  78. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-C6_Urrii.js +10 -0
  79. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DHl1Ss7O.js +10 -0
  80. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-B9p0m3Uf.js +145 -0
  81. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-D33UwAtz.js +145 -0
  82. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC--rpDODjT.js +1 -0
  83. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DSp83t9D.js +1 -0
  84. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Ca9uGk46.js +1 -0
  85. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-OTWrEpQO.js +1 -0
  86. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-BCHaGBHB.js +61 -0
  87. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-D5Bb3Jj7.js +61 -0
  88. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CKbkkwye.js +162 -0
  89. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CgiqDY8M.js +162 -0
  90. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BMvBBbeV.js +7 -0
  91. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-C_Tlzchx.js +7 -0
  92. package/dist/dashboard/dist/index.html +14 -0
  93. package/dist/dashboard/dist/reports/business-coverage.json +201 -0
  94. package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
  95. package/dist/dashboard/dist/reports/coverage-summary.json +995 -0
  96. package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
  97. package/dist/dashboard/dist/reports/error-coverage.json +367 -0
  98. package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
  99. package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
  100. package/dist/dashboard/dist/reports/security-coverage.json +299 -0
  101. package/dist/dashboard/dist/vite.svg +1 -0
  102. package/dist/src/generation/context-builder.d.ts +6 -0
  103. package/dist/src/generation/context-builder.d.ts.map +1 -0
  104. package/dist/src/generation/context-builder.js +202 -0
  105. package/dist/src/generation/engine.d.ts +40 -0
  106. package/dist/src/generation/engine.d.ts.map +1 -0
  107. package/dist/src/generation/engine.js +378 -0
  108. package/dist/src/generation/file-router.d.ts +7 -0
  109. package/dist/src/generation/file-router.d.ts.map +1 -0
  110. package/dist/src/generation/file-router.js +79 -0
  111. package/dist/src/generation/gap-extractor.d.ts +12 -0
  112. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  113. package/dist/src/generation/gap-extractor.js +281 -0
  114. package/dist/src/generation/template-renderer.d.ts +10 -0
  115. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  116. package/dist/src/generation/template-renderer.js +526 -0
  117. package/dist/src/generation/types.d.ts +73 -0
  118. package/dist/src/generation/types.d.ts.map +1 -0
  119. package/dist/src/generation/types.js +2 -0
  120. package/dist/src/index.js +154 -0
  121. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts +21 -0
  122. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts.map +1 -0
  123. package/dist/src/pipeline/detectors/expressMiddlewareDetector.js +201 -0
  124. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts +23 -0
  125. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts.map +1 -0
  126. package/dist/src/pipeline/detectors/flaskBlueprintDetector.js +263 -0
  127. package/dist/src/pipeline/detectors/springDddDetector.d.ts +23 -0
  128. package/dist/src/pipeline/detectors/springDddDetector.d.ts.map +1 -0
  129. package/dist/src/pipeline/detectors/springDddDetector.js +237 -0
  130. package/dist/src/pipeline/detectors/types.d.ts +97 -0
  131. package/dist/src/pipeline/detectors/types.d.ts.map +1 -0
  132. package/dist/src/pipeline/detectors/types.js +15 -0
  133. package/dist/src/reporting.d.ts.map +1 -1
  134. package/dist/src/reporting.js +2 -1
  135. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts +5 -0
  136. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts.map +1 -0
  137. package/dist/src/streaming/detectors/eventBridgeDetector.js +82 -0
  138. package/dist/src/streaming/detectors/kafkaDetector.d.ts +5 -0
  139. package/dist/src/streaming/detectors/kafkaDetector.d.ts.map +1 -0
  140. package/dist/src/streaming/detectors/kafkaDetector.js +97 -0
  141. package/dist/src/streaming/detectors/natsDetector.d.ts +5 -0
  142. package/dist/src/streaming/detectors/natsDetector.d.ts.map +1 -0
  143. package/dist/src/streaming/detectors/natsDetector.js +96 -0
  144. package/dist/src/streaming/detectors/pubsubDetector.d.ts +5 -0
  145. package/dist/src/streaming/detectors/pubsubDetector.d.ts.map +1 -0
  146. package/dist/src/streaming/detectors/pubsubDetector.js +82 -0
  147. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts +5 -0
  148. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts.map +1 -0
  149. package/dist/src/streaming/detectors/rabbitmqDetector.js +103 -0
  150. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts +5 -0
  151. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts.map +1 -0
  152. package/dist/src/streaming/detectors/redisPubsubDetector.js +81 -0
  153. package/dist/src/streaming/detectors/snsDetector.d.ts +5 -0
  154. package/dist/src/streaming/detectors/snsDetector.d.ts.map +1 -0
  155. package/dist/src/streaming/detectors/snsDetector.js +81 -0
  156. package/dist/src/streaming/detectors/sqsDetector.d.ts +5 -0
  157. package/dist/src/streaming/detectors/sqsDetector.d.ts.map +1 -0
  158. package/dist/src/streaming/detectors/sqsDetector.js +81 -0
  159. package/dist/src/streaming/eventCoverage.d.ts +46 -0
  160. package/dist/src/streaming/eventCoverage.d.ts.map +1 -0
  161. package/dist/src/streaming/eventCoverage.js +270 -0
  162. package/dist/src/streaming/types.d.ts +34 -0
  163. package/dist/src/streaming/types.d.ts.map +1 -0
  164. package/dist/src/streaming/types.js +2 -0
  165. package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
  166. package/dist/src/summary/markdownRenderer.js +1 -0
  167. package/dist/src/summary/summaryTypes.d.ts.map +1 -1
  168. package/dist/src/summary/summaryTypes.js +1 -0
  169. package/package.json +1 -1
@@ -0,0 +1,378 @@
1
+ "use strict";
2
+ /**
3
+ * Test Generation Engine (Feature F-TGE)
4
+ *
5
+ * Entry point: TestGenerationEngine
6
+ * CLI commands: generate-tests, export-ai-flows, score-tests
7
+ *
8
+ * Every generated file starts with: // AUTO-GENERATED by api-test-coverage-analyzer
9
+ * expect(response.status) ALWAYS before expect(response.body)
10
+ * NEVER use toBeTruthy() or toBeDefined() — use specific matchers
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ var __importDefault = (this && this.__importDefault) || function (mod) {
46
+ return (mod && mod.__esModule) ? mod : { "default": mod };
47
+ };
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.TestGenerationEngine = void 0;
50
+ const fs = __importStar(require("node:fs"));
51
+ const path = __importStar(require("node:path"));
52
+ const fast_glob_1 = __importDefault(require("fast-glob"));
53
+ const gap_extractor_1 = require("./gap-extractor");
54
+ const context_builder_1 = require("./context-builder");
55
+ const template_renderer_1 = require("./template-renderer");
56
+ const file_router_1 = require("./file-router");
57
+ // ─── Test Generation Engine ──────────────────────────────────────────────────
58
+ class TestGenerationEngine {
59
+ constructor(projectRoot, reportsDir = 'reports') {
60
+ this.projectRoot = path.resolve(projectRoot);
61
+ this.reportsDir = path.resolve(this.projectRoot, reportsDir);
62
+ }
63
+ /**
64
+ * Generate test files for all detected coverage gaps.
65
+ * In dry-run mode, files are not written to disk.
66
+ */
67
+ generateTests(options = {}) {
68
+ var _a;
69
+ const gaps = (0, gap_extractor_1.extractGaps)(this.reportsDir);
70
+ const context = (0, context_builder_1.buildContext)(this.projectRoot, gaps, {
71
+ baseUrl: options.baseUrl,
72
+ authHeader: options.authHeader,
73
+ });
74
+ const outDir = (_a = options.outDir) !== null && _a !== void 0 ? _a : this.projectRoot;
75
+ const generated = [];
76
+ for (const gap of context.gaps) {
77
+ const content = (0, template_renderer_1.renderTemplate)(gap, context);
78
+ const outputPath = (0, file_router_1.routeOutputPath)(gap, context, outDir);
79
+ const templateUsed = (0, template_renderer_1.getTemplateName)(gap, context);
80
+ generated.push({
81
+ outputPath,
82
+ content,
83
+ gapId: gap.id,
84
+ templateUsed,
85
+ });
86
+ if (!options.dryRun) {
87
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
88
+ fs.writeFileSync(outputPath, content, 'utf-8');
89
+ }
90
+ }
91
+ // Write manifest
92
+ const manifest = {
93
+ generatedAt: new Date().toISOString(),
94
+ totalFiles: generated.length,
95
+ files: generated.map((f) => ({
96
+ outputPath: f.outputPath,
97
+ gapId: f.gapId,
98
+ templateUsed: f.templateUsed,
99
+ })),
100
+ };
101
+ if (!options.dryRun) {
102
+ fs.mkdirSync(this.reportsDir, { recursive: true });
103
+ fs.writeFileSync(path.join(this.reportsDir, 'generated-tests-manifest.json'), JSON.stringify(manifest, null, 2), 'utf-8');
104
+ }
105
+ return generated;
106
+ }
107
+ /**
108
+ * Score existing test files for quality.
109
+ * Dimensions: statusAssertion, bodyAssertion, errorPath, noGenericMatchers, descriptiveName.
110
+ */
111
+ scoreTests(testsGlob) {
112
+ const testFiles = fast_glob_1.default.sync(testsGlob, { absolute: true })
113
+ .filter((f) => isTestFile(f));
114
+ const reports = [];
115
+ for (const testFile of testFiles) {
116
+ let content;
117
+ try {
118
+ content = fs.readFileSync(testFile, 'utf-8');
119
+ }
120
+ catch {
121
+ continue;
122
+ }
123
+ const report = scoreTestFile(testFile, content);
124
+ reports.push(report);
125
+ }
126
+ return reports;
127
+ }
128
+ /**
129
+ * Export AI-ready flows: structured prompts for Copilot / LLM consumption.
130
+ * RULE-AI05: prompt ≤ 800 tokens (~3200 chars)
131
+ */
132
+ exportAiFlows(options = {}) {
133
+ var _a;
134
+ const gaps = (0, gap_extractor_1.extractGaps)(this.reportsDir);
135
+ const context = (0, context_builder_1.buildContext)(this.projectRoot, gaps);
136
+ const flows = [];
137
+ const byType = {};
138
+ for (const gap of gaps) {
139
+ const outputPath = (0, file_router_1.routeOutputPath)(gap, context, this.projectRoot);
140
+ // Build a prompt within token budget (RULE-AI05: ≤ 800 tokens ≈ 3200 chars)
141
+ const prompt = buildCopilotPrompt(gap, context, outputPath);
142
+ let truncatedPrompt = prompt;
143
+ if (truncatedPrompt.length > 3200) {
144
+ const suffix = '\n...';
145
+ const maxBody = 3200 - suffix.length; // 3196 chars for body, 4 for suffix
146
+ const cutoff = truncatedPrompt.lastIndexOf('\n', maxBody);
147
+ truncatedPrompt = truncatedPrompt.substring(0, cutoff > 0 ? cutoff : maxBody) + suffix;
148
+ }
149
+ flows.push({
150
+ id: `flow-${gap.id}`,
151
+ gapId: gap.id,
152
+ gapType: gap.type,
153
+ suggestedOutputPath: outputPath, // RULE-AI01
154
+ copilotPrompt: truncatedPrompt, // RULE-AI02, AI03, AI05, AI06
155
+ existingSimilarTests: gap.existingSimilarTests, // RULE-AI04
156
+ estimatedComplexity: estimateComplexity(gap),
157
+ riskScore: gap.riskScore,
158
+ });
159
+ byType[gap.type] = ((_a = byType[gap.type]) !== null && _a !== void 0 ? _a : 0) + 1;
160
+ }
161
+ const manifest = {
162
+ generatedAt: new Date().toISOString(),
163
+ totalFlows: flows.length,
164
+ byType: byType,
165
+ flows,
166
+ };
167
+ // Write JSON
168
+ fs.mkdirSync(this.reportsDir, { recursive: true });
169
+ fs.writeFileSync(path.join(this.reportsDir, 'ai-ready-flows.json'), JSON.stringify(manifest, null, 2), 'utf-8');
170
+ // Write markdown summary
171
+ const md = renderAiFlowsMarkdown(manifest);
172
+ fs.writeFileSync(path.join(this.reportsDir, 'ai-ready-flows.md'), md, 'utf-8');
173
+ return manifest;
174
+ }
175
+ }
176
+ exports.TestGenerationEngine = TestGenerationEngine;
177
+ // ─── Scoring helpers ─────────────────────────────────────────────────────────
178
+ /** Only score actual test files, not helpers, fixtures, or utility modules. */
179
+ function isTestFile(filePath) {
180
+ const base = path.basename(filePath);
181
+ const isTest = /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(base) ||
182
+ /test\.(ts|js|tsx|jsx)$/.test(base);
183
+ // Exclude fixture directories — these contain sample test data, not real tests
184
+ const isFixture = /[/\\]fixtures[/\\]/.test(filePath);
185
+ return isTest && !isFixture;
186
+ }
187
+ /** HTTP client package names whose presence in imports indicates an API test. */
188
+ const HTTP_CLIENT_PACKAGES = [
189
+ 'supertest', 'axios', 'node-fetch', 'got',
190
+ ];
191
+ /** Angular-specific HTTP client identifiers in imports. */
192
+ const HTTP_CLIENT_IDENTIFIERS = [
193
+ 'HttpClient', 'HttpTestingController',
194
+ ];
195
+ /**
196
+ * Detect whether a test file is an API/HTTP integration test (vs a unit/library test).
197
+ * Only checks the file's own import/require statements —
198
+ * NOT inline strings that test code might analyze as content.
199
+ */
200
+ function isApiTest(content) {
201
+ const importSection = extractImportSection(content);
202
+ return HTTP_CLIENT_PACKAGES.some((pkg) => new RegExp(`import\\b.*['"]${pkg}['"]`).test(importSection) ||
203
+ new RegExp(`require\\s*\\(\\s*['"]${pkg}['"]`).test(importSection)) || HTTP_CLIENT_IDENTIFIERS.some((id) => new RegExp(`import\\b.*${id}`).test(importSection));
204
+ }
205
+ /** Extract the import/require section at the top of the file, before any test blocks. */
206
+ function extractImportSection(content) {
207
+ const lines = content.split('\n');
208
+ const importLines = [];
209
+ for (const line of lines) {
210
+ const trimmed = line.trim();
211
+ if (/^(describe|it|test|beforeAll|beforeEach|afterAll|afterEach)\s*\(/.test(trimmed)) {
212
+ break;
213
+ }
214
+ importLines.push(line);
215
+ }
216
+ return importLines.join('\n');
217
+ }
218
+ /** Test name keywords that indicate negative / error / edge-case testing. */
219
+ const ERROR_PATH_NAME_KEYWORDS = [
220
+ 'fail', 'invalid', 'throw', 'missing', 'empty', '\\bnot\\b', 'negative',
221
+ 'reject', 'undefined', 'null', 'zero', 'without', 'default', 'skip',
222
+ 'ignore', '\\bno\\s', 'none', 'unknown', 'error',
223
+ ];
224
+ /** Content-level patterns that indicate error/edge-case handling. */
225
+ const ERROR_PATH_CONTENT_PATTERNS = [
226
+ /\.catch\b/,
227
+ /expect\([^)]*\.status\)\s*\.toBe\(\s*(4|5)\d{2}\s*\)/,
228
+ /\.expect\(\s*(4|5)\d{2}\s*\)/,
229
+ /describe.*error/i,
230
+ /toThrow/,
231
+ /rejects\.toThrow/,
232
+ /throw new Error/,
233
+ /console\.warn/,
234
+ /expect\([^)]*\)\.toBe\(\s*false\s*\)/,
235
+ /expect\([^)]*\)\.toEqual\(\s*\[\s*\]\s*\)/,
236
+ /expect\([^)]*\)\.toBe\(\s*0\s*\)/,
237
+ /expect\([^)]*\)\.toBeNull/,
238
+ /expect\([^)]*\)\.toBeUndefined/,
239
+ ];
240
+ /** Specific matcher patterns (as opposed to generic toBeTruthy/toBeDefined). */
241
+ const SPECIFIC_MATCHER_PATTERNS = [
242
+ /\.toBe\(/, /\.toEqual\(/, /\.toContain\(/, /\.toHaveLength\(/,
243
+ /\.toMatch\(/, /\.toThrow/, /\.toBeGreaterThan/, /\.toBeLessThan/,
244
+ /\.toHaveProperty\(/, /\.toHaveBeenCalled/, /\.toBeNull\(/,
245
+ /\.toBeUndefined\(/, /\.toBeInstanceOf\(/, /\.toStrictEqual\(/,
246
+ /\.toMatchObject\(/,
247
+ ];
248
+ function scoreTestFile(filePath, content) {
249
+ const apiTest = isApiTest(content);
250
+ // Status and body assertions only apply to API tests.
251
+ // For unit/library tests, these are automatically satisfied.
252
+ const hasStatusAssertion = !apiTest ||
253
+ /expect\([^)]*\.status\b/.test(content) ||
254
+ /expect\([^)]*statusCode\b/.test(content) ||
255
+ /\.expect\(\s*\d{3}\s*\)/.test(content);
256
+ const hasBodyAssertion = !apiTest ||
257
+ /expect\([^)]*\.body\b/.test(content) ||
258
+ /expect\([^)]*\.data\b/.test(content) ||
259
+ /expect\([^)]*\.text\b/.test(content);
260
+ const hasErrorPath = ERROR_PATH_CONTENT_PATTERNS.some((p) => p.test(content)) ||
261
+ ERROR_PATH_NAME_KEYWORDS.some((kw) => new RegExp(`(it|test)\\s*\\(.*${kw}`, 'i').test(content));
262
+ // For unit/library tests, toBeDefined()/toBeTruthy() are acceptable when
263
+ // the test also uses specific matchers (toBe, toEqual, toContain, etc.).
264
+ // Only flag when generic matchers are used without any specific ones.
265
+ const usesGenericMatchers = /toBeTruthy\(\)/.test(content) || /toBeDefined\(\)/.test(content);
266
+ const usesSpecificMatchers = SPECIFIC_MATCHER_PATTERNS.some((p) => p.test(content));
267
+ const hasNoGenericMatchers = !usesGenericMatchers ||
268
+ (!apiTest && usesSpecificMatchers);
269
+ const hasDescriptiveName = (/describe\(\s*['"`][^'"`]{5,}['"`]/.test(content) ||
270
+ /test\(\s*['"`][^'"`]{15,}['"`]/.test(content)) && (/it\(\s*['"`][^'"`]{10,}['"`]/.test(content) ||
271
+ /test\(\s*['"`][^'"`]{15,}['"`]/.test(content));
272
+ const dimensions = {
273
+ hasStatusAssertion,
274
+ hasBodyAssertion,
275
+ hasErrorPath,
276
+ hasNoGenericMatchers,
277
+ hasDescriptiveName,
278
+ };
279
+ const score = Object.values(dimensions).filter(Boolean).length * 20;
280
+ const violations = [];
281
+ if (!hasStatusAssertion)
282
+ violations.push('Missing status code assertion');
283
+ if (!hasBodyAssertion)
284
+ violations.push('Missing body/data assertion');
285
+ if (!hasErrorPath)
286
+ violations.push('Missing error/negative path');
287
+ if (!hasNoGenericMatchers)
288
+ violations.push('Uses toBeTruthy() or toBeDefined() — prefer specific matchers');
289
+ if (!hasDescriptiveName)
290
+ violations.push('Test names not sufficiently descriptive');
291
+ return {
292
+ file: filePath,
293
+ overallScore: score,
294
+ dimensions,
295
+ violations,
296
+ };
297
+ }
298
+ // ─── AI Flow helpers ─────────────────────────────────────────────────────────
299
+ /** RULE-AI02: exact import path; RULE-AI03: ALL missing test cases; RULE-AI06: English only */
300
+ function buildCopilotPrompt(gap, ctx, suggestedOutputPath) {
301
+ // RULE-AI02: derive concrete import path from gap.sourceFile when available
302
+ const importPath = gap.sourceFile
303
+ ? path.relative(path.dirname(suggestedOutputPath), gap.sourceFile).replace(/\.[jt]sx?$/, '')
304
+ : path.relative(path.dirname(suggestedOutputPath), ctx.projectRoot);
305
+ const lines = [
306
+ `Write a ${ctx.testFramework} test file at: ${suggestedOutputPath}`, // RULE-AI01
307
+ `Import from: ${importPath}`, // RULE-AI02
308
+ '',
309
+ `Gap type: ${gap.type}`,
310
+ `Risk score: ${gap.riskScore}/100`,
311
+ ];
312
+ // RULE-AI03: ALL missing test cases
313
+ if (gap.endpoint) {
314
+ lines.push(`Test: ${gap.endpoint.method} ${gap.endpoint.path}`);
315
+ lines.push('Include: success case, validation error, auth required');
316
+ }
317
+ if (gap.parameter) {
318
+ lines.push(`Test parameter: ${gap.parameter.name} (${gap.parameter.in}, required: ${gap.parameter.required})`);
319
+ lines.push('Include: valid value, missing value, invalid type');
320
+ }
321
+ if (gap.businessRule) {
322
+ lines.push(`Test business rule: ${gap.businessRule.description}`);
323
+ lines.push('Include: rule satisfied, rule violated, edge case');
324
+ }
325
+ if (gap.integrationFlow) {
326
+ lines.push(`Test flow: ${gap.integrationFlow.name} — missing step: ${gap.integrationFlow.missingStep}`);
327
+ }
328
+ // RULE-AI04: reference existingSimilarTests[0] when available
329
+ if (gap.existingSimilarTests.length > 0) {
330
+ lines.push('');
331
+ lines.push(`Reference existing test: ${gap.existingSimilarTests[0]}`);
332
+ }
333
+ lines.push('');
334
+ lines.push('Rules:');
335
+ lines.push('- expect(response.status) ALWAYS before expect(response.body)');
336
+ lines.push('- NEVER use toBeTruthy() or toBeDefined()');
337
+ lines.push('- First line: // AUTO-GENERATED by api-test-coverage-analyzer');
338
+ return lines.join('\n');
339
+ }
340
+ function estimateComplexity(gap) {
341
+ if (gap.riskScore >= 80)
342
+ return 'high';
343
+ if (gap.riskScore >= 50)
344
+ return 'medium';
345
+ return 'low';
346
+ }
347
+ function renderAiFlowsMarkdown(manifest) {
348
+ const lines = [
349
+ '# AI-Ready Test Generation Flows',
350
+ '',
351
+ `Generated: ${manifest.generatedAt}`,
352
+ `Total flows: ${manifest.totalFlows}`,
353
+ '',
354
+ '## By Type',
355
+ '',
356
+ '| Gap Type | Count |',
357
+ '|----------|-------|',
358
+ ];
359
+ for (const [type, count] of Object.entries(manifest.byType)) {
360
+ lines.push(`| ${type} | ${count} |`);
361
+ }
362
+ lines.push('');
363
+ lines.push('## Flows');
364
+ lines.push('');
365
+ for (const flow of manifest.flows) {
366
+ lines.push(`### ${flow.id}`);
367
+ lines.push('');
368
+ lines.push(`- **Gap:** ${flow.gapType}`);
369
+ lines.push(`- **Risk:** ${flow.riskScore}/100`);
370
+ lines.push(`- **Complexity:** ${flow.estimatedComplexity}`);
371
+ lines.push(`- **Output:** \`${flow.suggestedOutputPath}\``);
372
+ if (flow.existingSimilarTests.length > 0) {
373
+ lines.push(`- **Reference:** ${flow.existingSimilarTests[0]}`);
374
+ }
375
+ lines.push('');
376
+ }
377
+ return lines.join('\n');
378
+ }
@@ -0,0 +1,7 @@
1
+ import type { GenerationContext, DetectedGap } from './types';
2
+ /**
3
+ * Determines the output file path for a generated test based on
4
+ * the language, framework, and gap type.
5
+ */
6
+ export declare function routeOutputPath(gap: DetectedGap, context: GenerationContext, outDir: string): string;
7
+ //# sourceMappingURL=file-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-router.d.ts","sourceRoot":"","sources":["../../../src/generation/file-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,MAAM,GACb,MAAM,CAiBR"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.routeOutputPath = routeOutputPath;
37
+ const path = __importStar(require("node:path"));
38
+ /**
39
+ * Determines the output file path for a generated test based on
40
+ * the language, framework, and gap type.
41
+ */
42
+ function routeOutputPath(gap, context, outDir) {
43
+ const { framework, language } = context;
44
+ let baseDir;
45
+ if (framework === 'cypress') {
46
+ baseDir = path.join(outDir, 'cypress', 'e2e', 'generated');
47
+ }
48
+ else if (language === 'java' || language === 'kotlin') {
49
+ baseDir = path.join(outDir, 'src', 'test', 'generated');
50
+ }
51
+ else {
52
+ baseDir = path.join(outDir, 'tests', 'generated');
53
+ }
54
+ const ext = getFileExtension(language);
55
+ const filename = buildFilename(gap, language);
56
+ return path.join(baseDir, filename + ext);
57
+ }
58
+ function getFileExtension(language) {
59
+ switch (language) {
60
+ case 'typescript': return '.test.ts';
61
+ case 'javascript': return '.test.js';
62
+ case 'python': return '_test.py';
63
+ case 'java': return 'Test.java';
64
+ case 'kotlin': return 'Test.kt';
65
+ default: return '.test.ts';
66
+ }
67
+ }
68
+ function buildFilename(gap, language) {
69
+ // Sanitise the gap id to produce a valid filename
70
+ const sanitised = gap.id.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-');
71
+ // For Java/Kotlin, use PascalCase class names
72
+ if (language === 'java' || language === 'kotlin') {
73
+ return sanitised
74
+ .split('-')
75
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
76
+ .join('');
77
+ }
78
+ return sanitised;
79
+ }
@@ -0,0 +1,12 @@
1
+ import type { DetectedGap } from './types';
2
+ /**
3
+ * Builds 5 synthetic sample gaps for onboarding / dry-run purposes.
4
+ * Used when coverage-summary.json has zero gaps or does not exist.
5
+ */
6
+ export declare function buildSampleGaps(): DetectedGap[];
7
+ /**
8
+ * Reads reports/coverage-summary.json and extracts uncovered items as DetectedGap[].
9
+ * When there are zero gaps (perfect coverage), returns buildSampleGaps() instead of [].
10
+ */
11
+ export declare function extractGaps(reportsDir: string): DetectedGap[];
12
+ //# sourceMappingURL=gap-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gap-extractor.d.ts","sourceRoot":"","sources":["../../../src/generation/gap-extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,SAAS,CAAC;AAcpD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,WAAW,EAAE,CA4C/C;AAmBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE,CA6D7D"}