api-tests-coverage 1.0.17 → 1.0.19

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 (128) hide show
  1. package/README.md +7 -3
  2. package/dist/dashboard/dist/assets/_basePickBy-CYB1KXah.js +1 -0
  3. package/dist/dashboard/dist/assets/_basePickBy-DUQHbXda.js +1 -0
  4. package/dist/dashboard/dist/assets/_baseUniq-Bwm426M6.js +1 -0
  5. package/dist/dashboard/dist/assets/_baseUniq-Ct8XEXnH.js +1 -0
  6. package/dist/dashboard/dist/assets/arc-B7p8x22e.js +1 -0
  7. package/dist/dashboard/dist/assets/arc-CjFGY63A.js +1 -0
  8. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-Boahc5dR.js +36 -0
  9. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-wVr1_uNB.js +36 -0
  10. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-BBXc88fn.js +122 -0
  11. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-CavSRNuP.js +122 -0
  12. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-BsgzPfQ3.js +10 -0
  13. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-w18S5AEN.js +10 -0
  14. package/dist/dashboard/dist/assets/channel-BgeGdqQG.js +1 -0
  15. package/dist/dashboard/dist/assets/channel-psxgcQ_j.js +1 -0
  16. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BF8loPLD.js +1 -0
  17. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-IN53WLTx.js +1 -0
  18. package/dist/dashboard/dist/assets/chunk-55IACEB6-C3HNF-UF.js +1 -0
  19. package/dist/dashboard/dist/assets/chunk-55IACEB6-kJkjQYxk.js +1 -0
  20. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-B7YfMggR.js +165 -0
  21. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-wQ6TCEMq.js +165 -0
  22. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-B7xHuqZu.js +220 -0
  23. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-DfslhtXS.js +220 -0
  24. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-BBMfQbw1.js +15 -0
  25. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-K3PC79JF.js +15 -0
  26. package/dist/dashboard/dist/assets/chunk-QN33PNHL-CmeZ1h1Z.js +1 -0
  27. package/dist/dashboard/dist/assets/chunk-QN33PNHL-DFgUs0T8.js +1 -0
  28. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-Cyg7Km90.js +1 -0
  29. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-DKgOcPif.js +1 -0
  30. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BoJFBewj.js +1 -0
  31. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-C8KNXDi7.js +1 -0
  32. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-AMwn99HP.js +1 -0
  33. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-CM6Qs-Qs.js +1 -0
  34. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-AMwn99HP.js +1 -0
  35. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-CM6Qs-Qs.js +1 -0
  36. package/dist/dashboard/dist/assets/clone-DEYRVSAn.js +1 -0
  37. package/dist/dashboard/dist/assets/clone-KEkbvJY9.js +1 -0
  38. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DMGRGhwB.js +1 -0
  39. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-YL9kFxCl.js +1 -0
  40. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-CJT7lofP.js +4 -0
  41. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-NZWnQN_Y.js +4 -0
  42. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-DGtyS7lD.js +24 -0
  43. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-DtE0cTIs.js +24 -0
  44. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BHyZd544.js +43 -0
  45. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CSCGZUfr.js +43 -0
  46. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DdqZVGN1.js +24 -0
  47. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-qvXlTDud.js +24 -0
  48. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-D0MbudeO.js +60 -0
  49. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-Dhb_VQMS.js +60 -0
  50. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-DRAD4OG7.js +162 -0
  51. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-gKUH-GJ2.js +162 -0
  52. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-DK_45K6s.js +267 -0
  53. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-Dm_lLo9y.js +267 -0
  54. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DM9AW1aP.js +65 -0
  55. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DYrdM8tK.js +65 -0
  56. package/dist/dashboard/dist/assets/graph-CD7-npU0.js +1 -0
  57. package/dist/dashboard/dist/assets/graph-Clj85F2M.js +1 -0
  58. package/dist/dashboard/dist/assets/index-CqEIqNus.js +781 -0
  59. package/dist/dashboard/dist/assets/index-DbUdNJca.js +781 -0
  60. package/dist/dashboard/dist/assets/index-xecKLQ58.css +1 -0
  61. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BMp4C5wf.js +2 -0
  62. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-DyT5Fs8R.js +2 -0
  63. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BC0GSZ7W.js +139 -0
  64. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-nYZBlgTD.js +139 -0
  65. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-COTfX74l.js +89 -0
  66. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-D6aRd_q1.js +89 -0
  67. package/dist/dashboard/dist/assets/layout-6njVG9Ld.js +1 -0
  68. package/dist/dashboard/dist/assets/layout-BbJNDkTr.js +1 -0
  69. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B93XW27v.js +68 -0
  70. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-CkyYtMaD.js +68 -0
  71. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-9G1tEuaq.js +30 -0
  72. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-uWFQFMEe.js +30 -0
  73. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-i3-JTN3e.js +7 -0
  74. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-jDtdB4Ws.js +7 -0
  75. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-Dw260IiT.js +64 -0
  76. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-WIJ0qiJG.js +64 -0
  77. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-Cb4WB9UB.js +10 -0
  78. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-LR8T4Hv0.js +10 -0
  79. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-BqGJWVUS.js +145 -0
  80. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-DBqchhlr.js +145 -0
  81. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-0Wd-KmOv.js +1 -0
  82. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-Bl16d4W5.js +1 -0
  83. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-B05ygO34.js +1 -0
  84. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-BlwaoFEG.js +1 -0
  85. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-CAmQOjBu.js +61 -0
  86. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-D6JNee_P.js +61 -0
  87. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CCvvSJBX.js +162 -0
  88. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CRP-WvE-.js +162 -0
  89. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-5DoR2_q5.js +7 -0
  90. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-B72UwDAP.js +7 -0
  91. package/dist/dashboard/dist/index.html +2 -2
  92. package/dist/src/config/defaultConfig.d.ts.map +1 -1
  93. package/dist/src/config/defaultConfig.js +37 -0
  94. package/dist/src/config/types.d.ts +42 -0
  95. package/dist/src/config/types.d.ts.map +1 -1
  96. package/dist/src/config/validateConfig.d.ts.map +1 -1
  97. package/dist/src/config/validateConfig.js +3 -0
  98. package/dist/src/generation/ai-flow-exporter.d.ts +7 -0
  99. package/dist/src/generation/ai-flow-exporter.d.ts.map +1 -0
  100. package/dist/src/generation/ai-flow-exporter.js +260 -0
  101. package/dist/src/generation/context-builder.d.ts +16 -0
  102. package/dist/src/generation/context-builder.d.ts.map +1 -0
  103. package/dist/src/generation/context-builder.js +170 -0
  104. package/dist/src/generation/engine.d.ts +19 -0
  105. package/dist/src/generation/engine.d.ts.map +1 -0
  106. package/dist/src/generation/engine.js +204 -0
  107. package/dist/src/generation/file-router.d.ts +8 -0
  108. package/dist/src/generation/file-router.d.ts.map +1 -0
  109. package/dist/src/generation/file-router.js +98 -0
  110. package/dist/src/generation/gap-extractor.d.ts +7 -0
  111. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  112. package/dist/src/generation/gap-extractor.js +291 -0
  113. package/dist/src/generation/index.d.ts +9 -0
  114. package/dist/src/generation/index.d.ts.map +1 -0
  115. package/dist/src/generation/index.js +15 -0
  116. package/dist/src/generation/quality-scorer.d.ts +15 -0
  117. package/dist/src/generation/quality-scorer.d.ts.map +1 -0
  118. package/dist/src/generation/quality-scorer.js +273 -0
  119. package/dist/src/generation/template-renderer.d.ts +12 -0
  120. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  121. package/dist/src/generation/template-renderer.js +546 -0
  122. package/dist/src/generation/types.d.ts +269 -0
  123. package/dist/src/generation/types.d.ts.map +1 -0
  124. package/dist/src/generation/types.js +6 -0
  125. package/dist/src/index.js +113 -0
  126. package/dist/src/inference/routeInference.d.ts.map +1 -1
  127. package/dist/src/inference/routeInference.js +54 -8
  128. package/package.json +1 -1
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Feature 28 — Test Generation & Quality Intelligence Engine
3
+ * Type definitions for the test generation pipeline.
4
+ */
5
+ export type GapType = 'endpoint' | 'parameter' | 'error' | 'business' | 'integration' | 'security' | 'auth';
6
+ export type GapPriority = 'P0' | 'P1' | 'P2' | 'P3' | 'P4' | 'P5';
7
+ export type TestType = 'unit' | 'integration' | 'cypress' | 'security';
8
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
9
+ export type AuthType = 'jwt' | 'basic' | 'oauth2' | 'apikey';
10
+ export type ParameterLocation = 'query' | 'path' | 'header' | 'body';
11
+ export type MissingCase = 'boundary-min' | 'boundary-max' | 'invalid-type' | 'missing-required' | 'sql-injection' | 'xss';
12
+ export type SecurityControlType = 'sql-injection' | 'xss' | 'auth-bypass' | 'rate-limit' | 'mass-assignment';
13
+ export type FileNamingConvention = 'kebab' | 'camelCase' | 'snake_case';
14
+ export interface DetectedGap {
15
+ id: string;
16
+ type: GapType;
17
+ priority: GapPriority;
18
+ riskScore: number;
19
+ endpoint: {
20
+ method: HttpMethod;
21
+ path: string;
22
+ operationId?: string;
23
+ summary?: string;
24
+ tags?: string[];
25
+ auth: {
26
+ required: boolean;
27
+ optional: boolean;
28
+ type?: AuthType;
29
+ headerName?: string;
30
+ scheme?: string;
31
+ };
32
+ };
33
+ parameters?: ParameterInfo[];
34
+ responses?: ResponseInfo[];
35
+ businessRule?: BusinessRuleInfo;
36
+ securityControl?: SecurityControlInfo;
37
+ flow?: FlowInfo;
38
+ }
39
+ export interface ParameterInfo {
40
+ name: string;
41
+ in: ParameterLocation;
42
+ required: boolean;
43
+ schema: {
44
+ type: string;
45
+ format?: string;
46
+ minimum?: number;
47
+ maximum?: number;
48
+ minLength?: number;
49
+ maxLength?: number;
50
+ enum?: unknown[];
51
+ pattern?: string;
52
+ };
53
+ missingCases: MissingCase[];
54
+ }
55
+ export interface ResponseInfo {
56
+ statusCode: number;
57
+ description?: string;
58
+ schema?: unknown;
59
+ }
60
+ export interface BusinessRuleInfo {
61
+ id: string;
62
+ description: string;
63
+ condition: string;
64
+ acceptanceCriteria: string[];
65
+ }
66
+ export interface SecurityControlInfo {
67
+ type: SecurityControlType;
68
+ description: string;
69
+ attackVector: string;
70
+ expectedStatusCode: number;
71
+ }
72
+ export interface FlowInfo {
73
+ id: string;
74
+ name: string;
75
+ steps: Array<{
76
+ method: string;
77
+ path: string;
78
+ description: string;
79
+ }>;
80
+ missingStepIndex: number;
81
+ }
82
+ export interface GenerationContext {
83
+ gap: {
84
+ id: string;
85
+ type: GapType;
86
+ priority: GapPriority;
87
+ riskScore: number;
88
+ };
89
+ endpoint: {
90
+ method: string;
91
+ path: string;
92
+ pathNormalized: string;
93
+ operationId?: string;
94
+ summary?: string;
95
+ tags?: string[];
96
+ auth: {
97
+ required: boolean;
98
+ optional: boolean;
99
+ type?: AuthType;
100
+ headerName?: string;
101
+ scheme?: string;
102
+ };
103
+ };
104
+ parameters: ParameterInfo[];
105
+ responses: ResponseInfo[];
106
+ fixtures: {
107
+ validPayload: Record<string, unknown>;
108
+ invalidPayload: Record<string, unknown>;
109
+ authToken: string;
110
+ pathParams: Record<string, string>;
111
+ };
112
+ project: {
113
+ name: string;
114
+ language: string;
115
+ framework: string;
116
+ testFramework: string;
117
+ baseUrl: string;
118
+ importPrefix: string;
119
+ };
120
+ businessRule?: BusinessRuleInfo;
121
+ securityControl?: SecurityControlInfo;
122
+ flow?: FlowInfo;
123
+ }
124
+ export interface GeneratedFile {
125
+ gapId: string;
126
+ filePath: string;
127
+ relativePath: string;
128
+ content: string;
129
+ testType: TestType;
130
+ language: string;
131
+ framework: string;
132
+ }
133
+ export interface GenerationResult {
134
+ files: GeneratedFile[];
135
+ dryRun: boolean;
136
+ totalGaps: number;
137
+ generatedCount: number;
138
+ skippedCount: number;
139
+ errors: Array<{
140
+ gapId: string;
141
+ message: string;
142
+ }>;
143
+ }
144
+ export interface GenerationOptions {
145
+ reportsDir: string;
146
+ outDir: string;
147
+ language?: string;
148
+ framework?: string;
149
+ priority?: GapPriority;
150
+ dryRun?: boolean;
151
+ overwrite?: boolean;
152
+ gapId?: string;
153
+ types?: GapType[];
154
+ noSecurity?: boolean;
155
+ noCypress?: boolean;
156
+ }
157
+ export interface QualityDimensions {
158
+ assertionDepth: number;
159
+ negativePathCoverage: number;
160
+ authCoverage: number;
161
+ boundaryCoverage: number;
162
+ testIndependence: number;
163
+ }
164
+ export interface FileQualityScore {
165
+ file: string;
166
+ score: number;
167
+ dimensions: QualityDimensions;
168
+ issues: string[];
169
+ strengths: string[];
170
+ }
171
+ export interface HighRiskLowQualityGap {
172
+ endpoint: string;
173
+ qualityScore: number;
174
+ riskScore: number;
175
+ primaryIssue: string;
176
+ }
177
+ export interface TestQualityReport {
178
+ overallScore: number;
179
+ byFile: FileQualityScore[];
180
+ lowestQualityFiles: string[];
181
+ highestRiskLowQualityGaps: HighRiskLowQualityGap[];
182
+ }
183
+ export interface QualityScorerOptions {
184
+ testsGlob?: string;
185
+ reportsDir?: string;
186
+ failBelow?: number;
187
+ }
188
+ export interface MissingTestCase {
189
+ id: string;
190
+ description: string;
191
+ expectedStatus: number;
192
+ }
193
+ export interface AiFlowGap {
194
+ gapId: string;
195
+ priority: GapPriority;
196
+ riskScore: number;
197
+ type: GapType;
198
+ endpoint: {
199
+ method: string;
200
+ path: string;
201
+ auth: {
202
+ required: boolean;
203
+ type?: AuthType;
204
+ scheme?: string;
205
+ };
206
+ };
207
+ missingTestCases: MissingTestCase[];
208
+ requestSchema?: unknown;
209
+ responseSchema?: unknown;
210
+ copilotPrompt: string;
211
+ suggestedOutputPath: string;
212
+ existingSimilarTests: string[];
213
+ generatedCode?: string;
214
+ }
215
+ export interface AiReadyFlows {
216
+ generatedAt: string;
217
+ project: {
218
+ name: string;
219
+ language: string;
220
+ testFramework: string;
221
+ appImportPath: string;
222
+ };
223
+ gaps: AiFlowGap[];
224
+ }
225
+ export interface AiFlowExporterOptions {
226
+ reportsDir: string;
227
+ outDir: string;
228
+ format?: 'markdown' | 'json' | 'both';
229
+ maxGaps?: number;
230
+ priority?: GapPriority;
231
+ }
232
+ export interface GenerationConfig {
233
+ enabled?: boolean;
234
+ outputDir?: string;
235
+ dryRun?: boolean;
236
+ overwrite?: boolean;
237
+ minPriority?: GapPriority;
238
+ unitFramework?: string;
239
+ integrationFramework?: string;
240
+ e2eFramework?: string;
241
+ fileNaming?: FileNamingConvention;
242
+ includeTypes?: GapType[];
243
+ securityTests?: {
244
+ enabled?: boolean;
245
+ includeInjection?: boolean;
246
+ includeAuthBypass?: boolean;
247
+ includeRateLimit?: boolean;
248
+ };
249
+ fixtures?: {
250
+ authTokenPlaceholder?: string;
251
+ baseUrl?: string;
252
+ };
253
+ }
254
+ export interface TestQualityConfig {
255
+ enabled?: boolean;
256
+ minimumScore?: number;
257
+ enforceOnGlob?: string;
258
+ excludeGlob?: string;
259
+ outputPath?: string;
260
+ }
261
+ export interface AiFlowsConfig {
262
+ enabled?: boolean;
263
+ outputDir?: string;
264
+ maxGapsPerExport?: number;
265
+ minPriority?: GapPriority;
266
+ includeGeneratedCode?: boolean;
267
+ copilotPromptMaxTokens?: number;
268
+ }
269
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/generation/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Feature 28 — Test Generation & Quality Intelligence Engine
4
+ * Type definitions for the test generation pipeline.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/src/index.js CHANGED
@@ -63,6 +63,7 @@ const integrationFlowInference_1 = require("./inference/integrationFlowInference
63
63
  const routeInference_1 = require("./inference/routeInference");
64
64
  const scanManifest_1 = require("./inference/scanManifest");
65
65
  const serveDashboard_1 = require("./serveDashboard");
66
+ const index_4 = require("./generation/index");
66
67
  // Register all language AST analyzers at startup.
67
68
  // This side-effect import ensures each language module's registerAnalyzer() call runs.
68
69
  (0, astAnalysisOrchestrator_1.registerAllAnalyzers)();
@@ -2044,6 +2045,118 @@ program
2044
2045
  });
