api-tests-coverage 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/pipeline/confidence.d.ts +70 -0
- package/dist/src/pipeline/confidence.d.ts.map +1 -0
- package/dist/src/pipeline/confidence.js +198 -0
- package/dist/src/pipeline/graph.d.ts +58 -0
- package/dist/src/pipeline/graph.d.ts.map +1 -0
- package/dist/src/pipeline/graph.js +199 -0
- package/dist/src/pipeline/index.d.ts +24 -0
- package/dist/src/pipeline/index.d.ts.map +1 -0
- package/dist/src/pipeline/index.js +41 -0
- package/dist/src/pipeline/orchestrator.d.ts +42 -0
- package/dist/src/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/src/pipeline/orchestrator.js +115 -0
- package/dist/src/pipeline/stageInterface.d.ts +45 -0
- package/dist/src/pipeline/stageInterface.d.ts.map +1 -0
- package/dist/src/pipeline/stageInterface.js +17 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts +38 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.js +203 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts +19 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/astStage.js +238 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts +23 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +183 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.js +268 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts +22 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/importResolver.js +186 -0
- package/dist/src/pipeline/stages/ast/types.d.ts +85 -0
- package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/types.js +5 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts +25 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.js +90 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/dastStage.js +203 -0
- package/dist/src/pipeline/stages/dast/types.d.ts +49 -0
- package/dist/src/pipeline/stages/dast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/types.js +9 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/iastStage.js +191 -0
- package/dist/src/pipeline/stages/iast/types.d.ts +48 -0
- package/dist/src/pipeline/stages/iast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/types.js +8 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +17 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.js +60 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +141 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts +39 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeRules.js +90 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts +20 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeStage.js +145 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts +11 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.js +46 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts +15 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/ciDetector.js +87 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts +31 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.js +296 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts +25 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.js +416 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts +21 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/scaStage.js +208 -0
- package/dist/src/pipeline/stages/sca/types.d.ts +61 -0
- package/dist/src/pipeline/stages/sca/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/types.js +9 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts +19 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +118 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +238 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +22 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.js +134 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts +16 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +191 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/tiaStage.js +215 -0
- package/dist/src/pipeline/stages/tia/types.d.ts +52 -0
- package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/types.js +5 -0
- package/dist/src/pipeline/types.d.ts +128 -0
- package/dist/src/pipeline/types.d.ts.map +1 -0
- package/dist/src/pipeline/types.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCA (Software Composition Analysis) stage output types.
|
|
3
|
+
*
|
|
4
|
+
* The SCA stage is the first stage in the pipeline. It formalizes the existing
|
|
5
|
+
* discovery engine output and adds dependency classification, version extraction,
|
|
6
|
+
* and CI platform detection.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Category for a classified dependency.
|
|
10
|
+
*/
|
|
11
|
+
export type DependencyCategory = 'httpClient' | 'testFramework' | 'assertionLibrary' | 'mockingLibrary' | 'securityLibrary' | 'performanceTool' | 'e2eFramework' | 'framework' | 'database' | 'logging' | 'utility' | 'unknown';
|
|
12
|
+
/**
|
|
13
|
+
* CI platform detected from project structure.
|
|
14
|
+
*/
|
|
15
|
+
export type CiPlatform = 'github-actions' | 'gitlab-ci' | 'jenkins' | 'azure-devops' | 'circleci' | 'travis-ci' | 'none';
|
|
16
|
+
/**
|
|
17
|
+
* Output produced by the SCA stage.
|
|
18
|
+
* Downstream stages (especially AST and TIA) reference this to select
|
|
19
|
+
* correct analysis heuristics.
|
|
20
|
+
*/
|
|
21
|
+
export interface ScaOutput {
|
|
22
|
+
/** Detected programming languages (e.g. ["java", "typescript"]) */
|
|
23
|
+
languages: string[];
|
|
24
|
+
/** Detected frameworks (e.g. ["spring-boot", "express", "nestjs"]) */
|
|
25
|
+
frameworks: string[];
|
|
26
|
+
/** Detected HTTP client libraries (e.g. ["axios", "retrofit", "okhttp"]) */
|
|
27
|
+
httpClients: string[];
|
|
28
|
+
/** Detected test frameworks (e.g. ["jest", "junit5", "pytest"]) */
|
|
29
|
+
testFrameworks: string[];
|
|
30
|
+
/** Detected assertion libraries (e.g. ["assertj", "chai", "hamcrest"]) */
|
|
31
|
+
assertionLibraries: string[];
|
|
32
|
+
/** Detected security libraries (e.g. ["spring-security", "passport"]) */
|
|
33
|
+
securityLibraries: string[];
|
|
34
|
+
/** Detected performance testing tools (e.g. ["k6", "gatling", "locust"]) */
|
|
35
|
+
performanceTools: string[];
|
|
36
|
+
/** Detected mocking libraries (e.g. ["mockito", "mockk", "jest.mock", "sinon"]) */
|
|
37
|
+
mockingLibraries: string[];
|
|
38
|
+
/** Detected E2E testing frameworks (e.g. ["cypress", "playwright", "selenium"]) */
|
|
39
|
+
e2eFrameworks: string[];
|
|
40
|
+
/** All detected dependency names → version strings */
|
|
41
|
+
dependencyVersions: Record<string, string>;
|
|
42
|
+
/** Detected CI platform */
|
|
43
|
+
ciPlatform: CiPlatform;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A single parsed dependency entry from a manifest file.
|
|
47
|
+
*/
|
|
48
|
+
export interface ParsedDependency {
|
|
49
|
+
name: string;
|
|
50
|
+
version: string;
|
|
51
|
+
scope: 'production' | 'development' | 'test' | 'build' | 'unknown';
|
|
52
|
+
sourceFile: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Result of parsing all manifest files in a project.
|
|
56
|
+
*/
|
|
57
|
+
export interface DependencyParseResult {
|
|
58
|
+
dependencies: ParsedDependency[];
|
|
59
|
+
manifestFiles: string[];
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/sca/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,WAAW,GACX,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,cAAc,GACd,UAAU,GACV,WAAW,GACX,MAAM,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,mEAAmE;IACnE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,sEAAsE;IACtE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,0EAA0E;IAC1E,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,yEAAyE;IACzE,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mFAAmF;IACnF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mFAAmF;IACnF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sDAAsD;IACtD,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,2BAA2B;IAC3B,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACnE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SCA (Software Composition Analysis) stage output types.
|
|
4
|
+
*
|
|
5
|
+
* The SCA stage is the first stage in the pipeline. It formalizes the existing
|
|
6
|
+
* discovery engine output and adds dependency classification, version extraction,
|
|
7
|
+
* and CI platform detection.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock boundary detector — detects mock patterns across all languages
|
|
3
|
+
* and creates mock boundary records.
|
|
4
|
+
*
|
|
5
|
+
* Detects patterns from spec §17:
|
|
6
|
+
* - Java: @Mock, @Spy, @MockBean, @SpyBean, Mockito.mock(), mockk<>(), every{}
|
|
7
|
+
* - JS/TS: jest.mock(), jest.fn(), jest.spyOn(), sinon.stub(), sinon.mock(), nock()
|
|
8
|
+
* - Python: @patch(), @patch.object(), Mock(), MagicMock(), AsyncMock(), mocker.patch()
|
|
9
|
+
*/
|
|
10
|
+
import type { DetectedMockBoundary } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Detect mock boundaries in a test file.
|
|
13
|
+
*
|
|
14
|
+
* @param filePath - The test file path
|
|
15
|
+
* @param content - The file content
|
|
16
|
+
* @param language - The detected language
|
|
17
|
+
*/
|
|
18
|
+
export declare function detectMockBoundaries(filePath: string, content: string, language: string): DetectedMockBoundary[];
|
|
19
|
+
//# sourceMappingURL=mockBoundaryDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockBoundaryDetector.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/mockBoundaryDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAmEpD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,oBAAoB,EAAE,CA8CxB"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mock boundary detector — detects mock patterns across all languages
|
|
4
|
+
* and creates mock boundary records.
|
|
5
|
+
*
|
|
6
|
+
* Detects patterns from spec §17:
|
|
7
|
+
* - Java: @Mock, @Spy, @MockBean, @SpyBean, Mockito.mock(), mockk<>(), every{}
|
|
8
|
+
* - JS/TS: jest.mock(), jest.fn(), jest.spyOn(), sinon.stub(), sinon.mock(), nock()
|
|
9
|
+
* - Python: @patch(), @patch.object(), Mock(), MagicMock(), AsyncMock(), mocker.patch()
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.detectMockBoundaries = detectMockBoundaries;
|
|
13
|
+
const JAVA_KOTLIN_PATTERNS = [
|
|
14
|
+
{ regex: /@Mock\b/, library: 'mockito', mockType: 'return-value' },
|
|
15
|
+
{ regex: /@Spy\b/, library: 'mockito', mockType: 'spy' },
|
|
16
|
+
{ regex: /@MockBean\b/, library: 'spring-test', mockType: 'return-value' },
|
|
17
|
+
{ regex: /@SpyBean\b/, library: 'spring-test', mockType: 'spy' },
|
|
18
|
+
{ regex: /Mockito\.mock\(/, library: 'mockito', mockType: 'return-value' },
|
|
19
|
+
{ regex: /Mockito\.spy\(/, library: 'mockito', mockType: 'spy' },
|
|
20
|
+
{ regex: /when\(.*\)\.thenReturn\(/, library: 'mockito', mockType: 'return-value' },
|
|
21
|
+
{ regex: /when\(.*\)\.thenThrow\(/, library: 'mockito', mockType: 'exception' },
|
|
22
|
+
{ regex: /doReturn\(.*\)\.when\(/, library: 'mockito', mockType: 'return-value' },
|
|
23
|
+
{ regex: /doThrow\(.*\)\.when\(/, library: 'mockito', mockType: 'exception' },
|
|
24
|
+
{ regex: /mockk</, library: 'mockk', mockType: 'return-value' },
|
|
25
|
+
{ regex: /spyk\(/, library: 'mockk', mockType: 'spy' },
|
|
26
|
+
{ regex: /every\s*\{/, library: 'mockk', mockType: 'return-value' },
|
|
27
|
+
{ regex: /coEvery\s*\{/, library: 'mockk', mockType: 'return-value' },
|
|
28
|
+
{ regex: /verify\s*\{/, library: 'mockk', mockType: 'spy' },
|
|
29
|
+
];
|
|
30
|
+
const JS_TS_PATTERNS = [
|
|
31
|
+
{ regex: /jest\.mock\(/, library: 'jest', mockType: 'return-value' },
|
|
32
|
+
{ regex: /jest\.fn\(/, library: 'jest', mockType: 'return-value' },
|
|
33
|
+
{ regex: /jest\.spyOn\(/, library: 'jest', mockType: 'spy' },
|
|
34
|
+
{ regex: /\.mockReturnValue\(/, library: 'jest', mockType: 'return-value' },
|
|
35
|
+
{ regex: /\.mockResolvedValue\(/, library: 'jest', mockType: 'return-value' },
|
|
36
|
+
{ regex: /\.mockRejectedValue\(/, library: 'jest', mockType: 'exception' },
|
|
37
|
+
{ regex: /\.mockImplementation\(/, library: 'jest', mockType: 'return-value' },
|
|
38
|
+
{ regex: /sinon\.stub\(/, library: 'sinon', mockType: 'return-value' },
|
|
39
|
+
{ regex: /sinon\.mock\(/, library: 'sinon', mockType: 'return-value' },
|
|
40
|
+
{ regex: /sinon\.spy\(/, library: 'sinon', mockType: 'spy' },
|
|
41
|
+
{ regex: /\.returns\(/, library: 'sinon', mockType: 'return-value' },
|
|
42
|
+
{ regex: /\.throws\(/, library: 'sinon', mockType: 'exception' },
|
|
43
|
+
{ regex: /nock\(/, library: 'nock', mockType: 'return-value' },
|
|
44
|
+
{ regex: /\.useFakeTimers\(/, library: 'jest', mockType: 'timer' },
|
|
45
|
+
{ regex: /sinon\.useFakeTimers\(/, library: 'sinon', mockType: 'timer' },
|
|
46
|
+
];
|
|
47
|
+
const PYTHON_PATTERNS = [
|
|
48
|
+
{ regex: /@patch\(/, library: 'unittest.mock', mockType: 'return-value' },
|
|
49
|
+
{ regex: /@patch\.object\(/, library: 'unittest.mock', mockType: 'return-value' },
|
|
50
|
+
{ regex: /Mock\(\)/, library: 'unittest.mock', mockType: 'return-value' },
|
|
51
|
+
{ regex: /MagicMock\(\)/, library: 'unittest.mock', mockType: 'return-value' },
|
|
52
|
+
{ regex: /AsyncMock\(\)/, library: 'unittest.mock', mockType: 'return-value' },
|
|
53
|
+
{ regex: /mocker\.patch\(/, library: 'pytest-mock', mockType: 'return-value' },
|
|
54
|
+
{ regex: /mocker\.spy\(/, library: 'pytest-mock', mockType: 'spy' },
|
|
55
|
+
{ regex: /\.return_value\s*=/, library: 'unittest.mock', mockType: 'return-value' },
|
|
56
|
+
{ regex: /\.side_effect\s*=/, library: 'unittest.mock', mockType: 'exception' },
|
|
57
|
+
{ regex: /responses\.add\(/, library: 'responses', mockType: 'return-value' },
|
|
58
|
+
{ regex: /requests_mock\./, library: 'requests-mock', mockType: 'return-value' },
|
|
59
|
+
{ regex: /freezegun|freeze_time/, library: 'freezegun', mockType: 'timer' },
|
|
60
|
+
];
|
|
61
|
+
const RUBY_PATTERNS = [
|
|
62
|
+
{ regex: /allow\(.*\)\.to\s+receive\(/, library: 'rspec-mocks', mockType: 'return-value' },
|
|
63
|
+
{ regex: /expect\(.*\)\.to\s+receive\(/, library: 'rspec-mocks', mockType: 'spy' },
|
|
64
|
+
{ regex: /double\(/, library: 'rspec-mocks', mockType: 'return-value' },
|
|
65
|
+
{ regex: /instance_double\(/, library: 'rspec-mocks', mockType: 'return-value' },
|
|
66
|
+
{ regex: /stub_request\(/, library: 'webmock', mockType: 'return-value' },
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Detect mock boundaries in a test file.
|
|
70
|
+
*
|
|
71
|
+
* @param filePath - The test file path
|
|
72
|
+
* @param content - The file content
|
|
73
|
+
* @param language - The detected language
|
|
74
|
+
*/
|
|
75
|
+
function detectMockBoundaries(filePath, content, language) {
|
|
76
|
+
var _a;
|
|
77
|
+
const boundaries = [];
|
|
78
|
+
const lines = content.split('\n');
|
|
79
|
+
let patterns;
|
|
80
|
+
switch (language) {
|
|
81
|
+
case 'java':
|
|
82
|
+
case 'kotlin':
|
|
83
|
+
patterns = JAVA_KOTLIN_PATTERNS;
|
|
84
|
+
break;
|
|
85
|
+
case 'javascript':
|
|
86
|
+
case 'typescript':
|
|
87
|
+
patterns = JS_TS_PATTERNS;
|
|
88
|
+
break;
|
|
89
|
+
case 'python':
|
|
90
|
+
patterns = PYTHON_PATTERNS;
|
|
91
|
+
break;
|
|
92
|
+
case 'ruby':
|
|
93
|
+
patterns = RUBY_PATTERNS;
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
// Try all patterns
|
|
97
|
+
patterns = [...JS_TS_PATTERNS, ...JAVA_KOTLIN_PATTERNS, ...PYTHON_PATTERNS, ...RUBY_PATTERNS];
|
|
98
|
+
}
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
const line = lines[i];
|
|
101
|
+
for (const pattern of patterns) {
|
|
102
|
+
if (pattern.regex.test(line)) {
|
|
103
|
+
// Extract mocked target (heuristic: argument in parentheses)
|
|
104
|
+
const targetMatch = /[\(][\s]*['"`]?([a-zA-Z0-9_./@]+)['"`]?/.exec(line);
|
|
105
|
+
const mockedTarget = (_a = targetMatch === null || targetMatch === void 0 ? void 0 : targetMatch[1]) !== null && _a !== void 0 ? _a : 'unknown';
|
|
106
|
+
boundaries.push({
|
|
107
|
+
testFilePath: filePath,
|
|
108
|
+
mockingLibrary: pattern.library,
|
|
109
|
+
mockType: pattern.mockType,
|
|
110
|
+
mockedTarget,
|
|
111
|
+
line: i + 1,
|
|
112
|
+
});
|
|
113
|
+
break; // One detection per line
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return boundaries;
|
|
118
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameterized test expander — detects parameterized test patterns and
|
|
3
|
+
* calculates variant counts.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - JUnit: @ParameterizedTest + @ValueSource, @CsvSource, @MethodSource, @EnumSource
|
|
7
|
+
* - Jest: test.each(table), describe.each(table), it.each
|
|
8
|
+
* - pytest: @pytest.mark.parametrize()
|
|
9
|
+
* - Kotest: data-driven tests
|
|
10
|
+
*/
|
|
11
|
+
import type { ExpandedParameterizedTest } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* Detect and expand parameterized tests in a file.
|
|
14
|
+
*
|
|
15
|
+
* @param filePath - The test file path
|
|
16
|
+
* @param content - The file content
|
|
17
|
+
* @param language - The detected language
|
|
18
|
+
*/
|
|
19
|
+
export declare function expandParameterizedTests(filePath: string, content: string, language: string): ExpandedParameterizedTest[];
|
|
20
|
+
//# sourceMappingURL=parameterizedTestExpander.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parameterizedTestExpander.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/parameterizedTestExpander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,yBAAyB,EAAE,CAa7B"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parameterized test expander — detects parameterized test patterns and
|
|
4
|
+
* calculates variant counts.
|
|
5
|
+
*
|
|
6
|
+
* Supports:
|
|
7
|
+
* - JUnit: @ParameterizedTest + @ValueSource, @CsvSource, @MethodSource, @EnumSource
|
|
8
|
+
* - Jest: test.each(table), describe.each(table), it.each
|
|
9
|
+
* - pytest: @pytest.mark.parametrize()
|
|
10
|
+
* - Kotest: data-driven tests
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.expandParameterizedTests = expandParameterizedTests;
|
|
14
|
+
/**
|
|
15
|
+
* Detect and expand parameterized tests in a file.
|
|
16
|
+
*
|
|
17
|
+
* @param filePath - The test file path
|
|
18
|
+
* @param content - The file content
|
|
19
|
+
* @param language - The detected language
|
|
20
|
+
*/
|
|
21
|
+
function expandParameterizedTests(filePath, content, language) {
|
|
22
|
+
switch (language) {
|
|
23
|
+
case 'java':
|
|
24
|
+
case 'kotlin':
|
|
25
|
+
return expandJavaKotlinParameterized(filePath, content);
|
|
26
|
+
case 'javascript':
|
|
27
|
+
case 'typescript':
|
|
28
|
+
return expandJestParameterized(filePath, content);
|
|
29
|
+
case 'python':
|
|
30
|
+
return expandPytestParameterized(filePath, content);
|
|
31
|
+
default:
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// ─── JUnit Parameterized ────────────────────────────────────────────────────
|
|
36
|
+
function expandJavaKotlinParameterized(filePath, content) {
|
|
37
|
+
const results = [];
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
const line = lines[i].trim();
|
|
41
|
+
// @ParameterizedTest must precede the data source annotation
|
|
42
|
+
if (!line.includes('@ParameterizedTest'))
|
|
43
|
+
continue;
|
|
44
|
+
// Look ahead for data source annotations
|
|
45
|
+
let testName = 'unknown';
|
|
46
|
+
let variantCount = 'unresolvable';
|
|
47
|
+
let pattern = '@ParameterizedTest';
|
|
48
|
+
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
|
49
|
+
const nextLine = lines[j].trim();
|
|
50
|
+
// Extract test method name
|
|
51
|
+
const methodMatch = /(?:void|fun)\s+(\w+)\s*\(/.exec(nextLine);
|
|
52
|
+
if (methodMatch) {
|
|
53
|
+
testName = methodMatch[1];
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
// @ValueSource(strings = {"a", "b", "c"})
|
|
57
|
+
const valueSourceMatch = /@ValueSource\(\s*\w+\s*=\s*\{([^}]+)\}/.exec(nextLine);
|
|
58
|
+
if (valueSourceMatch) {
|
|
59
|
+
variantCount = valueSourceMatch[1].split(',').length;
|
|
60
|
+
pattern = '@ValueSource';
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// @CsvSource({"a,1", "b,2", "c,3"})
|
|
64
|
+
const csvSourceMatch = /@CsvSource\(\s*(?:value\s*=\s*)?\{([^}]+)\}/.exec(nextLine);
|
|
65
|
+
if (csvSourceMatch) {
|
|
66
|
+
variantCount = csvSourceMatch[1].split(/",\s*"/).length;
|
|
67
|
+
pattern = '@CsvSource';
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// @MethodSource("methodName")
|
|
71
|
+
const methodSourceMatch = /@MethodSource\(\s*"(\w+)"/.exec(nextLine);
|
|
72
|
+
if (methodSourceMatch) {
|
|
73
|
+
pattern = `@MethodSource(${methodSourceMatch[1]})`;
|
|
74
|
+
// Try to find the method and count Stream.of() arguments
|
|
75
|
+
const sourceMethodRegex = new RegExp(`(?:static\\s+)?(?:Stream|List|Collection|Set).*?\\s+${methodSourceMatch[1]}\\s*\\([^)]*\\)\\s*\\{([\\s\\S]*?)\\}`, 'm');
|
|
76
|
+
const sourceMethodMatch = sourceMethodRegex.exec(content);
|
|
77
|
+
if (sourceMethodMatch) {
|
|
78
|
+
const body = sourceMethodMatch[1];
|
|
79
|
+
const argsMatch = body.match(/(?:Arguments\.of|of)\s*\(/g);
|
|
80
|
+
if (argsMatch) {
|
|
81
|
+
variantCount = argsMatch.length;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// @EnumSource(MyEnum.class)
|
|
87
|
+
if (nextLine.includes('@EnumSource')) {
|
|
88
|
+
pattern = '@EnumSource';
|
|
89
|
+
variantCount = 'unresolvable'; // Would need to find enum declaration
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
results.push({
|
|
94
|
+
testFilePath: filePath,
|
|
95
|
+
testName,
|
|
96
|
+
variantCount,
|
|
97
|
+
pattern,
|
|
98
|
+
line: i + 1,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
// ─── Jest Parameterized ─────────────────────────────────────────────────────
|
|
104
|
+
function expandJestParameterized(filePath, content) {
|
|
105
|
+
var _a;
|
|
106
|
+
const results = [];
|
|
107
|
+
const lines = content.split('\n');
|
|
108
|
+
for (let i = 0; i < lines.length; i++) {
|
|
109
|
+
const line = lines[i];
|
|
110
|
+
// test.each([...])('name', ...)
|
|
111
|
+
// it.each([...])('name', ...)
|
|
112
|
+
// describe.each([...])('name', ...)
|
|
113
|
+
const eachMatch = /(?:test|it|describe)\.each\s*\(\s*\[/.exec(line);
|
|
114
|
+
if (eachMatch) {
|
|
115
|
+
// Try to extract the array and count items
|
|
116
|
+
const arrayContent = extractBalancedBrackets(content, content.indexOf(eachMatch[0], i > 0 ? content.indexOf(lines[i]) : 0));
|
|
117
|
+
const variantCount = arrayContent ? countArrayElements(arrayContent) : 'unresolvable';
|
|
118
|
+
// Extract test name from the next argument
|
|
119
|
+
const nameMatch = /\)\s*\(\s*['"`]([^'"`]+)['"`]/.exec(content.substring(content.indexOf(eachMatch[0])));
|
|
120
|
+
const testName = (_a = nameMatch === null || nameMatch === void 0 ? void 0 : nameMatch[1]) !== null && _a !== void 0 ? _a : 'parameterized test';
|
|
121
|
+
results.push({
|
|
122
|
+
testFilePath: filePath,
|
|
123
|
+
testName,
|
|
124
|
+
variantCount,
|
|
125
|
+
pattern: 'test.each',
|
|
126
|
+
line: i + 1,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// test.each`template`('name', ...) — tagged template form
|
|
130
|
+
const templateMatch = /(?:test|it|describe)\.each\s*`/.exec(line);
|
|
131
|
+
if (templateMatch && !eachMatch) {
|
|
132
|
+
// Count rows in tagged template (lines between backticks, minus header)
|
|
133
|
+
let rowCount = 0;
|
|
134
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
135
|
+
if (lines[j].includes('`'))
|
|
136
|
+
break;
|
|
137
|
+
if (lines[j].trim())
|
|
138
|
+
rowCount++;
|
|
139
|
+
}
|
|
140
|
+
const variantCount = rowCount > 0 ? rowCount : 'unresolvable';
|
|
141
|
+
results.push({
|
|
142
|
+
testFilePath: filePath,
|
|
143
|
+
testName: 'parameterized test',
|
|
144
|
+
variantCount,
|
|
145
|
+
pattern: 'test.each (template)',
|
|
146
|
+
line: i + 1,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
// ─── Pytest Parameterized ───────────────────────────────────────────────────
|
|
153
|
+
function expandPytestParameterized(filePath, content) {
|
|
154
|
+
const results = [];
|
|
155
|
+
const lines = content.split('\n');
|
|
156
|
+
for (let i = 0; i < lines.length; i++) {
|
|
157
|
+
const line = lines[i].trim();
|
|
158
|
+
// @pytest.mark.parametrize("name", [...])
|
|
159
|
+
const paramMatch = /@pytest\.mark\.parametrize\s*\(/.exec(line);
|
|
160
|
+
if (paramMatch) {
|
|
161
|
+
// Try to extract array content and count elements
|
|
162
|
+
let variantCount = 'unresolvable';
|
|
163
|
+
// Look for the bracket content
|
|
164
|
+
const fullContent = lines.slice(i, Math.min(i + 20, lines.length)).join('\n');
|
|
165
|
+
const bracketContent = extractBalancedBrackets(fullContent, fullContent.indexOf('['));
|
|
166
|
+
if (bracketContent) {
|
|
167
|
+
variantCount = countPytestParams(bracketContent);
|
|
168
|
+
}
|
|
169
|
+
// Extract test name from following def line
|
|
170
|
+
let testName = 'unknown';
|
|
171
|
+
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
|
172
|
+
const defMatch = /def\s+(\w+)\s*\(/.exec(lines[j]);
|
|
173
|
+
if (defMatch) {
|
|
174
|
+
testName = defMatch[1];
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
results.push({
|
|
179
|
+
testFilePath: filePath,
|
|
180
|
+
testName,
|
|
181
|
+
variantCount,
|
|
182
|
+
pattern: '@pytest.mark.parametrize',
|
|
183
|
+
line: i + 1,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
}
|
|
189
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
190
|
+
function extractBalancedBrackets(content, startIndex) {
|
|
191
|
+
if (startIndex < 0 || startIndex >= content.length || content[startIndex] !== '[') {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
let depth = 0;
|
|
195
|
+
let i = startIndex;
|
|
196
|
+
while (i < content.length) {
|
|
197
|
+
if (content[i] === '[')
|
|
198
|
+
depth++;
|
|
199
|
+
else if (content[i] === ']') {
|
|
200
|
+
depth--;
|
|
201
|
+
if (depth === 0) {
|
|
202
|
+
return content.substring(startIndex + 1, i);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
i++;
|
|
206
|
+
}
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
function countArrayElements(arrayContent) {
|
|
210
|
+
// Count top-level elements (not nested)
|
|
211
|
+
let depth = 0;
|
|
212
|
+
let count = 1;
|
|
213
|
+
for (const char of arrayContent) {
|
|
214
|
+
if (char === '[' || char === '(' || char === '{')
|
|
215
|
+
depth++;
|
|
216
|
+
else if (char === ']' || char === ')' || char === '}')
|
|
217
|
+
depth--;
|
|
218
|
+
else if (char === ',' && depth === 0)
|
|
219
|
+
count++;
|
|
220
|
+
}
|
|
221
|
+
// If only whitespace, return 0
|
|
222
|
+
if (arrayContent.trim().length === 0)
|
|
223
|
+
return 0;
|
|
224
|
+
return count;
|
|
225
|
+
}
|
|
226
|
+
function countPytestParams(bracketContent) {
|
|
227
|
+
// Each top-level tuple/value is a test case
|
|
228
|
+
const trimmed = bracketContent.trim();
|
|
229
|
+
if (!trimmed)
|
|
230
|
+
return 0;
|
|
231
|
+
// Count top-level commas for simple values
|
|
232
|
+
// For tuples, count each (...) group
|
|
233
|
+
const tupleMatches = trimmed.match(/\([^)]*\)/g);
|
|
234
|
+
if (tupleMatches)
|
|
235
|
+
return tupleMatches.length;
|
|
236
|
+
// Otherwise count comma-separated values
|
|
237
|
+
return countArrayElements(trimmed);
|
|
238
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-to-endpoint mapper — maps test files to endpoints using multiple evidence sources.
|
|
3
|
+
*
|
|
4
|
+
* Evidence types:
|
|
5
|
+
* 1. Explicit URL: Direct URL string in test file
|
|
6
|
+
* 2. Resolved constant: URL from resolved constant/variable
|
|
7
|
+
* 3. Import graph: Linked via import chain
|
|
8
|
+
* 4. Naming convention: Test file name matches endpoint handler
|
|
9
|
+
* 5. Framework metadata: Framework annotations link test to endpoint
|
|
10
|
+
* 6. Helper traversal: URL resolved through helper function chain
|
|
11
|
+
* 7. Page object: URL resolved through page object pattern
|
|
12
|
+
*/
|
|
13
|
+
import type { TestEndpointMapping } from './types';
|
|
14
|
+
import type { CoverageKnowledgeGraph } from '../../graph';
|
|
15
|
+
/**
|
|
16
|
+
* Map test files to endpoints using available evidence in the graph.
|
|
17
|
+
*
|
|
18
|
+
* @param graph - The coverage knowledge graph (from AST stage)
|
|
19
|
+
* @param testFiles - List of test file paths
|
|
20
|
+
*/
|
|
21
|
+
export declare function mapTestsToEndpoints(graph: CoverageKnowledgeGraph, testFiles: string[]): TestEndpointMapping[];
|
|
22
|
+
//# sourceMappingURL=testEndpointMapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testEndpointMapper.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/tia/testEndpointMapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG1D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,sBAAsB,EAC7B,SAAS,EAAE,MAAM,EAAE,GAClB,mBAAmB,EAAE,CAqFvB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test-to-endpoint mapper — maps test files to endpoints using multiple evidence sources.
|
|
4
|
+
*
|
|
5
|
+
* Evidence types:
|
|
6
|
+
* 1. Explicit URL: Direct URL string in test file
|
|
7
|
+
* 2. Resolved constant: URL from resolved constant/variable
|
|
8
|
+
* 3. Import graph: Linked via import chain
|
|
9
|
+
* 4. Naming convention: Test file name matches endpoint handler
|
|
10
|
+
* 5. Framework metadata: Framework annotations link test to endpoint
|
|
11
|
+
* 6. Helper traversal: URL resolved through helper function chain
|
|
12
|
+
* 7. Page object: URL resolved through page object pattern
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.mapTestsToEndpoints = mapTestsToEndpoints;
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
/**
|
|
51
|
+
* Map test files to endpoints using available evidence in the graph.
|
|
52
|
+
*
|
|
53
|
+
* @param graph - The coverage knowledge graph (from AST stage)
|
|
54
|
+
* @param testFiles - List of test file paths
|
|
55
|
+
*/
|
|
56
|
+
function mapTestsToEndpoints(graph, testFiles) {
|
|
57
|
+
var _a, _b;
|
|
58
|
+
const mappings = [];
|
|
59
|
+
// Get all endpoint nodes
|
|
60
|
+
const endpointNodes = graph.getNodesByType('endpoint');
|
|
61
|
+
if (endpointNodes.length === 0)
|
|
62
|
+
return mappings;
|
|
63
|
+
for (const testFile of testFiles) {
|
|
64
|
+
const fileNodeId = `file:${testFile}`;
|
|
65
|
+
// 1. Check for existing 'tests' edges from AST stage
|
|
66
|
+
if (graph.hasNode(fileNodeId)) {
|
|
67
|
+
const testEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'tests');
|
|
68
|
+
for (const edge of testEdges) {
|
|
69
|
+
mappings.push({
|
|
70
|
+
testFilePath: testFile,
|
|
71
|
+
endpointId: edge.targetNodeId,
|
|
72
|
+
evidenceType: 'explicit-url',
|
|
73
|
+
confidence: 'high',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 2. Check for 'asserts' edges (higher confidence than just 'tests')
|
|
78
|
+
if (graph.hasNode(fileNodeId)) {
|
|
79
|
+
const assertEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'asserts');
|
|
80
|
+
for (const edge of assertEdges) {
|
|
81
|
+
// Don't duplicate if already mapped via 'tests'
|
|
82
|
+
if (!mappings.some((m) => m.testFilePath === testFile && m.endpointId === edge.targetNodeId)) {
|
|
83
|
+
mappings.push({
|
|
84
|
+
testFilePath: testFile,
|
|
85
|
+
endpointId: edge.targetNodeId,
|
|
86
|
+
evidenceType: 'explicit-url',
|
|
87
|
+
confidence: 'high',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 3. Naming convention matching
|
|
93
|
+
const testBasename = path.basename(testFile, path.extname(testFile))
|
|
94
|
+
.replace(/\.(test|spec|Test|Tests|Spec|IT)$/, '')
|
|
95
|
+
.replace(/^test_|_test$/, '')
|
|
96
|
+
.replace(/^Test|Test$/, '');
|
|
97
|
+
for (const endpoint of endpointNodes) {
|
|
98
|
+
const handlerName = (_b = (_a = endpoint.metadata) === null || _a === void 0 ? void 0 : _a.handlerFunction) !== null && _b !== void 0 ? _b : '';
|
|
99
|
+
const endpointLabel = endpoint.label.toLowerCase();
|
|
100
|
+
// Match test file name to handler/controller name
|
|
101
|
+
if (testBasename.toLowerCase().includes(handlerName.toLowerCase()) && handlerName.length > 3) {
|
|
102
|
+
if (!mappings.some((m) => m.testFilePath === testFile && m.endpointId === endpoint.id)) {
|
|
103
|
+
mappings.push({
|
|
104
|
+
testFilePath: testFile,
|
|
105
|
+
endpointId: endpoint.id,
|
|
106
|
+
evidenceType: 'naming-convention',
|
|
107
|
+
confidence: 'low',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// 4. Import graph traversal: follow imports from test file to find endpoint connections
|
|
113
|
+
if (graph.hasNode(fileNodeId)) {
|
|
114
|
+
const importEdges = graph.getEdgesFrom(fileNodeId).filter((e) => e.type === 'imports-helper');
|
|
115
|
+
for (const importEdge of importEdges) {
|
|
116
|
+
// Check if the imported file defines any endpoints
|
|
117
|
+
const importedFileEdges = graph.getEdgesFrom(importEdge.targetNodeId);
|
|
118
|
+
for (const fileEdge of importedFileEdges) {
|
|
119
|
+
if (fileEdge.type === 'tests' || fileEdge.type === 'defines') {
|
|
120
|
+
if (!mappings.some((m) => m.testFilePath === testFile && m.endpointId === fileEdge.targetNodeId)) {
|
|
121
|
+
mappings.push({
|
|
122
|
+
testFilePath: testFile,
|
|
123
|
+
endpointId: fileEdge.targetNodeId,
|
|
124
|
+
evidenceType: 'import-graph',
|
|
125
|
+
confidence: 'medium',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return mappings;
|
|
134
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test layer classifier — classifies test files into 7 layers:
|
|
3
|
+
* unit, component, integration, api, e2e, performance, security.
|
|
4
|
+
*
|
|
5
|
+
* Uses multiple signals: directory patterns, naming patterns,
|
|
6
|
+
* framework annotations, and import analysis.
|
|
7
|
+
*/
|
|
8
|
+
import type { TestClassification } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Classify a test file into a layer.
|
|
11
|
+
*
|
|
12
|
+
* @param filePath - The test file path
|
|
13
|
+
* @param content - Optional file content for content-based classification
|
|
14
|
+
*/
|
|
15
|
+
export declare function classifyTestLayer(filePath: string, content?: string): TestClassification;
|
|
16
|
+
//# sourceMappingURL=testLayerClassifier.d.ts.map
|
|
@@ -0,0 +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;AAyGlD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,kBAAkB,CA0DpB"}
|