api-tests-coverage 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +703 -0
- package/config.yaml.example +227 -0
- package/dist/action/src/index.d.ts +2 -0
- package/dist/action/src/index.d.ts.map +1 -0
- package/dist/action/src/index.js +349 -0
- package/dist/action/src/prComment.d.ts +34 -0
- package/dist/action/src/prComment.d.ts.map +1 -0
- package/dist/action/src/prComment.js +146 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts +36 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts.map +1 -0
- package/dist/src/ast/astAnalysisOrchestrator.js +123 -0
- package/dist/src/ast/astTypes.d.ts +105 -0
- package/dist/src/ast/astTypes.d.ts.map +1 -0
- package/dist/src/ast/astTypes.js +9 -0
- package/dist/src/ast/languageAnalyzer.d.ts +46 -0
- package/dist/src/ast/languageAnalyzer.d.ts.map +1 -0
- package/dist/src/ast/languageAnalyzer.js +9 -0
- package/dist/src/ast/languageCapabilities.d.ts +24 -0
- package/dist/src/ast/languageCapabilities.d.ts.map +1 -0
- package/dist/src/ast/languageCapabilities.js +92 -0
- package/dist/src/ast/parseFile.d.ts +16 -0
- package/dist/src/ast/parseFile.d.ts.map +1 -0
- package/dist/src/ast/parseFile.js +65 -0
- package/dist/src/ast/parserRegistry.d.ts +39 -0
- package/dist/src/ast/parserRegistry.d.ts.map +1 -0
- package/dist/src/ast/parserRegistry.js +66 -0
- package/dist/src/buildSummary.d.ts +26 -0
- package/dist/src/buildSummary.d.ts.map +1 -0
- package/dist/src/buildSummary.js +193 -0
- package/dist/src/businessCoverage.d.ts +68 -0
- package/dist/src/businessCoverage.d.ts.map +1 -0
- package/dist/src/businessCoverage.js +290 -0
- package/dist/src/compatibilityCoverage.d.ts +83 -0
- package/dist/src/compatibilityCoverage.d.ts.map +1 -0
- package/dist/src/compatibilityCoverage.js +501 -0
- package/dist/src/config/defaultConfig.d.ts +9 -0
- package/dist/src/config/defaultConfig.d.ts.map +1 -0
- package/dist/src/config/defaultConfig.js +97 -0
- package/dist/src/config/index.d.ts +12 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +37 -0
- package/dist/src/config/loadConfig.d.ts +29 -0
- package/dist/src/config/loadConfig.d.ts.map +1 -0
- package/dist/src/config/loadConfig.js +135 -0
- package/dist/src/config/mergeConfig.d.ts +15 -0
- package/dist/src/config/mergeConfig.d.ts.map +1 -0
- package/dist/src/config/mergeConfig.js +57 -0
- package/dist/src/config/schema.d.ts +15 -0
- package/dist/src/config/schema.d.ts.map +1 -0
- package/dist/src/config/schema.js +30 -0
- package/dist/src/config/types.d.ts +175 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +9 -0
- package/dist/src/config/validateConfig.d.ts +22 -0
- package/dist/src/config/validateConfig.d.ts.map +1 -0
- package/dist/src/config/validateConfig.js +171 -0
- package/dist/src/config.d.ts +168 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +204 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts +67 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/callGraph.js +275 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts +23 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.js +394 -0
- package/dist/src/coverage/deep-analysis/index.d.ts +17 -0
- package/dist/src/coverage/deep-analysis/index.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/index.js +63 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts +60 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.js +121 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts +36 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.js +92 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts +55 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.js +152 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts +70 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.js +152 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts +80 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.js +216 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts +71 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.js +226 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts +58 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/symbolTable.js +230 -0
- package/dist/src/coverage/deep-analysis/types.d.ts +122 -0
- package/dist/src/coverage/deep-analysis/types.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/types.js +21 -0
- package/dist/src/discovery/fileClassifier.d.ts +50 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -0
- package/dist/src/discovery/fileClassifier.js +238 -0
- package/dist/src/discovery/projectDiscovery.d.ts +66 -0
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -0
- package/dist/src/discovery/projectDiscovery.js +287 -0
- package/dist/src/endpointCoverage.d.ts +70 -0
- package/dist/src/endpointCoverage.d.ts.map +1 -0
- package/dist/src/endpointCoverage.js +381 -0
- package/dist/src/errorCoverage.d.ts +93 -0
- package/dist/src/errorCoverage.d.ts.map +1 -0
- package/dist/src/errorCoverage.js +698 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +1441 -0
- package/dist/src/inference/businessRuleInference.d.ts +63 -0
- package/dist/src/inference/businessRuleInference.d.ts.map +1 -0
- package/dist/src/inference/businessRuleInference.js +268 -0
- package/dist/src/inference/integrationFlowInference.d.ts +56 -0
- package/dist/src/inference/integrationFlowInference.d.ts.map +1 -0
- package/dist/src/inference/integrationFlowInference.js +266 -0
- package/dist/src/integrationCoverage.d.ts +72 -0
- package/dist/src/integrationCoverage.d.ts.map +1 -0
- package/dist/src/integrationCoverage.js +317 -0
- package/dist/src/intelligence/index.d.ts +20 -0
- package/dist/src/intelligence/index.d.ts.map +1 -0
- package/dist/src/intelligence/index.js +105 -0
- package/dist/src/intelligence/linkageEngine.d.ts +20 -0
- package/dist/src/intelligence/linkageEngine.d.ts.map +1 -0
- package/dist/src/intelligence/linkageEngine.js +522 -0
- package/dist/src/intelligence/markdownReporter.d.ts +12 -0
- package/dist/src/intelligence/markdownReporter.d.ts.map +1 -0
- package/dist/src/intelligence/markdownReporter.js +265 -0
- package/dist/src/intelligence/riskScoring.d.ts +53 -0
- package/dist/src/intelligence/riskScoring.d.ts.map +1 -0
- package/dist/src/intelligence/riskScoring.js +181 -0
- package/dist/src/intelligence/types.d.ts +121 -0
- package/dist/src/intelligence/types.d.ts.map +1 -0
- package/dist/src/intelligence/types.js +8 -0
- package/dist/src/languageDetection.d.ts +100 -0
- package/dist/src/languageDetection.d.ts.map +1 -0
- package/dist/src/languageDetection.js +349 -0
- package/dist/src/languages/java/index.d.ts +16 -0
- package/dist/src/languages/java/index.d.ts.map +1 -0
- package/dist/src/languages/java/index.js +103 -0
- package/dist/src/languages/java/parser.d.ts +7 -0
- package/dist/src/languages/java/parser.d.ts.map +1 -0
- package/dist/src/languages/java/parser.js +50 -0
- package/dist/src/languages/java/semanticBuilder.d.ts +21 -0
- package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -0
- package/dist/src/languages/java/semanticBuilder.js +358 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts +20 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/annotationExtractor.js +94 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts +18 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/assertionResolver.js +150 -0
- package/dist/src/languages/javascript/callResolver.d.ts +23 -0
- package/dist/src/languages/javascript/callResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/callResolver.js +236 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts +23 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.js +205 -0
- package/dist/src/languages/javascript/index.d.ts +20 -0
- package/dist/src/languages/javascript/index.d.ts.map +1 -0
- package/dist/src/languages/javascript/index.js +136 -0
- package/dist/src/languages/javascript/parser.d.ts +14 -0
- package/dist/src/languages/javascript/parser.d.ts.map +1 -0
- package/dist/src/languages/javascript/parser.js +38 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts +31 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/symbolResolver.js +183 -0
- package/dist/src/languages/kotlin/index.d.ts +16 -0
- package/dist/src/languages/kotlin/index.d.ts.map +1 -0
- package/dist/src/languages/kotlin/index.js +151 -0
- package/dist/src/languages/kotlin/parser.d.ts +11 -0
- package/dist/src/languages/kotlin/parser.d.ts.map +1 -0
- package/dist/src/languages/kotlin/parser.js +74 -0
- package/dist/src/languages/python/index.d.ts +15 -0
- package/dist/src/languages/python/index.d.ts.map +1 -0
- package/dist/src/languages/python/index.js +293 -0
- package/dist/src/languages/ruby/index.d.ts +15 -0
- package/dist/src/languages/ruby/index.d.ts.map +1 -0
- package/dist/src/languages/ruby/index.js +274 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts +43 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts.map +1 -0
- package/dist/src/languages/shared/treeSitterUtils.js +100 -0
- package/dist/src/languages/typescript/index.d.ts +14 -0
- package/dist/src/languages/typescript/index.d.ts.map +1 -0
- package/dist/src/languages/typescript/index.js +25 -0
- package/dist/src/lib/index.d.ts +228 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +486 -0
- package/dist/src/mcp/client/index.d.ts +37 -0
- package/dist/src/mcp/client/index.d.ts.map +1 -0
- package/dist/src/mcp/client/index.js +235 -0
- package/dist/src/mcp/config.d.ts +50 -0
- package/dist/src/mcp/config.d.ts.map +1 -0
- package/dist/src/mcp/config.js +125 -0
- package/dist/src/mcp/events.d.ts +24 -0
- package/dist/src/mcp/events.d.ts.map +1 -0
- package/dist/src/mcp/events.js +48 -0
- package/dist/src/mcp/fallback/index.d.ts +50 -0
- package/dist/src/mcp/fallback/index.d.ts.map +1 -0
- package/dist/src/mcp/fallback/index.js +216 -0
- package/dist/src/mcp/index.d.ts +67 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +212 -0
- package/dist/src/mcp/normalizer.d.ts +21 -0
- package/dist/src/mcp/normalizer.d.ts.map +1 -0
- package/dist/src/mcp/normalizer.js +99 -0
- package/dist/src/mcp/prompts/index.d.ts +86 -0
- package/dist/src/mcp/prompts/index.d.ts.map +1 -0
- package/dist/src/mcp/prompts/index.js +304 -0
- package/dist/src/mcp/templates/index.d.ts +35 -0
- package/dist/src/mcp/templates/index.d.ts.map +1 -0
- package/dist/src/mcp/templates/index.js +143 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts +47 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts.map +1 -0
- package/dist/src/mcp/testing/mock-server/index.js +157 -0
- package/dist/src/mcp/types.d.ts +127 -0
- package/dist/src/mcp/types.d.ts.map +1 -0
- package/dist/src/mcp/types.js +8 -0
- package/dist/src/observability.d.ts +138 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +519 -0
- package/dist/src/parameterCoverage.d.ts +75 -0
- package/dist/src/parameterCoverage.d.ts.map +1 -0
- package/dist/src/parameterCoverage.js +629 -0
- package/dist/src/perfResilienceCoverage.d.ts +155 -0
- package/dist/src/perfResilienceCoverage.d.ts.map +1 -0
- package/dist/src/perfResilienceCoverage.js +670 -0
- package/dist/src/pluginLoader.d.ts +51 -0
- package/dist/src/pluginLoader.d.ts.map +1 -0
- package/dist/src/pluginLoader.js +72 -0
- package/dist/src/publishing.d.ts +63 -0
- package/dist/src/publishing.d.ts.map +1 -0
- package/dist/src/publishing.js +379 -0
- package/dist/src/qualityGate.d.ts +58 -0
- package/dist/src/qualityGate.d.ts.map +1 -0
- package/dist/src/qualityGate.js +118 -0
- package/dist/src/reporting.d.ts +41 -0
- package/dist/src/reporting.d.ts.map +1 -0
- package/dist/src/reporting.js +278 -0
- package/dist/src/screenshots.d.ts +71 -0
- package/dist/src/screenshots.d.ts.map +1 -0
- package/dist/src/screenshots.js +141 -0
- package/dist/src/security/gate/index.d.ts +11 -0
- package/dist/src/security/gate/index.d.ts.map +1 -0
- package/dist/src/security/gate/index.js +65 -0
- package/dist/src/security/index.d.ts +30 -0
- package/dist/src/security/index.d.ts.map +1 -0
- package/dist/src/security/index.js +342 -0
- package/dist/src/security/normalizers/semgrep.d.ts +10 -0
- package/dist/src/security/normalizers/semgrep.d.ts.map +1 -0
- package/dist/src/security/normalizers/semgrep.js +104 -0
- package/dist/src/security/normalizers/trivy.d.ts +10 -0
- package/dist/src/security/normalizers/trivy.d.ts.map +1 -0
- package/dist/src/security/normalizers/trivy.js +78 -0
- package/dist/src/security/normalizers/zap.d.ts +10 -0
- package/dist/src/security/normalizers/zap.d.ts.map +1 -0
- package/dist/src/security/normalizers/zap.js +104 -0
- package/dist/src/security/scanners/semgrep.d.ts +6 -0
- package/dist/src/security/scanners/semgrep.d.ts.map +1 -0
- package/dist/src/security/scanners/semgrep.js +125 -0
- package/dist/src/security/scanners/trivy.d.ts +6 -0
- package/dist/src/security/scanners/trivy.d.ts.map +1 -0
- package/dist/src/security/scanners/trivy.js +115 -0
- package/dist/src/security/scanners/zap.d.ts +6 -0
- package/dist/src/security/scanners/zap.d.ts.map +1 -0
- package/dist/src/security/scanners/zap.js +135 -0
- package/dist/src/security/types.d.ts +146 -0
- package/dist/src/security/types.d.ts.map +1 -0
- package/dist/src/security/types.js +6 -0
- package/dist/src/securityCoverage.d.ts +116 -0
- package/dist/src/securityCoverage.d.ts.map +1 -0
- package/dist/src/securityCoverage.js +725 -0
- package/dist/src/summary/buildSummary.d.ts +28 -0
- package/dist/src/summary/buildSummary.d.ts.map +1 -0
- package/dist/src/summary/buildSummary.js +257 -0
- package/dist/src/summary/evaluateMetrics.d.ts +31 -0
- package/dist/src/summary/evaluateMetrics.d.ts.map +1 -0
- package/dist/src/summary/evaluateMetrics.js +118 -0
- package/dist/src/summary/index.d.ts +10 -0
- package/dist/src/summary/index.d.ts.map +1 -0
- package/dist/src/summary/index.js +22 -0
- package/dist/src/summary/markdownRenderer.d.ts +139 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -0
- package/dist/src/summary/markdownRenderer.js +459 -0
- package/dist/src/summary/prSummary.d.ts +24 -0
- package/dist/src/summary/prSummary.d.ts.map +1 -0
- package/dist/src/summary/prSummary.js +233 -0
- package/dist/src/summary/summaryTypes.d.ts +35 -0
- package/dist/src/summary/summaryTypes.d.ts.map +1 -0
- package/dist/src/summary/summaryTypes.js +27 -0
- package/package.json +84 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { SupportedLanguage } from './languageDetection';
|
|
2
|
+
import type { DeepAnalysisConfig } from './coverage/deep-analysis/types';
|
|
3
|
+
import type { ResolutionType, ConfidenceLevel } from './coverage/deep-analysis/types';
|
|
4
|
+
import type { AstAnalysisConfig } from './config/types';
|
|
5
|
+
export interface Endpoint {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
/** Regex that matches concrete paths (e.g. /users/123 for /users/{id}) */
|
|
9
|
+
pathRegex: RegExp;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Metadata for a single match between a test call and an endpoint.
|
|
13
|
+
*/
|
|
14
|
+
export interface EndpointMatch {
|
|
15
|
+
/** How the endpoint was resolved */
|
|
16
|
+
resolutionType: ResolutionType;
|
|
17
|
+
/** Confidence level of the resolution */
|
|
18
|
+
confidence: ConfidenceLevel;
|
|
19
|
+
/** Whether the call was followed by a response assertion */
|
|
20
|
+
assertionLinked?: boolean;
|
|
21
|
+
/** The raw call text as seen in the source file */
|
|
22
|
+
rawCall?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface EndpointCoverage extends Endpoint {
|
|
25
|
+
covered: boolean;
|
|
26
|
+
testFiles: string[];
|
|
27
|
+
/** Languages from which this endpoint is covered (populated when --language is used). */
|
|
28
|
+
languages?: string[];
|
|
29
|
+
/** Deep-analysis metadata for each match (populated when deep analysis is enabled). */
|
|
30
|
+
matches?: EndpointMatch[];
|
|
31
|
+
}
|
|
32
|
+
export interface CoverageReport {
|
|
33
|
+
total: number;
|
|
34
|
+
covered: number;
|
|
35
|
+
percentage: number;
|
|
36
|
+
endpoints: EndpointCoverage[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert an OpenAPI path template (e.g. /users/{id}/orders) to a RegExp
|
|
40
|
+
* that matches concrete paths (e.g. /users/123/orders).
|
|
41
|
+
*/
|
|
42
|
+
export declare function pathToRegex(apiPath: string): RegExp;
|
|
43
|
+
/**
|
|
44
|
+
* Parse an OpenAPI/Swagger spec file and return a flat list of endpoints.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseOpenApiSpec(specPath: string): Promise<Endpoint[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Analyse test files matching the given glob pattern and determine which
|
|
49
|
+
* endpoints from the spec are covered.
|
|
50
|
+
*
|
|
51
|
+
* When `languages` is supplied the appropriate language-specific HTTP-call
|
|
52
|
+
* extractor is used for each file (based on its extension when `languages`
|
|
53
|
+
* contains `'auto'`, or the first matching language otherwise).
|
|
54
|
+
*
|
|
55
|
+
* When `deepAnalysisConfig` is supplied (and enabled), the deep analysis
|
|
56
|
+
* layer is also run to catch indirect calls (constants, templates, wrappers, etc.).
|
|
57
|
+
*/
|
|
58
|
+
export declare function analyzeTestCoverage(endpoints: Endpoint[], testGlob: string, languages?: SupportedLanguage[], deepAnalysisConfig?: DeepAnalysisConfig, astAnalysisConfig?: AstAnalysisConfig): Promise<EndpointCoverage[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Build the coverage summary numbers from a coverage map.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildCoverageReport(coverageMap: EndpointCoverage[]): CoverageReport;
|
|
63
|
+
/**
|
|
64
|
+
* Write JSON and HTML coverage reports to the given directory.
|
|
65
|
+
*
|
|
66
|
+
* The JSON report includes deep-analysis match metadata per endpoint.
|
|
67
|
+
* The HTML report shows resolution type and confidence badges.
|
|
68
|
+
*/
|
|
69
|
+
export declare function generateReports(report: CoverageReport, reportsDir: string): void;
|
|
70
|
+
//# sourceMappingURL=endpointCoverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpointCoverage.d.ts","sourceRoot":"","sources":["../../src/endpointCoverage.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,iBAAiB,EAIlB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,cAAc,EAAE,cAAc,CAAC;IAC/B,yCAAyC;IACzC,UAAU,EAAE,eAAe,CAAC;IAC5B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAiB,SAAQ,QAAQ;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,uFAAuF;IACvF,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAuB5E;AAwHD;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,QAAQ,EAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,iBAAiB,EAAE,EAC/B,kBAAkB,CAAC,EAAE,kBAAkB,EACvC,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAgF7B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAKnF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CA+GhF"}
|
|
@@ -0,0 +1,381 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.pathToRegex = pathToRegex;
|
|
40
|
+
exports.parseOpenApiSpec = parseOpenApiSpec;
|
|
41
|
+
exports.analyzeTestCoverage = analyzeTestCoverage;
|
|
42
|
+
exports.buildCoverageReport = buildCoverageReport;
|
|
43
|
+
exports.generateReports = generateReports;
|
|
44
|
+
const swagger_parser_1 = __importDefault(require("@apidevtools/swagger-parser"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
48
|
+
const languageDetection_1 = require("./languageDetection");
|
|
49
|
+
const types_1 = require("./coverage/deep-analysis/types");
|
|
50
|
+
const astAnalysisOrchestrator_1 = require("./ast/astAnalysisOrchestrator");
|
|
51
|
+
/**
|
|
52
|
+
* Convert an OpenAPI path template (e.g. /users/{id}/orders) to a RegExp
|
|
53
|
+
* that matches concrete paths (e.g. /users/123/orders).
|
|
54
|
+
*/
|
|
55
|
+
function pathToRegex(apiPath) {
|
|
56
|
+
// Split on parameter placeholders, escape each static segment, then rejoin
|
|
57
|
+
const pattern = apiPath
|
|
58
|
+
.split(/\{[^}]+\}/)
|
|
59
|
+
.map((segment) => segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
60
|
+
.join('[^\\/]+');
|
|
61
|
+
return new RegExp(`^${pattern}$`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse an OpenAPI/Swagger spec file and return a flat list of endpoints.
|
|
65
|
+
*/
|
|
66
|
+
async function parseOpenApiSpec(specPath) {
|
|
67
|
+
const api = await swagger_parser_1.default.dereference(specPath);
|
|
68
|
+
const endpoints = [];
|
|
69
|
+
const paths = api.paths;
|
|
70
|
+
if (!paths)
|
|
71
|
+
return endpoints;
|
|
72
|
+
const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'];
|
|
73
|
+
for (const [apiPath, pathItem] of Object.entries(paths)) {
|
|
74
|
+
if (!pathItem || typeof pathItem !== 'object')
|
|
75
|
+
continue;
|
|
76
|
+
for (const method of httpMethods) {
|
|
77
|
+
if (method in pathItem) {
|
|
78
|
+
endpoints.push({
|
|
79
|
+
method: method.toUpperCase(),
|
|
80
|
+
path: apiPath,
|
|
81
|
+
pathRegex: pathToRegex(apiPath),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return endpoints;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Search a file's contents for HTTP calls matching any of the given endpoints.
|
|
90
|
+
* Returns an array of matched endpoint indices.
|
|
91
|
+
*
|
|
92
|
+
* When `language` is provided the appropriate language-specific extractor is
|
|
93
|
+
* used; otherwise the JavaScript/TypeScript extractor is used as a fallback.
|
|
94
|
+
*/
|
|
95
|
+
function findCoveredEndpoints(fileContents, endpoints, language) {
|
|
96
|
+
const covered = new Set();
|
|
97
|
+
// Choose extractor based on language hint
|
|
98
|
+
const httpCalls = language && language !== 'auto'
|
|
99
|
+
? (0, languageDetection_1.extractHttpCalls)(fileContents, language)
|
|
100
|
+
: (0, languageDetection_1.extractHttpCallsFromJs)(fileContents);
|
|
101
|
+
for (const call of httpCalls) {
|
|
102
|
+
// Skip OpenAPI path templates that appear in comments/descriptions
|
|
103
|
+
if (call.path.includes('{'))
|
|
104
|
+
continue;
|
|
105
|
+
endpoints.forEach((endpoint, idx) => {
|
|
106
|
+
if (endpoint.method === call.method && endpoint.pathRegex.test(call.path)) {
|
|
107
|
+
covered.add(idx);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return covered;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Run deep analysis on a single file and return a set of endpoint indices
|
|
115
|
+
* that are covered along with their match metadata.
|
|
116
|
+
*
|
|
117
|
+
* Returns a Map of endpoint index → EndpointMatch[] (may be multiple matches).
|
|
118
|
+
*/
|
|
119
|
+
function findDeepCoveredEndpoints(fileContents, filePath, endpoints, language, deepConfig, astConfig) {
|
|
120
|
+
const deepMatches = new Map();
|
|
121
|
+
// Use the AST orchestrator as primary path (falls back to regex automatically)
|
|
122
|
+
const context = (0, astAnalysisOrchestrator_1.buildAnalysisContext)(astConfig, deepConfig);
|
|
123
|
+
const resolvedCalls = (0, astAnalysisOrchestrator_1.analyzeFile)(fileContents, filePath, language, context);
|
|
124
|
+
for (const resolved of resolvedCalls) {
|
|
125
|
+
// Try both the raw path and the normalized path against each endpoint
|
|
126
|
+
const pathsToTry = [resolved.path];
|
|
127
|
+
if (resolved.normalizedPath && resolved.normalizedPath !== resolved.path) {
|
|
128
|
+
pathsToTry.push(resolved.normalizedPath);
|
|
129
|
+
}
|
|
130
|
+
endpoints.forEach((endpoint, idx) => {
|
|
131
|
+
if (endpoint.method !== resolved.method)
|
|
132
|
+
return;
|
|
133
|
+
for (const candidatePath of pathsToTry) {
|
|
134
|
+
// Skip if it still contains unresolved placeholders that don't match templates
|
|
135
|
+
// Accept both concrete paths matching the regex and template paths matching the spec path
|
|
136
|
+
const matchesConcrete = !candidatePath.includes('{') && endpoint.pathRegex.test(candidatePath);
|
|
137
|
+
const matchesTemplate = candidatePath === endpoint.path;
|
|
138
|
+
// Also try matching normalized path patterns
|
|
139
|
+
const matchesNormalized = candidatePath.includes('{') && templatePathsMatch(candidatePath, endpoint.path);
|
|
140
|
+
if (matchesConcrete || matchesTemplate || matchesNormalized) {
|
|
141
|
+
const match = {
|
|
142
|
+
resolutionType: resolved.resolutionType,
|
|
143
|
+
confidence: resolved.confidence,
|
|
144
|
+
assertionLinked: resolved.assertionLinked,
|
|
145
|
+
rawCall: resolved.rawCall,
|
|
146
|
+
};
|
|
147
|
+
const existing = deepMatches.get(idx);
|
|
148
|
+
if (existing) {
|
|
149
|
+
// Avoid duplicate resolution types for same endpoint
|
|
150
|
+
const isDuplicate = existing.some((m) => m.resolutionType === match.resolutionType && m.confidence === match.confidence);
|
|
151
|
+
if (!isDuplicate)
|
|
152
|
+
existing.push(match);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
deepMatches.set(idx, [match]);
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return deepMatches;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Compare two OpenAPI-style path templates for structural equivalence.
|
|
166
|
+
* e.g. /users/{id} matches /users/{userId}
|
|
167
|
+
*/
|
|
168
|
+
function templatePathsMatch(a, b) {
|
|
169
|
+
const partsA = a.split('/');
|
|
170
|
+
const partsB = b.split('/');
|
|
171
|
+
if (partsA.length !== partsB.length)
|
|
172
|
+
return false;
|
|
173
|
+
return partsA.every((seg, i) => {
|
|
174
|
+
const segB = partsB[i];
|
|
175
|
+
// Both are params → match
|
|
176
|
+
if (/^\{.+\}$/.test(seg) && /^\{.+\}$/.test(segB))
|
|
177
|
+
return true;
|
|
178
|
+
// One is param, other isn't
|
|
179
|
+
if (/^\{.+\}$/.test(seg) || /^\{.+\}$/.test(segB))
|
|
180
|
+
return false;
|
|
181
|
+
return seg === segB;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Analyse test files matching the given glob pattern and determine which
|
|
186
|
+
* endpoints from the spec are covered.
|
|
187
|
+
*
|
|
188
|
+
* When `languages` is supplied the appropriate language-specific HTTP-call
|
|
189
|
+
* extractor is used for each file (based on its extension when `languages`
|
|
190
|
+
* contains `'auto'`, or the first matching language otherwise).
|
|
191
|
+
*
|
|
192
|
+
* When `deepAnalysisConfig` is supplied (and enabled), the deep analysis
|
|
193
|
+
* layer is also run to catch indirect calls (constants, templates, wrappers, etc.).
|
|
194
|
+
*/
|
|
195
|
+
async function analyzeTestCoverage(endpoints, testGlob, languages, deepAnalysisConfig, astAnalysisConfig) {
|
|
196
|
+
var _a, _b;
|
|
197
|
+
const testFiles = await (0, fast_glob_1.default)(testGlob, { onlyFiles: true });
|
|
198
|
+
const coverageMap = endpoints.map((ep) => ({
|
|
199
|
+
...ep,
|
|
200
|
+
covered: false,
|
|
201
|
+
testFiles: [],
|
|
202
|
+
languages: [],
|
|
203
|
+
matches: [],
|
|
204
|
+
}));
|
|
205
|
+
const deepConfig = deepAnalysisConfig !== null && deepAnalysisConfig !== void 0 ? deepAnalysisConfig : types_1.DEFAULT_DEEP_ANALYSIS_CONFIG;
|
|
206
|
+
for (const filePath of testFiles) {
|
|
207
|
+
const contents = fs.readFileSync(filePath, 'utf-8');
|
|
208
|
+
// Determine the language for this file
|
|
209
|
+
let lang;
|
|
210
|
+
if (languages && languages.length > 0 && !languages.includes('auto')) {
|
|
211
|
+
// Use the first explicitly provided language
|
|
212
|
+
lang = languages[0];
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
// Auto-detect from extension
|
|
216
|
+
lang = (_a = (0, languageDetection_1.detectLanguageFromExtension)(filePath)) !== null && _a !== void 0 ? _a : undefined;
|
|
217
|
+
}
|
|
218
|
+
const fileLanguage = lang !== null && lang !== void 0 ? lang : 'javascript';
|
|
219
|
+
// ── Direct regex-based coverage detection ─────────────────────────────
|
|
220
|
+
const directCoveredIndices = findCoveredEndpoints(contents, endpoints, lang);
|
|
221
|
+
for (const idx of directCoveredIndices) {
|
|
222
|
+
coverageMap[idx].covered = true;
|
|
223
|
+
if (!coverageMap[idx].testFiles.includes(filePath)) {
|
|
224
|
+
coverageMap[idx].testFiles.push(filePath);
|
|
225
|
+
}
|
|
226
|
+
if (!coverageMap[idx].languages.includes(fileLanguage)) {
|
|
227
|
+
coverageMap[idx].languages.push(fileLanguage);
|
|
228
|
+
}
|
|
229
|
+
// Record as a direct match
|
|
230
|
+
const alreadyHasDirect = (_b = coverageMap[idx].matches) === null || _b === void 0 ? void 0 : _b.some((m) => m.resolutionType === 'direct');
|
|
231
|
+
if (!alreadyHasDirect) {
|
|
232
|
+
coverageMap[idx].matches.push({ resolutionType: 'direct', confidence: 'high' });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// ── Deep analysis coverage detection ──────────────────────────────────
|
|
236
|
+
if (deepConfig.enabled) {
|
|
237
|
+
const deepCovered = findDeepCoveredEndpoints(contents, filePath, endpoints, fileLanguage, deepConfig, astAnalysisConfig);
|
|
238
|
+
for (const [idx, matches] of deepCovered.entries()) {
|
|
239
|
+
// Skip if already covered by direct detection (don't downgrade)
|
|
240
|
+
coverageMap[idx].covered = true;
|
|
241
|
+
if (!coverageMap[idx].testFiles.includes(filePath)) {
|
|
242
|
+
coverageMap[idx].testFiles.push(filePath);
|
|
243
|
+
}
|
|
244
|
+
if (!coverageMap[idx].languages.includes(fileLanguage)) {
|
|
245
|
+
coverageMap[idx].languages.push(fileLanguage);
|
|
246
|
+
}
|
|
247
|
+
for (const match of matches) {
|
|
248
|
+
// Don't add duplicate match entries
|
|
249
|
+
const isDuplicate = coverageMap[idx].matches.some((m) => m.resolutionType === match.resolutionType && m.rawCall === match.rawCall);
|
|
250
|
+
if (!isDuplicate) {
|
|
251
|
+
coverageMap[idx].matches.push(match);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return coverageMap;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Build the coverage summary numbers from a coverage map.
|
|
261
|
+
*/
|
|
262
|
+
function buildCoverageReport(coverageMap) {
|
|
263
|
+
const total = coverageMap.length;
|
|
264
|
+
const coveredCount = coverageMap.filter((ep) => ep.covered).length;
|
|
265
|
+
const percentage = total === 0 ? 0 : Math.round((coveredCount / total) * 10000) / 100;
|
|
266
|
+
return { total, covered: coveredCount, percentage, endpoints: coverageMap };
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Write JSON and HTML coverage reports to the given directory.
|
|
270
|
+
*
|
|
271
|
+
* The JSON report includes deep-analysis match metadata per endpoint.
|
|
272
|
+
* The HTML report shows resolution type and confidence badges.
|
|
273
|
+
*/
|
|
274
|
+
function generateReports(report, reportsDir) {
|
|
275
|
+
if (!fs.existsSync(reportsDir)) {
|
|
276
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
// ── JSON report ──────────────────────────────────────────────────────────
|
|
279
|
+
const jsonPath = path.join(reportsDir, 'endpoint-coverage.json');
|
|
280
|
+
const jsonReport = {
|
|
281
|
+
total: report.total,
|
|
282
|
+
covered: report.covered,
|
|
283
|
+
percentage: report.percentage,
|
|
284
|
+
endpoints: report.endpoints.map(({ method, path: p, covered, testFiles, languages, matches }) => ({
|
|
285
|
+
method,
|
|
286
|
+
path: p,
|
|
287
|
+
covered,
|
|
288
|
+
testFiles,
|
|
289
|
+
...(languages && languages.length > 0 ? { languages } : {}),
|
|
290
|
+
...(matches && matches.length > 0 ? { matches } : {}),
|
|
291
|
+
})),
|
|
292
|
+
};
|
|
293
|
+
fs.writeFileSync(jsonPath, JSON.stringify(jsonReport, null, 2), 'utf-8');
|
|
294
|
+
// ── HTML report ──────────────────────────────────────────────────────────
|
|
295
|
+
const htmlPath = path.join(reportsDir, 'endpoint-coverage.html');
|
|
296
|
+
const rows = report.endpoints
|
|
297
|
+
.map(({ method, path: p, covered, testFiles, languages, matches }) => {
|
|
298
|
+
const rowClass = covered ? 'covered' : 'uncovered';
|
|
299
|
+
const status = covered ? '✓ Covered' : '✗ Not covered';
|
|
300
|
+
const files = testFiles.length > 0 ? testFiles.join('<br>') : '—';
|
|
301
|
+
const langs = languages && languages.length > 0 ? languages.join(', ') : '—';
|
|
302
|
+
// Resolution type + confidence badges
|
|
303
|
+
let matchBadges = '—';
|
|
304
|
+
if (matches && matches.length > 0) {
|
|
305
|
+
const uniqueTypes = [...new Set(matches.map((m) => m.resolutionType))];
|
|
306
|
+
matchBadges = uniqueTypes
|
|
307
|
+
.map((rt) => {
|
|
308
|
+
var _a;
|
|
309
|
+
const bestMatch = matches.find((m) => m.resolutionType === rt);
|
|
310
|
+
const conf = (_a = bestMatch === null || bestMatch === void 0 ? void 0 : bestMatch.confidence) !== null && _a !== void 0 ? _a : 'medium';
|
|
311
|
+
const assertLinked = (bestMatch === null || bestMatch === void 0 ? void 0 : bestMatch.assertionLinked) ? ' ✓' : '';
|
|
312
|
+
const confClass = conf === 'high' ? 'conf-high' : conf === 'medium' ? 'conf-medium' : 'conf-low';
|
|
313
|
+
return `<span class="badge badge-${rt}">${rt}</span> <span class="${confClass}">${conf}${assertLinked}</span>`;
|
|
314
|
+
})
|
|
315
|
+
.join(' ');
|
|
316
|
+
}
|
|
317
|
+
return ` <tr class="${rowClass}">
|
|
318
|
+
<td>${method}</td>
|
|
319
|
+
<td>${p}</td>
|
|
320
|
+
<td>${status}</td>
|
|
321
|
+
<td>${files}</td>
|
|
322
|
+
<td>${langs}</td>
|
|
323
|
+
<td>${matchBadges}</td>
|
|
324
|
+
</tr>`;
|
|
325
|
+
})
|
|
326
|
+
.join('\n');
|
|
327
|
+
const html = `<!DOCTYPE html>
|
|
328
|
+
<html lang="en">
|
|
329
|
+
<head>
|
|
330
|
+
<meta charset="UTF-8">
|
|
331
|
+
<title>Endpoint Coverage Report</title>
|
|
332
|
+
<style>
|
|
333
|
+
body { font-family: sans-serif; padding: 2rem; }
|
|
334
|
+
h1 { margin-bottom: 0.5rem; }
|
|
335
|
+
.summary { margin-bottom: 1.5rem; font-size: 1.1rem; }
|
|
336
|
+
table { border-collapse: collapse; width: 100%; }
|
|
337
|
+
th, td { border: 1px solid #ccc; padding: 0.5rem 1rem; text-align: left; }
|
|
338
|
+
th { background: #f0f0f0; }
|
|
339
|
+
tr.covered { background: #e6ffe6; }
|
|
340
|
+
tr.uncovered { background: #ffe6e6; }
|
|
341
|
+
.badge { display: inline-block; padding: 0.1rem 0.4rem; border-radius: 3px;
|
|
342
|
+
font-size: 0.75rem; font-weight: 600; color: #fff;
|
|
343
|
+
background: #555; margin-right: 0.2rem; }
|
|
344
|
+
.badge-direct { background: #2a9d8f; }
|
|
345
|
+
.badge-constant { background: #457b9d; }
|
|
346
|
+
.badge-enum { background: #6a4c93; }
|
|
347
|
+
.badge-string-template { background: #e9c46a; color: #333; }
|
|
348
|
+
.badge-wrapper-method { background: #e76f51; }
|
|
349
|
+
.badge-request-builder { background: #264653; }
|
|
350
|
+
.badge-client-mapping { background: #c77dff; }
|
|
351
|
+
.badge-heuristic { background: #aaa; }
|
|
352
|
+
.conf-high { color: #2a9d8f; font-weight: 600; font-size: 0.8rem; }
|
|
353
|
+
.conf-medium { color: #e9c46a; font-weight: 600; font-size: 0.8rem; }
|
|
354
|
+
.conf-low { color: #e76f51; font-weight: 600; font-size: 0.8rem; }
|
|
355
|
+
</style>
|
|
356
|
+
</head>
|
|
357
|
+
<body>
|
|
358
|
+
<h1>Endpoint Coverage Report</h1>
|
|
359
|
+
<div class="summary">
|
|
360
|
+
Covered: <strong>${report.covered}/${report.total}</strong> endpoints
|
|
361
|
+
(<strong>${report.percentage}%</strong>)
|
|
362
|
+
</div>
|
|
363
|
+
<table>
|
|
364
|
+
<thead>
|
|
365
|
+
<tr>
|
|
366
|
+
<th>Method</th>
|
|
367
|
+
<th>Path</th>
|
|
368
|
+
<th>Status</th>
|
|
369
|
+
<th>Test Files</th>
|
|
370
|
+
<th>Languages</th>
|
|
371
|
+
<th>Resolution</th>
|
|
372
|
+
</tr>
|
|
373
|
+
</thead>
|
|
374
|
+
<tbody>
|
|
375
|
+
${rows}
|
|
376
|
+
</tbody>
|
|
377
|
+
</table>
|
|
378
|
+
</body>
|
|
379
|
+
</html>`;
|
|
380
|
+
fs.writeFileSync(htmlPath, html, 'utf-8');
|
|
381
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { AstAnalysisConfig, DeepAnalysisCoverageConfig } from './config/types';
|
|
2
|
+
export type ErrorCategory = 'missing-parameter' | 'invalid-value' | 'unauthorized' | 'forbidden' | 'not-found' | 'conflict' | 'server-error';
|
|
3
|
+
export declare const ERROR_CATEGORIES: ErrorCategory[];
|
|
4
|
+
export interface ErrorScenario {
|
|
5
|
+
/** Unique key, e.g. "POST /users:400" */
|
|
6
|
+
id: string;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
method: string;
|
|
9
|
+
path: string;
|
|
10
|
+
statusCode: number;
|
|
11
|
+
description: string;
|
|
12
|
+
categories: ErrorCategory[];
|
|
13
|
+
}
|
|
14
|
+
export interface ErrorScenarioCoverage {
|
|
15
|
+
scenario: ErrorScenario;
|
|
16
|
+
covered: boolean;
|
|
17
|
+
/** Test description strings that matched this scenario */
|
|
18
|
+
matchedTests: string[];
|
|
19
|
+
/**
|
|
20
|
+
* AST metadata when coverage was informed by semantic analysis.
|
|
21
|
+
*/
|
|
22
|
+
astMetadata?: {
|
|
23
|
+
sourceLanguage?: string;
|
|
24
|
+
resolutionType?: string;
|
|
25
|
+
confidence?: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Options to enable AST-augmented error coverage analysis.
|
|
30
|
+
*/
|
|
31
|
+
export interface AstErrorAnalysisOptions {
|
|
32
|
+
astConfig: AstAnalysisConfig;
|
|
33
|
+
deepConfig?: DeepAnalysisCoverageConfig;
|
|
34
|
+
}
|
|
35
|
+
export interface CategorySummary {
|
|
36
|
+
total: number;
|
|
37
|
+
covered: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ErrorCoverageReport {
|
|
40
|
+
total: number;
|
|
41
|
+
covered: number;
|
|
42
|
+
percentage: number;
|
|
43
|
+
scenarios: ErrorScenarioCoverage[];
|
|
44
|
+
categorySummary: Record<ErrorCategory, CategorySummary>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Map a HTTP status code (and optional description) to one or more error
|
|
48
|
+
* categories that are relevant for heuristic matching.
|
|
49
|
+
*/
|
|
50
|
+
export declare function statusCodeToCategories(statusCode: number, description: string): ErrorCategory[];
|
|
51
|
+
/**
|
|
52
|
+
* Parse an OpenAPI 3 spec and return all non-2xx (error) response scenarios.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseErrorScenarios(specPath: string): Promise<ErrorScenario[]>;
|
|
55
|
+
interface TestSegment {
|
|
56
|
+
description: string;
|
|
57
|
+
content: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Return true if the test segment appears to exercise the endpoint associated
|
|
61
|
+
* with the given error scenario.
|
|
62
|
+
*
|
|
63
|
+
* Detection strategy (in order):
|
|
64
|
+
* 1. Description contains "METHOD /pathBase" (e.g. "POST /users")
|
|
65
|
+
* 2. Description contains the path base
|
|
66
|
+
* 3. Content contains the path base AND the HTTP method as a string literal
|
|
67
|
+
*/
|
|
68
|
+
export declare function segmentMentionsEndpoint(segment: TestSegment, scenario: ErrorScenario): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Return true when a segment's description or content hints that it is
|
|
71
|
+
* exercising the given error category.
|
|
72
|
+
*/
|
|
73
|
+
export declare function matchesCategoryHeuristic(segment: TestSegment, category: ErrorCategory): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Decide whether a single test segment covers the given error scenario.
|
|
76
|
+
*/
|
|
77
|
+
export declare function segmentCoversScenario(segment: TestSegment, scenario: ErrorScenario): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Scan test files matching testGlob and determine which error scenarios are
|
|
80
|
+
* covered. Optionally augments text-scan results with AST-derived semantic
|
|
81
|
+
* signals when `astOptions` is provided.
|
|
82
|
+
*/
|
|
83
|
+
export declare function analyzeErrorCoverage(scenarios: ErrorScenario[], testGlob: string, astOptions?: AstErrorAnalysisOptions): Promise<ErrorScenarioCoverage[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Aggregate error coverage results into a summary report.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildErrorCoverageReport(coverages: ErrorScenarioCoverage[]): ErrorCoverageReport;
|
|
88
|
+
/**
|
|
89
|
+
* Write JSON and HTML error-coverage reports to reportsDir.
|
|
90
|
+
*/
|
|
91
|
+
export declare function generateErrorReports(report: ErrorCoverageReport, reportsDir: string): void;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=errorCoverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorCoverage.d.ts","sourceRoot":"","sources":["../../src/errorCoverage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAUpF,MAAM,MAAM,aAAa,GACrB,mBAAmB,GACnB,eAAe,GACf,cAAc,GACd,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,CAAC;AAEnB,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAQ3C,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;OAEG;IACH,WAAW,CAAC,EAAE;QACZ,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;CACzD;AAUD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CAkD/F;AAID;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA2CpF;AAID,UAAU,WAAW;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAqB9F;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CA8G/F;AA2DD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAe5F;AA8HD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,aAAa,EAAE,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,uBAAuB,GACnC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CA0ElC;AAID;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,qBAAqB,EAAE,GACjC,mBAAmB,CAsBrB;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAkH1F"}
|