2045
2046
  // Keep the process alive while the server runs
2046
2047
  });
2048
+ program
2049
+ .command('generate-tests')
2050
+ .description('Generate test scaffolds for detected coverage gaps')
2051
+ .option('--reports-dir <dir>', 'Directory with coverage reports', 'reports/')
2052
+ .option('--out-dir <dir>', 'Output directory for generated tests', 'generated-tests/')
2053
+ .option('--language <lang>', 'Target language override (auto-detected if omitted)')
2054
+ .option('--framework <fw>', 'Test framework override (auto-detected if omitted)')
2055
+ .option('--priority <p>', 'Only generate for gaps at this priority or higher', 'P1')
2056
+ .option('--dry-run', 'Print generated tests to stdout, do not write files', false)
2057
+ .option('--overwrite', 'Overwrite existing generated files', false)
2058
+ .option('--gap-id <id>', 'Generate tests for a single specific gap')
2059
+ .option('--types <list>', 'Comma-separated gap types to generate (default: all)')
2060
+ .option('--no-security', 'Skip security test generation')
2061
+ .option('--no-cypress', 'Skip Cypress test generation')
2062
+ .action(async (options) => {
2063
+ const types = options.types
2064
+ ? options.types.split(',').map(t => t.trim())
2065
+ : undefined;
2066
+ const result = await (0, index_4.generateTests)({
2067
+ reportsDir: options.reportsDir,
2068
+ outDir: options.outDir,
2069
+ language: options.language,
2070
+ framework: options.framework,
2071
+ priority: options.priority,
2072
+ dryRun: Boolean(options.dryRun),
2073
+ overwrite: Boolean(options.overwrite),
2074
+ gapId: options.gapId,
2075
+ types,
2076
+ noSecurity: Boolean(options.noSecurity),
2077
+ noCypress: Boolean(options.noCypress),
2078
+ });
2079
+ if (result.dryRun) {
2080
+ for (const file of result.files) {
2081
+ console.log(`\n${'='.repeat(60)}`);
2082
+ console.log(`// FILE: ${file.relativePath}`);
2083
+ console.log(`${'='.repeat(60)}`);
2084
+ console.log(file.content);
2085
+ }
2086
+ }
2087
+ else {
2088
+ console.log(`\nTest generation complete:`);
2089
+ console.log(` Gaps processed: ${result.totalGaps}`);
2090
+ console.log(` Files generated: ${result.generatedCount}`);
2091
+ console.log(` Errors: ${result.errors.length}`);
2092
+ if (result.errors.length > 0) {
2093
+ for (const err of result.errors) {
2094
+ console.error(` ERROR [${err.gapId}]: ${err.message}`);
2095
+ }
2096
+ }
2097
+ }
2098
+ });
2099
+ program
2100
+ .command('export-ai-flows')
2101
+ .description('Export AI-ready flow documentation for Copilot/Cursor/Claude')
2102
+ .option('--reports-dir <dir>', 'Directory with coverage reports', 'reports/')
2103
+ .option('--out-dir <dir>', 'Output directory for AI flow files', 'reports/')
2104
+ .option('--format <fmt>', 'Output format: markdown, json, or both', 'both')
2105
+ .option('--max-gaps <n>', 'Maximum number of gaps to include', '50')
2106
+ .option('--priority <p>', 'Only include gaps at this priority or higher', 'P3')
2107
+ .action(async (options) => {
2108
+ const flows = await (0, index_4.exportAiFlows)({
2109
+ reportsDir: options.reportsDir,
2110
+ outDir: options.outDir,
2111
+ format: options.format,
2112
+ maxGaps: parseInt(options.maxGaps, 10),
2113
+ priority: options.priority,
2114
+ });
2115
+ console.log(`\nAI flows export complete:`);
2116
+ console.log(` Gaps exported: ${flows.gaps.length}`);
2117
+ console.log(` Project: ${flows.project.name}`);
2118
+ console.log(` Language: ${flows.project.language}`);
2119
+ });
2120
+ program
2121
+ .command('score-tests')
2122
+ .description('Score quality of existing test suite on 5 dimensions (0-100)')
2123
+ .option('--tests <glob>', 'Glob pattern for test files to score', 'tests/**/*.test.ts')
2124
+ .option('--reports-dir <dir>', 'Directory to write quality score output', 'reports/')
2125
+ .option('--fail-below <score>', 'Exit non-zero if any file scores below this', '0')
2126
+ .action(async (options) => {
2127
+ var _a;
2128
+ try {
2129
+ const report = await (0, index_4.scoreTests)({
2130
+ testsGlob: options.tests,
2131
+ reportsDir: options.reportsDir,
2132
+ failBelow: parseInt(options.failBelow, 10),
2133
+ });
2134
+ console.log(`\nTest Quality Score Report:`);
2135
+ console.log(` Overall score: ${report.overallScore}/100`);
2136
+ console.log(` Files scored: ${report.byFile.length}`);
2137
+ if (report.lowestQualityFiles.length > 0) {
2138
+ console.log(`\n Lowest quality files:`);
2139
+ for (const f of report.lowestQualityFiles) {
2140
+ const entry = report.byFile.find(b => b.file === f);
2141
+ console.log(` ${f}: ${(_a = entry === null || entry === void 0 ? void 0 : entry.score) !== null && _a !== void 0 ? _a : '?'}/100`);
2142
+ }
2143
+ }
2144
+ if (report.highestRiskLowQualityGaps.length > 0) {
2145
+ console.log(`\n High risk + low quality:`);
2146
+ for (const g of report.highestRiskLowQualityGaps) {
2147
+ console.log(` ${g.endpoint}: quality=${g.qualityScore}, risk=${g.riskScore}`);
2148
+ console.log(` → ${g.primaryIssue}`);
2149
+ }
2150
+ }
2151
+ console.log(`\n Report written to: ${options.reportsDir}/test-quality.json`);
2152
+ }
2153
+ catch (err) {
2154
+ if (err instanceof Error) {
2155
+ console.error(err.message);
2156
+ }
2157
+ process.exit(1);
2158
+ }
2159
+ });
2047
2160
  // Parse the command-line arguments
