api-tests-coverage 1.0.23 → 1.0.25
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 +39 -0
- package/config.yaml.example +35 -0
- package/dist/dashboard/dist/assets/_basePickBy-BHjg34fk.js +1 -0
- package/dist/dashboard/dist/assets/_basePickBy-D4Hl8chy.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-BSUUnV_V.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-DxJYHd7T.js +1 -0
- package/dist/dashboard/dist/assets/arc-DcXkmNi0.js +1 -0
- package/dist/dashboard/dist/assets/arc-DhDluTY5.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-ChMY32ql.js +36 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-DGlUU7dC.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-CgXi3kEZ.js +122 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-DVhWtRxG.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B6esYq70.js +10 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-Cr3xB15y.js +10 -0
- package/dist/dashboard/dist/assets/channel-DYAie-7m.js +1 -0
- package/dist/dashboard/dist/assets/channel-Df6s6dhy.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-B7Pkx3C3.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BaW3__pI.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-8ClDkPsD.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-DyYevfEQ.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW--cjprmFF.js +165 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-C2bwZFec.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-D9bxNSnS.js +220 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-DO0T2xne.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-CCYA4j_f.js +15 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-CSek7h3u.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-BRCzcTtl.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-Cdhqs7xo.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-BzHw38Ki.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-TFdw1-iS.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CWotsEVz.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-dkJ0rsgF.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-DiIv5Pho.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dr8j2BkV.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-DiIv5Pho.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dr8j2BkV.js +1 -0
- package/dist/dashboard/dist/assets/clone-B4LorrSy.js +1 -0
- package/dist/dashboard/dist/assets/clone-D-A0zWrx.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-L06bC_vI.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-jzGbyPIS.js +1 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-D7rgvBx1.js +4 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-LQJxsDjp.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-2rYklqon.js +24 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-Bguvtjhb.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CDM-bAUc.js +43 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CGrvALqm.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DA3c-QP4.js +24 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DNQuKOCA.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BsYH8cLH.js +60 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-CgAEujxC.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-C8juupCT.js +162 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Da_JhBCy.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BzXOAiOm.js +267 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-D8FTswNn.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-6Rn0oWgA.js +65 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BFJR-ITH.js +65 -0
- package/dist/dashboard/dist/assets/graph-CIvnjOQQ.js +1 -0
- package/dist/dashboard/dist/assets/graph-VO6A5Zyb.js +1 -0
- package/dist/dashboard/dist/assets/index-BD_Ue7zI.js +777 -0
- package/dist/dashboard/dist/assets/index-BWX0sSZn.css +1 -0
- package/dist/dashboard/dist/assets/index-CbAFWEor.js +777 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BEOgUULT.js +2 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-OcK0Lxgi.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-CBFUW_L2.js +139 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DTJukVOY.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-BXpodEnf.js +89 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-Di65fNuD.js +89 -0
- package/dist/dashboard/dist/assets/layout-Cpj8l95P.js +1 -0
- package/dist/dashboard/dist/assets/layout-DAt24RVX.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-DxI8MXCF.js +68 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-_3DZbNEl.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-B--OM1Gs.js +30 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-BafKx3_Y.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-BcZsArkk.js +7 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CDx0v76p.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-CbvZ1a-7.js +64 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-CqFAO2t6.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-CqSaCg-3.js +10 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-D-fji9s3.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-6IXD1uqW.js +145 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-CWB1Ub2x.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DvSVQAfp.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-J-c1KNJ7.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-BMFdt0QQ.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-DRL2jF9p.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-Cll7Nvth.js +61 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-LOxOovzx.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-C6DntuKu.js +162 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-DtqX8zNC.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BKisDUaz.js +7 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-zxwS9i0A.js +7 -0
- package/dist/dashboard/dist/index.html +2 -2
- package/dist/dashboard/dist/reports/coverage-summary.json +75 -1
- package/dist/dashboard/dist/reports/security-full.json +157 -0
- package/dist/src/compatibilityCoverage.d.ts +34 -15
- package/dist/src/compatibilityCoverage.d.ts.map +1 -1
- package/dist/src/compatibilityCoverage.js +387 -85
- package/dist/src/config/defaultConfig.d.ts.map +1 -1
- package/dist/src/config/defaultConfig.js +62 -0
- package/dist/src/config/schema.d.ts.map +1 -1
- package/dist/src/config/schema.js +1 -1
- package/dist/src/config/types.d.ts +81 -1
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/config/validateConfig.d.ts.map +1 -1
- package/dist/src/config/validateConfig.js +126 -0
- package/dist/src/contracts/compatibilityMatrix.d.ts +20 -0
- package/dist/src/contracts/compatibilityMatrix.d.ts.map +1 -0
- package/dist/src/contracts/compatibilityMatrix.js +198 -0
- package/dist/src/contracts/pactBrokerClient.d.ts +10 -0
- package/dist/src/contracts/pactBrokerClient.d.ts.map +1 -0
- package/dist/src/contracts/pactBrokerClient.js +117 -0
- package/dist/src/contracts/schemaEvolutionChecker.d.ts +17 -0
- package/dist/src/contracts/schemaEvolutionChecker.d.ts.map +1 -0
- package/dist/src/contracts/schemaEvolutionChecker.js +95 -0
- package/dist/src/contracts/springCloudContractParser.d.ts +10 -0
- package/dist/src/contracts/springCloudContractParser.d.ts.map +1 -0
- package/dist/src/contracts/springCloudContractParser.js +144 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.js +25 -0
- package/dist/src/discovery/projectDiscovery.d.ts +2 -0
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
- package/dist/src/discovery/projectDiscovery.js +25 -25
- package/dist/src/index.js +233 -16
- package/dist/src/inference/routeInference.d.ts +10 -2
- package/dist/src/inference/routeInference.d.ts.map +1 -1
- package/dist/src/inference/routeInference.js +363 -62
- package/dist/src/languageDetection.d.ts.map +1 -1
- package/dist/src/languageDetection.js +21 -4
- package/dist/src/lib/index.d.ts +3 -0
- package/dist/src/lib/index.d.ts.map +1 -1
- package/dist/src/lib/index.js +3 -1
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +152 -79
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +5 -1
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testEndpointMapper.js +356 -42
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +20 -5
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/tiaStage.js +3 -1
- package/dist/src/pipeline/stages/tia/types.d.ts +11 -2
- package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -1
- package/dist/src/projectDefaults.d.ts +6 -0
- package/dist/src/projectDefaults.d.ts.map +1 -0
- package/dist/src/projectDefaults.js +43 -0
- package/dist/src/security/hub.d.ts +81 -0
- package/dist/src/security/hub.d.ts.map +1 -0
- package/dist/src/security/hub.js +420 -0
- package/dist/src/security/index.d.ts +1 -0
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +8 -2
- package/dist/src/security/normalizers/gitleaks.d.ts +7 -0
- package/dist/src/security/normalizers/gitleaks.d.ts.map +1 -0
- package/dist/src/security/normalizers/gitleaks.js +32 -0
- package/dist/src/security/scanners/gitleaks.d.ts +3 -0
- package/dist/src/security/scanners/gitleaks.d.ts.map +1 -0
- package/dist/src/security/scanners/gitleaks.js +105 -0
- package/dist/src/security/scanners/semgrep.d.ts.map +1 -1
- package/dist/src/security/scanners/semgrep.js +24 -2
- package/dist/src/security/scanners/trivy.d.ts.map +1 -1
- package/dist/src/security/scanners/trivy.js +24 -2
- package/dist/src/security/scanners/zap.d.ts.map +1 -1
- package/dist/src/security/scanners/zap.js +27 -2
- package/dist/src/security/types.d.ts +15 -1
- package/dist/src/security/types.d.ts.map +1 -1
- package/dist/src/streaming/schema/index.d.ts +23 -0
- package/dist/src/streaming/schema/index.d.ts.map +1 -0
- package/dist/src/streaming/schema/index.js +196 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
- package/dist/src/summary/markdownRenderer.js +15 -1
- package/dist/src/summary/summaryTypes.d.ts.map +1 -1
- package/dist/src/summary/summaryTypes.js +1 -0
- package/dist/src/unitAnalysis.d.ts +145 -0
- package/dist/src/unitAnalysis.d.ts.map +1 -0
- package/dist/src/unitAnalysis.js +1392 -0
- package/package.json +1 -1
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* 5. Framework metadata: Framework annotations link test to endpoint
|
|
11
11
|
* 6. Helper traversal: URL resolved through helper function chain
|
|
12
12
|
* 7. Page object: URL resolved through page object pattern
|
|
13
|
+
* 8. Service call: test covers a service method that is linked to an endpoint via call graph
|
|
14
|
+
* 9. Business rule: test method name implies a business rule covered by the test
|
|
13
15
|
*/
|
|
14
16
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
17
|
if (k2 === undefined) k2 = k;
|
|
@@ -46,89 +48,401 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
46
48
|
})();
|
|
47
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
50
|
exports.mapTestsToEndpoints = mapTestsToEndpoints;
|
|
51
|
+
const testLayerClassifier_1 = require("./testLayerClassifier");
|
|
52
|
+
const parameterizedTestExpander_1 = require("./parameterizedTestExpander");
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
49
54
|
const path = __importStar(require("path"));
|
|
50
55
|
/**
|
|
51
56
|
* Map test files to endpoints using available evidence in the graph.
|
|
52
57
|
*
|
|
53
58
|
* @param graph - The coverage knowledge graph (from AST stage)
|
|
54
59
|
* @param testFiles - List of test file paths
|
|
60
|
+
* @param fileContents - Optional test file contents keyed by file path. When absent,
|
|
61
|
+
* the mapper falls back to graph node metadata and finally to the filesystem.
|
|
55
62
|
*/
|
|
56
|
-
function mapTestsToEndpoints(graph, testFiles) {
|
|
57
|
-
var _a, _b;
|
|
63
|
+
function mapTestsToEndpoints(graph, testFiles, fileContents = {}) {
|
|
64
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
58
65
|
const mappings = [];
|
|
59
|
-
// Get all endpoint nodes
|
|
60
66
|
const endpointNodes = graph.getNodesByType('endpoint');
|
|
61
|
-
|
|
62
|
-
return mappings;
|
|
67
|
+
const seen = new Set();
|
|
63
68
|
for (const testFile of testFiles) {
|
|
64
69
|
const fileNodeId = `file:${testFile}`;
|
|
65
|
-
|
|
70
|
+
const content = resolveTestContent(graph, testFile, fileContents);
|
|
71
|
+
const insights = analyzeTestFile(graph, testFile, content);
|
|
72
|
+
const pushMapping = (mapping) => {
|
|
73
|
+
var _a, _b, _c, _d;
|
|
74
|
+
const key = [
|
|
75
|
+
mapping.testFilePath,
|
|
76
|
+
(_a = mapping.endpointId) !== null && _a !== void 0 ? _a : '',
|
|
77
|
+
(_b = mapping.serviceId) !== null && _b !== void 0 ? _b : '',
|
|
78
|
+
(_c = mapping.coveredMethod) !== null && _c !== void 0 ? _c : '',
|
|
79
|
+
(_d = mapping.businessRule) !== null && _d !== void 0 ? _d : '',
|
|
80
|
+
mapping.evidenceType,
|
|
81
|
+
].join('|');
|
|
82
|
+
if (seen.has(key))
|
|
83
|
+
return;
|
|
84
|
+
seen.add(key);
|
|
85
|
+
mappings.push({
|
|
86
|
+
...mapping,
|
|
87
|
+
layer: insights.layer,
|
|
88
|
+
mockLibrary: insights.mockLibrary,
|
|
89
|
+
qualityScore: insights.qualityScore,
|
|
90
|
+
parameterizedExpansions: insights.parameterizedExpansions,
|
|
91
|
+
assertionCount: insights.assertionCount,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
66
94
|
if (graph.hasNode(fileNodeId)) {
|
|
67
95
|
const testEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'tests');
|
|
68
96
|
for (const edge of testEdges) {
|
|
69
|
-
|
|
97
|
+
const endpointNode = graph.getNode(edge.targetNodeId);
|
|
98
|
+
pushMapping({
|
|
70
99
|
testFilePath: testFile,
|
|
71
100
|
endpointId: edge.targetNodeId,
|
|
72
101
|
evidenceType: 'explicit-url',
|
|
73
102
|
confidence: 'high',
|
|
103
|
+
coversEndpoint: (_a = endpointNode === null || endpointNode === void 0 ? void 0 : endpointNode.label) !== null && _a !== void 0 ? _a : edge.targetNodeId,
|
|
104
|
+
businessRule: insights.businessRules[0],
|
|
105
|
+
serviceId: (_b = insights.coveredMethods[0]) === null || _b === void 0 ? void 0 : _b.serviceId,
|
|
106
|
+
coveredMethod: (_c = insights.coveredMethods[0]) === null || _c === void 0 ? void 0 : _c.qualifiedMethod,
|
|
74
107
|
});
|
|
75
108
|
}
|
|
76
109
|
}
|
|
77
|
-
// 2. Check for 'asserts' edges (higher confidence than just 'tests')
|
|
78
110
|
if (graph.hasNode(fileNodeId)) {
|
|
79
111
|
const assertEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'asserts');
|
|
80
112
|
for (const edge of assertEdges) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
const endpointNode = graph.getNode(edge.targetNodeId);
|
|
114
|
+
pushMapping({
|
|
115
|
+
testFilePath: testFile,
|
|
116
|
+
endpointId: edge.targetNodeId,
|
|
117
|
+
evidenceType: 'explicit-url',
|
|
118
|
+
confidence: 'high',
|
|
119
|
+
coversEndpoint: (_d = endpointNode === null || endpointNode === void 0 ? void 0 : endpointNode.label) !== null && _d !== void 0 ? _d : edge.targetNodeId,
|
|
120
|
+
businessRule: insights.businessRules[0],
|
|
121
|
+
serviceId: (_e = insights.coveredMethods[0]) === null || _e === void 0 ? void 0 : _e.serviceId,
|
|
122
|
+
coveredMethod: (_f = insights.coveredMethods[0]) === null || _f === void 0 ? void 0 : _f.qualifiedMethod,
|
|
123
|
+
});
|
|
90
124
|
}
|
|
91
125
|
}
|
|
92
|
-
// 3. Naming convention matching
|
|
93
126
|
const testBasename = path.basename(testFile, path.extname(testFile))
|
|
94
127
|
.replace(/\.(test|spec|Test|Tests|Spec|IT)$/, '')
|
|
95
128
|
.replace(/^test_|_test$/, '')
|
|
96
129
|
.replace(/^Test|Test$/, '');
|
|
97
130
|
for (const endpoint of endpointNodes) {
|
|
98
|
-
const handlerName = (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
const handlerName = String((_h = (_g = endpoint.metadata) === null || _g === void 0 ? void 0 : _g.handlerFunction) !== null && _h !== void 0 ? _h : '');
|
|
132
|
+
if (handlerName.length > 3 &&
|
|
133
|
+
testBasename.toLowerCase().includes(handlerName.toLowerCase())) {
|
|
134
|
+
pushMapping({
|
|
135
|
+
testFilePath: testFile,
|
|
136
|
+
endpointId: endpoint.id,
|
|
137
|
+
evidenceType: 'naming-convention',
|
|
138
|
+
confidence: 'low',
|
|
139
|
+
coversEndpoint: endpoint.label,
|
|
140
|
+
businessRule: insights.businessRules[0],
|
|
141
|
+
serviceId: (_j = insights.coveredMethods[0]) === null || _j === void 0 ? void 0 : _j.serviceId,
|
|
142
|
+
coveredMethod: (_k = insights.coveredMethods[0]) === null || _k === void 0 ? void 0 : _k.qualifiedMethod,
|
|
143
|
+
});
|
|
110
144
|
}
|
|
111
145
|
}
|
|
112
|
-
// 4. Import graph traversal: follow imports from test file to find endpoint connections
|
|
113
146
|
if (graph.hasNode(fileNodeId)) {
|
|
114
147
|
const importEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'imports-helper');
|
|
115
148
|
for (const importEdge of importEdges) {
|
|
116
|
-
|
|
117
|
-
const importedFileEdges = graph.getEdgesFrom(importEdge.targetNodeId);
|
|
118
|
-
for (const fileEdge of importedFileEdges) {
|
|
149
|
+
for (const fileEdge of graph.getEdgesFrom(importEdge.targetNodeId)) {
|
|
119
150
|
if (fileEdge.type === 'tests' || fileEdge.type === 'defines') {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
const endpointNode = graph.getNode(fileEdge.targetNodeId);
|
|
152
|
+
pushMapping({
|
|
153
|
+
testFilePath: testFile,
|
|
154
|
+
endpointId: fileEdge.targetNodeId,
|
|
155
|
+
evidenceType: 'import-graph',
|
|
156
|
+
confidence: 'medium',
|
|
157
|
+
coversEndpoint: (_l = endpointNode === null || endpointNode === void 0 ? void 0 : endpointNode.label) !== null && _l !== void 0 ? _l : fileEdge.targetNodeId,
|
|
158
|
+
businessRule: insights.businessRules[0],
|
|
159
|
+
serviceId: (_m = insights.coveredMethods[0]) === null || _m === void 0 ? void 0 : _m.serviceId,
|
|
160
|
+
coveredMethod: (_o = insights.coveredMethods[0]) === null || _o === void 0 ? void 0 : _o.qualifiedMethod,
|
|
161
|
+
});
|
|
128
162
|
}
|
|
129
163
|
}
|
|
130
164
|
}
|
|
131
165
|
}
|
|
166
|
+
for (const coveredMethod of insights.coveredMethods) {
|
|
167
|
+
const endpoints = resolveEndpointsForMethod(graph, coveredMethod);
|
|
168
|
+
if (endpoints.length > 0) {
|
|
169
|
+
for (const endpoint of endpoints) {
|
|
170
|
+
pushMapping({
|
|
171
|
+
testFilePath: testFile,
|
|
172
|
+
endpointId: endpoint.id,
|
|
173
|
+
evidenceType: 'service-call',
|
|
174
|
+
confidence: 'medium',
|
|
175
|
+
serviceId: coveredMethod.serviceId,
|
|
176
|
+
coveredMethod: coveredMethod.qualifiedMethod,
|
|
177
|
+
businessRule: insights.businessRules[0],
|
|
178
|
+
coversEndpoint: endpoint.label,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
pushMapping({
|
|
184
|
+
testFilePath: testFile,
|
|
185
|
+
evidenceType: 'service-call',
|
|
186
|
+
confidence: 'medium',
|
|
187
|
+
serviceId: coveredMethod.serviceId,
|
|
188
|
+
coveredMethod: coveredMethod.qualifiedMethod,
|
|
189
|
+
businessRule: insights.businessRules[0],
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const businessRule of insights.businessRules) {
|
|
194
|
+
pushMapping({
|
|
195
|
+
testFilePath: testFile,
|
|
196
|
+
evidenceType: 'business-rule',
|
|
197
|
+
confidence: insights.coveredMethods.length > 0 ? 'medium' : 'low',
|
|
198
|
+
serviceId: (_p = insights.coveredMethods[0]) === null || _p === void 0 ? void 0 : _p.serviceId,
|
|
199
|
+
coveredMethod: (_q = insights.coveredMethods[0]) === null || _q === void 0 ? void 0 : _q.qualifiedMethod,
|
|
200
|
+
businessRule,
|
|
201
|
+
coversEndpoint: (_r = mappings.find((m) => m.testFilePath === testFile)) === null || _r === void 0 ? void 0 : _r.coversEndpoint,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
132
204
|
}
|
|
133
205
|
return mappings;
|
|
134
206
|
}
|
|
207
|
+
function resolveTestContent(graph, testFile, fileContents) {
|
|
208
|
+
var _a;
|
|
209
|
+
if (typeof fileContents[testFile] === 'string') {
|
|
210
|
+
return fileContents[testFile];
|
|
211
|
+
}
|
|
212
|
+
const fileNode = graph.getNode(`file:${testFile}`);
|
|
213
|
+
if (typeof ((_a = fileNode === null || fileNode === void 0 ? void 0 : fileNode.metadata) === null || _a === void 0 ? void 0 : _a.content) === 'string') {
|
|
214
|
+
return fileNode.metadata.content;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
if (fs.existsSync(testFile)) {
|
|
218
|
+
return fs.readFileSync(testFile, 'utf-8');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return '';
|
|
223
|
+
}
|
|
224
|
+
return '';
|
|
225
|
+
}
|
|
226
|
+
function analyzeTestFile(graph, testFile, content) {
|
|
227
|
+
const language = detectLanguageFromPath(testFile);
|
|
228
|
+
const expandedTests = (0, parameterizedTestExpander_1.expandParameterizedTests)(testFile, content, language);
|
|
229
|
+
const injectedTargets = extractInjectedTargets(content);
|
|
230
|
+
const coveredMethods = extractCoveredMethods(content, injectedTargets);
|
|
231
|
+
const layer = (0, testLayerClassifier_1.classifyTestLayer)(testFile, content).layer;
|
|
232
|
+
return {
|
|
233
|
+
layer,
|
|
234
|
+
mockLibrary: detectMockLibrary(content),
|
|
235
|
+
qualityScore: scoreUnitTestFile(content),
|
|
236
|
+
parameterizedExpansions: expandedTests.reduce((sum, test) => typeof test.variantCount === 'number' ? sum + test.variantCount : sum, 0),
|
|
237
|
+
assertionCount: countAssertions(content, graph, testFile),
|
|
238
|
+
injectedTargets,
|
|
239
|
+
coveredMethods,
|
|
240
|
+
businessRules: extractBusinessRules(content),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function detectLanguageFromPath(filePath) {
|
|
244
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
245
|
+
if (ext === '.java')
|
|
246
|
+
return 'java';
|
|
247
|
+
if (ext === '.kt' || ext === '.kts')
|
|
248
|
+
return 'kotlin';
|
|
249
|
+
if (ext === '.py')
|
|
250
|
+
return 'python';
|
|
251
|
+
if (ext === '.ts' || ext === '.tsx')
|
|
252
|
+
return 'typescript';
|
|
253
|
+
return 'javascript';
|
|
254
|
+
}
|
|
255
|
+
function extractInjectedTargets(content) {
|
|
256
|
+
const targets = [];
|
|
257
|
+
const regex = /@InjectMocks[\s\S]{0,1000}?(?:private|protected|public|internal)?\s*([A-Z]\w*)\s+([a-zA-Z_]\w*)\s*(?:=|;)/g;
|
|
258
|
+
let match;
|
|
259
|
+
while ((match = regex.exec(content)) !== null) {
|
|
260
|
+
targets.push({ className: match[1], variableName: match[2] });
|
|
261
|
+
}
|
|
262
|
+
const kotlinRegex = /@InjectMocks[\s\S]{0,1000}?(?:private|protected|public|internal)?\s*(?:lateinit\s+)?var\s+([a-zA-Z_]\w*)\s*:\s*([A-Z]\w*)/g;
|
|
263
|
+
while ((match = kotlinRegex.exec(content)) !== null) {
|
|
264
|
+
targets.push({ className: match[2], variableName: match[1] });
|
|
265
|
+
}
|
|
266
|
+
return dedupeBy(targets, (target) => `${target.className}:${target.variableName}`);
|
|
267
|
+
}
|
|
268
|
+
function extractCoveredMethods(content, injectedTargets) {
|
|
269
|
+
const methods = [];
|
|
270
|
+
for (const target of injectedTargets) {
|
|
271
|
+
const callRegex = new RegExp(`${target.variableName}\\.([a-zA-Z_]\\w*)\\s*\\(`, 'g');
|
|
272
|
+
let match;
|
|
273
|
+
while ((match = callRegex.exec(content)) !== null) {
|
|
274
|
+
methods.push({
|
|
275
|
+
serviceId: target.className,
|
|
276
|
+
methodName: match[1],
|
|
277
|
+
qualifiedMethod: `${target.className}.${match[1]}`,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return dedupeBy(methods, (method) => method.qualifiedMethod);
|
|
282
|
+
}
|
|
283
|
+
function extractBusinessRules(content) {
|
|
284
|
+
const names = [];
|
|
285
|
+
const testNameRegexes = [
|
|
286
|
+
/@Test[\s\S]{0,120}?(?:void|fun)\s+(\w+)\s*\(/g,
|
|
287
|
+
/@ParameterizedTest[\s\S]{0,120}?(?:void|fun)\s+(\w+)\s*\(/g,
|
|
288
|
+
/@DisplayName\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
|
|
289
|
+
];
|
|
290
|
+
for (const regex of testNameRegexes) {
|
|
291
|
+
let match;
|
|
292
|
+
while ((match = regex.exec(content)) !== null) {
|
|
293
|
+
const normalized = normalizeBusinessRule(match[1]);
|
|
294
|
+
if (normalized) {
|
|
295
|
+
names.push(normalized);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return dedupeBy(names, (name) => name);
|
|
300
|
+
}
|
|
301
|
+
function normalizeBusinessRule(rawName) {
|
|
302
|
+
const words = rawName
|
|
303
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
304
|
+
.replace(/[_-]+/g, ' ')
|
|
305
|
+
.toLowerCase()
|
|
306
|
+
.split(/\s+/)
|
|
307
|
+
.filter(Boolean)
|
|
308
|
+
.filter((word) => ![
|
|
309
|
+
'should',
|
|
310
|
+
'test',
|
|
311
|
+
'successfully',
|
|
312
|
+
'when',
|
|
313
|
+
'then',
|
|
314
|
+
'given',
|
|
315
|
+
'throws',
|
|
316
|
+
'throw',
|
|
317
|
+
'exception',
|
|
318
|
+
'returns',
|
|
319
|
+
'return',
|
|
320
|
+
'with',
|
|
321
|
+
'method',
|
|
322
|
+
'case',
|
|
323
|
+
].includes(word));
|
|
324
|
+
if (words.length === 0)
|
|
325
|
+
return undefined;
|
|
326
|
+
return words.slice(0, 4).join(' ');
|
|
327
|
+
}
|
|
328
|
+
function detectMockLibrary(content) {
|
|
329
|
+
if (/@Mock\b|Mockito\.|@InjectMocks\b|@ExtendWith\(MockitoExtension/i.test(content)) {
|
|
330
|
+
return 'mockito';
|
|
331
|
+
}
|
|
332
|
+
if (/mockk|every\s*\{|verify\s*\{|\.should\(\)\.have\./i.test(content)) {
|
|
333
|
+
return 'mockk';
|
|
334
|
+
}
|
|
335
|
+
if (/jest\.mock|jest\.spyOn/.test(content)) {
|
|
336
|
+
return 'jest';
|
|
337
|
+
}
|
|
338
|
+
if (/mocker\.|unittest\.mock|pytest-mock/.test(content)) {
|
|
339
|
+
return 'pytest-mock';
|
|
340
|
+
}
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
function countAssertions(graphContent, graph, testFile) {
|
|
344
|
+
var _a, _b, _c;
|
|
345
|
+
const matches = graphContent.match(/assertThat\s*\(|assertEquals\s*\(|assertNotNull\s*\(|assertTrue\s*\(|assertFalse\s*\(|assertThrows\s*\(|expect\s*\(|Mockito\.verify\s*\(|verify\s*\(/g);
|
|
346
|
+
const contentCount = (_a = matches === null || matches === void 0 ? void 0 : matches.length) !== null && _a !== void 0 ? _a : 0;
|
|
347
|
+
const assertionNode = graph.getNode(`assertion:${testFile}`);
|
|
348
|
+
const graphCount = Number((_c = (_b = assertionNode === null || assertionNode === void 0 ? void 0 : assertionNode.metadata) === null || _b === void 0 ? void 0 : _b.assertionCount) !== null && _c !== void 0 ? _c : 0);
|
|
349
|
+
return Math.max(contentCount, graphCount);
|
|
350
|
+
}
|
|
351
|
+
function scoreUnitTestFile(content) {
|
|
352
|
+
let score = 0;
|
|
353
|
+
const hasSpecificAssertions = /assertThat\s*\(|assertEquals\s*\(|expect\([^)]*\)\.(?:toEqual|toBe|toContain|toStrictEqual)/.test(content);
|
|
354
|
+
const hasFluentAssertions = /assertThat\s*\([\s\S]*?\)\.is|\.should\(\)\.have\./i.test(content);
|
|
355
|
+
const hasWeakAssertions = /assertTrue\s*\(|assertNotNull\s*\(/.test(content);
|
|
356
|
+
const hasAnyAssertion = hasSpecificAssertions || hasFluentAssertions || hasWeakAssertions || /assertThrows\s*\(/.test(content);
|
|
357
|
+
if (hasSpecificAssertions)
|
|
358
|
+
score += 25;
|
|
359
|
+
else if (hasFluentAssertions)
|
|
360
|
+
score += 15;
|
|
361
|
+
else if (!hasAnyAssertion)
|
|
362
|
+
score -= 10;
|
|
363
|
+
const hasInjectMocks = /@InjectMocks\b/.test(content);
|
|
364
|
+
const hasMocks = /@Mock\b|mockk|jest\.mock|mocker\.|spyOn/.test(content);
|
|
365
|
+
const hasVerify = /Mockito\.verify\s*\(|verify\s*\(|\.should\(\)\.have\./.test(content);
|
|
366
|
+
const mocksClassUnderTest = /@Mock[\s\S]{0,80}?([A-Z]\w*Service|[A-Z]\w*Controller)\s+[a-zA-Z_]\w*/.test(content) && !hasInjectMocks;
|
|
367
|
+
const usesRealExternalCalls = /new\s+RestTemplate|WebClient\.builder|axios\.|fetch\s*\(|httpClient\./.test(content);
|
|
368
|
+
if (hasInjectMocks && hasMocks)
|
|
369
|
+
score += 25;
|
|
370
|
+
else if (hasVerify)
|
|
371
|
+
score += 15;
|
|
372
|
+
else if (mocksClassUnderTest)
|
|
373
|
+
score -= 10;
|
|
374
|
+
else if (!hasMocks && usesRealExternalCalls)
|
|
375
|
+
score -= 15;
|
|
376
|
+
const descriptiveName = /(?:void|fun)\s+should[A-Z]\w+When[A-Z]\w+\s*\(/.test(content)
|
|
377
|
+
|| /(?:void|fun)\s+should[A-Z]\w{10,}\s*\(/.test(content);
|
|
378
|
+
const hasDisplayName = /@DisplayName\(\s*["'`][^"'`]{10,}["'`]\s*\)/.test(content);
|
|
379
|
+
if (descriptiveName)
|
|
380
|
+
score += 20;
|
|
381
|
+
else if (hasDisplayName)
|
|
382
|
+
score += 10;
|
|
383
|
+
const coversEdgeCases = /\bnull\b|\bempty\b|\bboundary\b|\bmax\b|\bmin\b|\bzero\b|\bblank\b|\bnegative\b/i.test(content);
|
|
384
|
+
const coversErrorPath = /assertThrows|exception|invalid|notFound|failure|error/i.test(content);
|
|
385
|
+
if (coversEdgeCases)
|
|
386
|
+
score += 20;
|
|
387
|
+
else if (coversErrorPath)
|
|
388
|
+
score += 10;
|
|
389
|
+
if (!/@SpringBootTest\b/.test(content))
|
|
390
|
+
score += 10;
|
|
391
|
+
else if (/@MockBean\b/.test(content))
|
|
392
|
+
score += 5;
|
|
393
|
+
else
|
|
394
|
+
score -= 10;
|
|
395
|
+
return Math.max(0, Math.min(100, score));
|
|
396
|
+
}
|
|
397
|
+
function resolveEndpointsForMethod(graph, coveredMethod) {
|
|
398
|
+
const queue = findMethodNodeIds(graph, coveredMethod);
|
|
399
|
+
const visited = new Set();
|
|
400
|
+
const endpoints = new Map();
|
|
401
|
+
while (queue.length > 0) {
|
|
402
|
+
const currentId = queue.shift();
|
|
403
|
+
if (!currentId)
|
|
404
|
+
continue;
|
|
405
|
+
if (visited.has(currentId))
|
|
406
|
+
continue;
|
|
407
|
+
visited.add(currentId);
|
|
408
|
+
for (const edge of graph.getEdgesFrom(currentId)) {
|
|
409
|
+
if (edge.type === 'defines') {
|
|
410
|
+
const endpoint = graph.getNode(edge.targetNodeId);
|
|
411
|
+
if ((endpoint === null || endpoint === void 0 ? void 0 : endpoint.type) === 'endpoint') {
|
|
412
|
+
endpoints.set(endpoint.id, endpoint);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const incomingEdge of graph.getEdgesTo(currentId)) {
|
|
417
|
+
if (incomingEdge.type === 'calls' && !visited.has(incomingEdge.sourceNodeId)) {
|
|
418
|
+
queue.push(incomingEdge.sourceNodeId);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return Array.from(endpoints.values());
|
|
423
|
+
}
|
|
424
|
+
function findMethodNodeIds(graph, coveredMethod) {
|
|
425
|
+
const allNodes = graph.getAllNodes();
|
|
426
|
+
return allNodes
|
|
427
|
+
.filter((node) => node.type === 'function' || node.type === 'service' || node.type === 'class')
|
|
428
|
+
.filter((node) => {
|
|
429
|
+
const label = node.label.toLowerCase();
|
|
430
|
+
const id = node.id.toLowerCase();
|
|
431
|
+
return (label === coveredMethod.methodName.toLowerCase()
|
|
432
|
+
|| id.includes(coveredMethod.qualifiedMethod.toLowerCase())
|
|
433
|
+
|| id.includes(`${coveredMethod.serviceId.toLowerCase()}:${coveredMethod.methodName.toLowerCase()}`));
|
|
434
|
+
})
|
|
435
|
+
.map((node) => node.id);
|
|
436
|
+
}
|
|
437
|
+
function dedupeBy(items, keySelector) {
|
|
438
|
+
const seen = new Set();
|
|
439
|
+
const deduped = [];
|
|
440
|
+
for (const item of items) {
|
|
441
|
+
const key = keySelector(item);
|
|
442
|
+
if (seen.has(key))
|
|
443
|
+
continue;
|
|
444
|
+
seen.add(key);
|
|
445
|
+
deduped.push(item);
|
|
446
|
+
}
|
|
447
|
+
return deduped;
|
|
448
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testLayerClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/testLayerClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"testLayerClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/testLayerClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AA6HlD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,kBAAkB,CA0DpB"}
|
|
@@ -58,12 +58,16 @@ const CLASSIFICATION_RULES = [
|
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
layer: 'performance',
|
|
61
|
-
directoryPatterns: [/\bperformance\b/i, /\bperf\b/i, /\bload\b/i, /\bstress\b/i, /\bbenchmark\b/i],
|
|
62
|
-
fileNamePatterns: [/\.perf\./i, /\.bench\./i, /\.load\./i, /\.k6\./i],
|
|
61
|
+
directoryPatterns: [/\bperformance\b/i, /\bperformance-tests\b/i, /\bperf\b/i, /\bload\b/i, /\bstress\b/i, /\bbenchmark\b/i],
|
|
62
|
+
fileNamePatterns: [/\.perf\./i, /\.bench\./i, /\.load\./i, /\.k6\./i, /Simulation\.(java|kt)$/i],
|
|
63
63
|
contentPatterns: [
|
|
64
64
|
/import\s.*k6/, // k6
|
|
65
65
|
/from\s+['"]k6/,
|
|
66
66
|
/io\.gatling/i, // Gatling
|
|
67
|
+
/extends\s+Simulation\b/, // Gatling Java/Kotlin Simulation
|
|
68
|
+
/ScenarioBuilder|setUp\s*\(/, // Gatling scenario setup
|
|
69
|
+
/http\s*\([^)]*\)\s*\.\s*(?:get|post|put|delete)\s*\(/, // Gatling HTTP requests
|
|
70
|
+
/rampUsers\s*\(|constantUsersPerSec\s*\(|atOnceUsers\s*\(/, // Gatling load profiles
|
|
67
71
|
/locust|HttpUser|TaskSet/i, // Locust
|
|
68
72
|
/import\s.*artillery/i, // Artillery
|
|
69
73
|
/autocannon/i, // Autocannon
|
|
@@ -99,14 +103,18 @@ const CLASSIFICATION_RULES = [
|
|
|
99
103
|
},
|
|
100
104
|
{
|
|
101
105
|
layer: 'integration',
|
|
102
|
-
directoryPatterns: [/\bintegration\b/i, /\bint[-_]?test\b/i],
|
|
103
|
-
fileNamePatterns: [/\.integration\./i, /\.int\./i, /IT\.java$/],
|
|
106
|
+
directoryPatterns: [/\bintegration\b/i, /\bintegration-tests\b/i, /\bcontract-tests\b/i, /\bint[-_]?test\b/i],
|
|
107
|
+
fileNamePatterns: [/\.integration\./i, /\.int\./i, /IT\.(java|kt)$/i, /IntegrationTest\.(java|kt)$/i],
|
|
104
108
|
contentPatterns: [
|
|
105
109
|
/@SpringBootTest/, // Spring Boot integration test
|
|
110
|
+
/@AutoConfigureMockMvc/, // Spring MockMvc integration
|
|
106
111
|
/TestRestTemplate/, // Spring REST testing
|
|
112
|
+
/@RestClientTest/, // Spring REST client slice
|
|
107
113
|
/@Testcontainers/i, // Testcontainers
|
|
114
|
+
/@Container\b/, // Testcontainers field
|
|
108
115
|
/@DataJpaTest/i, // Spring Data JPA
|
|
109
116
|
/@WebMvcTest/i, // Spring MVC test
|
|
117
|
+
/extends\s+AbstractIntegrationTest\b/, // Convention-based integration base class
|
|
110
118
|
/testcontainers/i, // Docker containers
|
|
111
119
|
/@pytest\.fixture.*scope\s*=\s*['"]session['"]/, // pytest session-scoped fixture (DB lifecycle)
|
|
112
120
|
/factory\.(?:Factory|DjangoModelFactory|SQLAlchemyModelFactory)/, // Factory Boy (real-model tests)
|
|
@@ -131,7 +139,14 @@ const CLASSIFICATION_RULES = [
|
|
|
131
139
|
directoryPatterns: [/\bunit\b/i],
|
|
132
140
|
fileNamePatterns: [/\.unit\./i],
|
|
133
141
|
contentPatterns: [
|
|
134
|
-
|
|
142
|
+
/@ExtendWith\(MockitoExtension/i,
|
|
143
|
+
/@RunWith\(MockitoJUnitRunner/i,
|
|
144
|
+
/@InjectMocks\b/,
|
|
145
|
+
/Mockito\.verify\s*\(/,
|
|
146
|
+
/assertThat\([\s\S]*?\)\.is/i,
|
|
147
|
+
/assertEquals|assertNotNull|assertTrue/,
|
|
148
|
+
/given\([\s\S]*?\)\.willReturn/i,
|
|
149
|
+
/\.should\(\)\.have\./,
|
|
135
150
|
],
|
|
136
151
|
priority: 10,
|
|
137
152
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tiaStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/tiaStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAWzC,qBAAa,QAAS,YAAW,aAAa,CAAC,SAAS,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"tiaStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/tiaStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAWzC,qBAAa,QAAS,YAAW,aAAa,CAAC,SAAS,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;CAqJ5D"}
|
|
@@ -81,6 +81,7 @@ class TiaStage {
|
|
|
81
81
|
parameterizedTests: [],
|
|
82
82
|
testEndpointMappings: [],
|
|
83
83
|
};
|
|
84
|
+
const testFileContents = {};
|
|
84
85
|
// 1. Classify test layers
|
|
85
86
|
for (const testFile of testFiles) {
|
|
86
87
|
let content;
|
|
@@ -92,6 +93,7 @@ class TiaStage {
|
|
|
92
93
|
continue;
|
|
93
94
|
}
|
|
94
95
|
filesScanned.push(testFile);
|
|
96
|
+
testFileContents[testFile] = content;
|
|
95
97
|
const classification = (0, testLayerClassifier_1.classifyTestLayer)(testFile, content);
|
|
96
98
|
output.classifications.push(classification);
|
|
97
99
|
// 2. Detect mock boundaries
|
|
@@ -103,7 +105,7 @@ class TiaStage {
|
|
|
103
105
|
output.parameterizedTests.push(...paramTests);
|
|
104
106
|
}
|
|
105
107
|
// 4. Map tests to endpoints
|
|
106
|
-
output.testEndpointMappings = (0, testEndpointMapper_1.mapTestsToEndpoints)(context.graph, testFiles);
|
|
108
|
+
output.testEndpointMappings = (0, testEndpointMapper_1.mapTestsToEndpoints)(context.graph, testFiles, testFileContents);
|
|
107
109
|
// 5. Populate graph with TIA-discovered nodes
|
|
108
110
|
// Add mock-boundary nodes
|
|
109
111
|
for (const boundary of output.mockBoundaries) {
|
|
@@ -36,9 +36,18 @@ export interface ExpandedParameterizedTest {
|
|
|
36
36
|
*/
|
|
37
37
|
export interface TestEndpointMapping {
|
|
38
38
|
testFilePath: string;
|
|
39
|
-
endpointId
|
|
40
|
-
evidenceType: 'explicit-url' | 'resolved-constant' | 'import-graph' | 'naming-convention' | 'framework-metadata' | 'helper-traversal' | 'page-object';
|
|
39
|
+
endpointId?: string;
|
|
40
|
+
evidenceType: 'explicit-url' | 'resolved-constant' | 'import-graph' | 'naming-convention' | 'framework-metadata' | 'helper-traversal' | 'page-object' | 'service-call' | 'business-rule';
|
|
41
41
|
confidence: 'high' | 'medium' | 'low';
|
|
42
|
+
serviceId?: string;
|
|
43
|
+
businessRule?: string;
|
|
44
|
+
coveredMethod?: string;
|
|
45
|
+
layer?: string;
|
|
46
|
+
mockLibrary?: string;
|
|
47
|
+
qualityScore?: number;
|
|
48
|
+
parameterizedExpansions?: number;
|
|
49
|
+
assertionCount?: number;
|
|
50
|
+
coversEndpoint?: string;
|
|
42
51
|
}
|
|
43
52
|
/**
|
|
44
53
|
* Output of the TIA pipeline stage.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAA4B,MAAM,aAAa,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,cAAc,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAA4B,MAAM,aAAa,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,cAAc,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EACR,cAAc,GACd,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,oBAAoB,GACpB,kBAAkB,GAClB,aAAa,GACb,cAAc,GACd,eAAe,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,cAAc,EAAE,oBAAoB,EAAE,CAAC;IACvC,kBAAkB,EAAE,yBAAyB,EAAE,CAAC;IAChD,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;CAC7C"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DEFAULT_DISCOVERY_EXCLUDE_DIRS: readonly ["node_modules", ".git", "dist", "build", "target", ".cache", "coverage", ".nyc_output", "__pycache__", ".pytest_cache", ".tox", "vendor", ".bundle", ".gradle"];
|
|
2
|
+
export declare const DEFAULT_GENERATED_SOURCE_DIRS: readonly ["build/generated-sources", "target/generated-sources", "src/generated", "build/generated", "target/generated"];
|
|
3
|
+
export declare const DEFAULT_OPENAPI_INTERFACE_SUFFIXES: readonly ["Api", "ApiDelegate", "Controller", "Resource", "Endpoint"];
|
|
4
|
+
export declare const DEFAULT_OPENAPI_EXCLUDE_SUFFIXES: readonly ["Mapper", "Repository", "Dao", "Client", "Feign"];
|
|
5
|
+
export declare const DEFAULT_GENERATED_SOURCE_PATH_SEGMENTS: string[];
|
|
6
|
+
//# sourceMappingURL=projectDefaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectDefaults.d.ts","sourceRoot":"","sources":["../../src/projectDefaults.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,2KAejC,CAAC;AAEX,eAAO,MAAM,6BAA6B,0HAMhC,CAAC;AAEX,eAAO,MAAM,kCAAkC,uEAMrC,CAAC;AAEX,eAAO,MAAM,gCAAgC,6DAMnC,CAAC;AAEX,eAAO,MAAM,sCAAsC,UAMlD,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_GENERATED_SOURCE_PATH_SEGMENTS = exports.DEFAULT_OPENAPI_EXCLUDE_SUFFIXES = exports.DEFAULT_OPENAPI_INTERFACE_SUFFIXES = exports.DEFAULT_GENERATED_SOURCE_DIRS = exports.DEFAULT_DISCOVERY_EXCLUDE_DIRS = void 0;
|
|
4
|
+
exports.DEFAULT_DISCOVERY_EXCLUDE_DIRS = [
|
|
5
|
+
'node_modules',
|
|
6
|
+
'.git',
|
|
7
|
+
'dist',
|
|
8
|
+
'build',
|
|
9
|
+
'target',
|
|
10
|
+
'.cache',
|
|
11
|
+
'coverage',
|
|
12
|
+
'.nyc_output',
|
|
13
|
+
'__pycache__',
|
|
14
|
+
'.pytest_cache',
|
|
15
|
+
'.tox',
|
|
16
|
+
'vendor',
|
|
17
|
+
'.bundle',
|
|
18
|
+
'.gradle',
|
|
19
|
+
];
|
|
20
|
+
exports.DEFAULT_GENERATED_SOURCE_DIRS = [
|
|
21
|
+
'build/generated-sources',
|
|
22
|
+
'target/generated-sources',
|
|
23
|
+
'src/generated',
|
|
24
|
+
'build/generated',
|
|
25
|
+
'target/generated',
|
|
26
|
+
];
|
|
27
|
+
exports.DEFAULT_OPENAPI_INTERFACE_SUFFIXES = [
|
|
28
|
+
'Api',
|
|
29
|
+
'ApiDelegate',
|
|
30
|
+
'Controller',
|
|
31
|
+
'Resource',
|
|
32
|
+
'Endpoint',
|
|
33
|
+
];
|
|
34
|
+
exports.DEFAULT_OPENAPI_EXCLUDE_SUFFIXES = [
|
|
35
|
+
'Mapper',
|
|
36
|
+
'Repository',
|
|
37
|
+
'Dao',
|
|
38
|
+
'Client',
|
|
39
|
+
'Feign',
|
|
40
|
+
];
|
|
41
|
+
exports.DEFAULT_GENERATED_SOURCE_PATH_SEGMENTS = Array.from(new Set(exports.DEFAULT_GENERATED_SOURCE_DIRS
|
|
42
|
+
.flatMap((dir) => dir.split('/'))
|
|
43
|
+
.filter((segment) => segment.includes('generated'))));
|