2048
2161
  program.parse(process.argv);
2049
2162
  // When invoked with no arguments (no subcommand), display help
@@ -1 +1 @@
1
- {"version":3,"file":"routeInference.d.ts","sourceRoot":"","sources":["../../../src/inference/routeInference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,eAAO,MAAM,YAAY,uEAAwE,CAAC;AAClG,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAqFD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAkIrE;AAiWD;;GAEG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAgBxE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAY5F"}
1
+ {"version":3,"file":"routeInference.d.ts","sourceRoot":"","sources":["../../../src/inference/routeInference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,eAAO,MAAM,YAAY,uEAAwE,CAAC;AAClG,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAmGD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAoJrE;AAkXD;;GAEG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAgBxE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAY5F"}
@@ -80,7 +80,18 @@ const JSDOC_ROUTE_PATTERN = /@route\s+\{(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)
80
80
  * Used to gate the URL+method object-form scan so we only activate it in
81
81
  * files that actually make HTTP calls (avoids scanning every JS file).
82
82
  */
83
- const HTTP_CLIENT_SIGNAL = /\$http|\bfetch\s*\(|\baxios\b/;
83
+ const HTTP_CLIENT_SIGNAL = /\$http|\bfetch\s*\(|\baxios\b|\bsuperagent\b/;
84
+ /**
85
+ * Matches client-side HTTP method calls: requests.get('/path'), api.post('/path'), etc.
86
+ * Group 1 = HTTP method, Group 2 = path literal starting with /
87
+ * Also handles: superagent.get(`${base}/path`) → captures the static suffix
88
+ */
89
+ const HTTP_CLIENT_METHOD_CALL = /\b\w+\s*\.\s*(get|post|put|patch|delete|del|head|options)\s*\(\s*(?:[^'"`\n]*?\+\s*)?['"`](\/[^'"`?\n]+)/i;
90
+ /**
91
+ * Signals that this JS/TS file is a client-side HTTP service layer.
92
+ * Expands on HTTP_CLIENT_SIGNAL to include superagent and common wrapper patterns.
93
+ */
94
+ const HTTP_SERVICE_SIGNAL = /\bsuperagent\b|\brequests\s*\.\s*(?:get|post|put|delete|del)\b|\bapi\s*\.\s*(?:get|post|put|delete)\b/;
84
95
  /**
85
96
  * Matches a `url:` property that ends with a literal path segment starting
86
97
  * with `/`. Handles both bare and concatenated forms:
@@ -162,6 +173,8 @@ function inferRoutesFromFile(filePath) {
162
173
  // We only enable the object-form method+url scanner for such files to
163
174
  // avoid false positives in ordinary JS/TS source.
164
175
  const usesHttpClient = HTTP_CLIENT_SIGNAL.test(content);
176
+ // Pre-check: does this file use a client-side HTTP service wrapper (requests.*, api.*, etc.)?
177
+ const usesHttpServiceWrapper = HTTP_SERVICE_SIGNAL.test(content);
165
178
  for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
166
179
  const line = lines[lineIdx];
167
180
  // 1a. router.method('/path', ...) — all on one line
@@ -239,6 +252,21 @@ function inferRoutesFromFile(filePath) {
239
252
  }
240
253
  }
241
254
  }
255
+ // 6. Client-side HTTP method calls: requests.get('/path'), api.post('/path')
256
+ // Only activate for files that contain HTTP service signals to avoid false positives
257
+ if (usesHttpServiceWrapper) {
258
+ const clientCallMatch = line.match(HTTP_CLIENT_METHOD_CALL);
259
+ if (clientCallMatch) {
260
+ let httpMethod = clientCallMatch[1].toLowerCase();
261
+ // Normalize 'del' → 'delete'
262
+ if (httpMethod === 'del')
263
+ httpMethod = 'delete';
264
+ if (exports.HTTP_METHODS.includes(httpMethod)) {
265
+ const routePath = clientCallMatch[2].split('?')[0]; // strip query string
266
+ addRoute(httpMethod, routePath, lineIdx + 1, 'code');
267
+ }
268
+ }
269
+ }
242
270
  }
243
271
  return [...byKey.values()];
244
272
  }
@@ -400,7 +428,7 @@ function findNextMethodNameInJava(javaLines, fromLine) {
400
428
  * @blueprint.route('/articles/<slug>', methods=['PUT'])
401
429
  * @app.route('/tags') ← defaults to GET
402
430
  */
403
- const FLASK_ROUTE = /@(\w+)\.route\s*\(\s*['"]([^'"]+)['"](?:\s*,\s*methods\s*=\s*\[([^\]]+)\])?\s*\)/;
431
+ const FLASK_ROUTE = /@(\w+)\.route\s*\(\s*['"]([^'"]+)['"](?:[^\n]*?\bmethods\s*=\s*[\[(]([^\])\n]+)[\])])?/;
404
432
  /**
405
433
  * Matches FastAPI decorators.
406
434
  * @app.get('/articles')
@@ -419,6 +447,22 @@ const FLASK_BLUEPRINT_CTOR = /(\w+)\s*=\s*Blueprint\s*\(\s*['"][^'"]+['"](?:\s*,
419
447
  * router = APIRouter(prefix="/articles")
420
448
  */
421
449
  const FASTAPI_APIROUTER = /(\w+)\s*=\s*APIRouter\s*\([^)]*?prefix\s*=\s*['"]([^'"]+)['"]/;
450
+ /**
451
+ * Scan ahead for a Python `def function_name(` line following a decorator.
452
+ * Skips additional decorator lines and stops at the first non-decorator,
453
+ * non-blank, non-comment line that is not a def.
454
+ */
455
+ function findPythonHandlerFunction(lines, fromLine) {
456
+ for (let j = fromLine + 1; j < Math.min(fromLine + 6, lines.length); j++) {
457
+ const m = lines[j].match(/^def\s+(\w+)\s*\(/);
458
+ if (m)
459
+ return m[1];
460
+ // Skip decorator lines
461
+ if (!lines[j].trim().startsWith('@') && lines[j].trim().length > 0 && !lines[j].trim().startsWith('#'))
462
+ break;
463
+ }
464
+ return undefined;
465
+ }
422
466
  /**
423
467
  * Infer routes from a Python (Flask/FastAPI) source file.
424
468
  */
@@ -432,10 +476,10 @@ function inferRoutesFromPythonFile(filePath) {
432
476
  }
433
477
  const lines = content.split('\n');
434
478
  const byKey = new Map();
435
- const addRoute = (method, routePath, lineNumber) => {
479
+ const addRoute = (method, routePath, lineNumber, handlerFunction) => {
436
480
  const key = `${method}:${routePath}`;
437
481
  if (!byKey.has(key)) {
438
- byKey.set(key, { method, path: routePath, sourceFile: filePath, lineNumber, discoveredVia: 'code' });
482
+ byKey.set(key, { method, path: routePath, handlerFunction, sourceFile: filePath, lineNumber, discoveredVia: 'code' });
439
483
  }
440
484
  };
441
485
  // Pass 1: Detect blueprint/router prefix for the file
@@ -463,18 +507,19 @@ function inferRoutesFromPythonFile(filePath) {
463
507
  const fullPath = localPrefix + (routePath.startsWith('/') ? routePath : '/' + routePath);
464
508
  // Normalize Flask <param> to {param}
465
509
  const normalizedPath = fullPath.replace(/<(?:\w+:)?(\w+)>/g, '{$1}');
510
+ const handlerFn = findPythonHandlerFunction(lines, i);
466
511
  if (methodsList) {
467
- // Parse methods=['GET', 'POST'] → individual routes
512
+ // Parse methods=['GET', 'POST'] or methods=('GET', 'POST') → individual routes
468
513
  const methods = methodsList.replace(/['"]/g, '').split(',').map((m) => m.trim().toLowerCase());
469
514
  for (const m of methods) {
470
515
  if (exports.HTTP_METHODS.includes(m)) {
471
- addRoute(m, normalizedPath, i + 1);
516
+ addRoute(m, normalizedPath, i + 1, handlerFn);
472
517
  }
473
518
  }
474
519
  }
475
520
  else {
476
521
  // Default to GET
477
- addRoute('get', normalizedPath, i + 1);
522
+ addRoute('get', normalizedPath, i + 1, handlerFn);
478
523
  }
479
524
  continue;
480
525
  }
@@ -484,8 +529,9 @@ function inferRoutesFromPythonFile(filePath) {
484
529
  const httpMethod = fastapiMatch[2].toLowerCase();
485
530
  const routePath = fastapiMatch[3];
486
531
  const fullPath = localPrefix + (routePath.startsWith('/') ? routePath : '/' + routePath);
532
+ const handlerFn = findPythonHandlerFunction(lines, i);
487
533
  // FastAPI uses {param} natively
488
- addRoute(httpMethod, fullPath, i + 1);
534
+ addRoute(httpMethod, fullPath, i + 1, handlerFn);
489
535
  continue;
490
536
  }
491
537
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-tests-coverage",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "CLI and library to measure how thoroughly your test suite exercises your API surface area",
5
5
  "main": "dist/src/lib/index.js",
6
6
  "types": "dist/src/lib/index.d.ts",