@skyramp/mcp 0.3.2-rc.pom-3 → 0.3.2
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/build/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +19 -13
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/prompts/code-reuse.js +3 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
- package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
- package/build/prompts/local-dev/local-dev-plan.js +429 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +190 -0
- package/build/prompts/pom-aware-code-reuse.js +12 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +37 -14
- package/build/prompts/sut-setup/shared.js +16 -12
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
- package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +32 -52
- package/build/recommendation/planRanker.d.ts +15 -2
- package/build/recommendation/planRanker.js +76 -5
- package/build/resources/testbotResource.js +2 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +2 -1
- package/build/services/TestExecutionService.js +8 -3
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +39 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/codeReuseTool.js +43 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
- package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
- package/build/tools/code-refactor/reuse-outcome.js +158 -0
- package/build/tools/code-refactor/reuse-state.d.ts +45 -0
- package/build/tools/code-refactor/reuse-state.js +140 -0
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +62 -21
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
- package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
- package/build/tools/generate-tests/generateMockRestTool.js +391 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generate-tests/planGuard.js +2 -22
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +264 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +96 -0
- package/build/tools/queryProxyMocksTool.d.ts +70 -0
- package/build/tools/queryProxyMocksTool.js +522 -0
- package/build/tools/runExistingTestsTool.d.ts +31 -0
- package/build/tools/runExistingTestsTool.js +214 -13
- package/build/tools/submitReportTool.js +38 -3
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +63 -40
- package/build/tools/test-management/registerTestPlanTool.js +55 -7
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- package/build/types/FrontendIntegration.d.ts +3 -0
- package/build/types/FrontendIntegration.js +3 -0
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/Recommendation.d.ts +20 -0
- package/build/types/Recommendation.js +32 -6
- package/build/types/RepositoryAnalysis.d.ts +131 -14
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/ReuseOutcome.d.ts +63 -0
- package/build/types/ReuseOutcome.js +33 -0
- package/build/types/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +22 -6
- package/build/types/TestbotReport.d.ts +7 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +26 -0
- package/build/utils/AnalysisStateManager.js +31 -1
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +99 -14
- package/build/utils/featureFlags.d.ts +31 -0
- package/build/utils/featureFlags.js +37 -0
- package/build/utils/frontendIntegration.js +8 -1
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +15 -0
- package/build/utils/logger.js +1 -1
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/pom-scope/pom-files.js +7 -0
- package/build/utils/pom-verify/bindings.js +11 -1
- package/build/utils/pom-verify/re-exports.d.ts +10 -0
- package/build/utils/pom-verify/re-exports.js +61 -0
- package/build/utils/pom-verify/resolve.d.ts +4 -1
- package/build/utils/pom-verify/resolve.js +7 -4
- package/build/utils/pom-verify/verify.d.ts +5 -0
- package/build/utils/pom-verify/verify.js +25 -3
- package/build/utils/progress.js +10 -5
- package/build/utils/proxy-terminal.js +3 -3
- package/build/utils/routeParsers.d.ts +3 -9
- package/build/utils/routeParsers.js +79 -4
- package/build/utils/utils.js +2 -2
- package/build/utils/versions.d.ts +4 -3
- package/build/utils/versions.js +3 -1
- package/build/utils/workspaceAuth.d.ts +46 -0
- package/build/utils/workspaceAuth.js +156 -1
- package/build/workspace/testSuites.d.ts +2 -1
- package/build/workspace/testSuites.js +1 -1
- package/build/workspace/workspace.d.ts +108 -32
- package/build/workspace/workspace.js +32 -4
- package/package.json +5 -2
- package/build/adapters/jestAdapter.test.d.ts +0 -1
- package/build/adapters/jestAdapter.test.js +0 -93
- package/build/adapters/mochaAdapter.test.d.ts +0 -1
- package/build/adapters/mochaAdapter.test.js +0 -63
- package/build/adapters/playwrightAdapter.test.d.ts +0 -1
- package/build/adapters/playwrightAdapter.test.js +0 -169
- package/build/adapters/pytestAdapter.test.d.ts +0 -1
- package/build/adapters/pytestAdapter.test.js +0 -90
- package/build/prompts/code-reuse.test.d.ts +0 -1
- package/build/prompts/code-reuse.test.js +0 -62
- package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
- package/build/prompts/pom-aware-code-reuse.test.js +0 -11
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
- package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
- package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
- package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
- package/build/prompts/testbot/testbot-prompts.test.js +0 -451
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
- package/build/recommendation/discriminators.test.d.ts +0 -1
- package/build/recommendation/discriminators.test.js +0 -324
- package/build/recommendation/diversity.test.d.ts +0 -1
- package/build/recommendation/diversity.test.js +0 -77
- package/build/recommendation/planRanker.test.d.ts +0 -1
- package/build/recommendation/planRanker.test.js +0 -110
- package/build/resources/testbotResource.test.d.ts +0 -1
- package/build/resources/testbotResource.test.js +0 -44
- package/build/services/AnalyticsService.test.d.ts +0 -1
- package/build/services/AnalyticsService.test.js +0 -86
- package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.integration.test.js +0 -162
- package/build/services/ScenarioGenerationService.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.test.js +0 -414
- package/build/services/TestDiscoveryService.test.d.ts +0 -1
- package/build/services/TestDiscoveryService.test.js +0 -983
- package/build/services/TestExecutionService.test.d.ts +0 -1
- package/build/services/TestExecutionService.test.js +0 -1032
- package/build/services/TestGenerationService.test.d.ts +0 -1
- package/build/services/TestGenerationService.test.js +0 -578
- package/build/tool-phase-coverage.test.d.ts +0 -1
- package/build/tool-phase-coverage.test.js +0 -49
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
- package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
- package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
- package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
- package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
- package/build/tools/generate-tests/planGuard.test.js +0 -185
- package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
- package/build/tools/generate-tests/scenarioLint.test.js +0 -100
- package/build/tools/runExistingTestsTool.test.d.ts +0 -1
- package/build/tools/runExistingTestsTool.test.js +0 -379
- package/build/tools/submitReportTool.test.d.ts +0 -1
- package/build/tools/submitReportTool.test.js +0 -1428
- package/build/tools/test-management/actionsTool.test.d.ts +0 -1
- package/build/tools/test-management/actionsTool.test.js +0 -436
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
- package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
- package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
- package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
- package/build/tools/trace/resolveSessionPaths.test.js +0 -104
- package/build/tools/trace/sessionState.test.d.ts +0 -1
- package/build/tools/trace/sessionState.test.js +0 -17
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
- package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
- package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
- package/build/tools/workspace/serviceUpsert.test.js +0 -50
- package/build/utils/AnalysisStateManager.test.d.ts +0 -1
- package/build/utils/AnalysisStateManager.test.js +0 -134
- package/build/utils/dartRouteExtractor.test.d.ts +0 -1
- package/build/utils/dartRouteExtractor.test.js +0 -307
- package/build/utils/docker.test.d.ts +0 -1
- package/build/utils/docker.test.js +0 -114
- package/build/utils/featureFlags.test.d.ts +0 -1
- package/build/utils/featureFlags.test.js +0 -81
- package/build/utils/fileWalk.test.d.ts +0 -1
- package/build/utils/fileWalk.test.js +0 -252
- package/build/utils/frontendIntegration.test.d.ts +0 -1
- package/build/utils/frontendIntegration.test.js +0 -229
- package/build/utils/frontendSelectors.test.d.ts +0 -1
- package/build/utils/frontendSelectors.test.js +0 -118
- package/build/utils/gitStaging.test.d.ts +0 -1
- package/build/utils/gitStaging.test.js +0 -111
- package/build/utils/httpDefaults.test.d.ts +0 -1
- package/build/utils/httpDefaults.test.js +0 -21
- package/build/utils/importerHop.test.d.ts +0 -1
- package/build/utils/importerHop.test.js +0 -469
- package/build/utils/pathAffinityClassification.test.d.ts +0 -1
- package/build/utils/pathAffinityClassification.test.js +0 -208
- package/build/utils/planMatchKeys.test.d.ts +0 -1
- package/build/utils/planMatchKeys.test.js +0 -123
- package/build/utils/pom-scope/index.test.d.ts +0 -1
- package/build/utils/pom-scope/index.test.js +0 -278
- package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
- package/build/utils/pom-scope/pom-files.test.js +0 -29
- package/build/utils/pom-scope/scoring.test.d.ts +0 -1
- package/build/utils/pom-scope/scoring.test.js +0 -39
- package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
- package/build/utils/pom-scope/selector-extractor.test.js +0 -67
- package/build/utils/pom-verify/bindings.test.d.ts +0 -1
- package/build/utils/pom-verify/bindings.test.js +0 -164
- package/build/utils/pom-verify/calls.test.d.ts +0 -1
- package/build/utils/pom-verify/calls.test.js +0 -61
- package/build/utils/pom-verify/resolve.test.d.ts +0 -1
- package/build/utils/pom-verify/resolve.test.js +0 -114
- package/build/utils/pom-verify/verify.test.d.ts +0 -1
- package/build/utils/pom-verify/verify.test.js +0 -374
- package/build/utils/pr-comment-parser.test.d.ts +0 -1
- package/build/utils/pr-comment-parser.test.js +0 -428
- package/build/utils/progress.test.d.ts +0 -1
- package/build/utils/progress.test.js +0 -37
- package/build/utils/projectMetadata.test.d.ts +0 -1
- package/build/utils/projectMetadata.test.js +0 -172
- package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
- package/build/utils/pythonMountPrefixes.test.js +0 -113
- package/build/utils/repoScanner.test.d.ts +0 -1
- package/build/utils/repoScanner.test.js +0 -190
- package/build/utils/reportVerification.test.d.ts +0 -1
- package/build/utils/reportVerification.test.js +0 -185
- package/build/utils/routeParsers.test.d.ts +0 -1
- package/build/utils/routeParsers.test.js +0 -1011
- package/build/utils/scenarioDrafting.test.d.ts +0 -1
- package/build/utils/scenarioDrafting.test.js +0 -876
- package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
- package/build/utils/sourceRouteExtractor.test.js +0 -738
- package/build/utils/telemetry.test.d.ts +0 -1
- package/build/utils/telemetry.test.js +0 -70
- package/build/utils/trace-parser.test.d.ts +0 -6
- package/build/utils/trace-parser.test.js +0 -140
- package/build/utils/uiPageEnumerator.test.d.ts +0 -1
- package/build/utils/uiPageEnumerator.test.js +0 -824
- package/build/utils/utils.test.d.ts +0 -1
- package/build/utils/utils.test.js +0 -103
- package/build/utils/walkerCharacterization.test.d.ts +0 -1
- package/build/utils/walkerCharacterization.test.js +0 -233
- package/build/utils/workspaceAuth.test.d.ts +0 -1
- package/build/utils/workspaceAuth.test.js +0 -260
- package/build/workspace/testSuites.test.d.ts +0 -1
- package/build/workspace/testSuites.test.js +0 -23
- package/build/workspace/workspace.test.d.ts +0 -1
- package/build/workspace/workspace.test.js +0 -264
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import { jest } from "@jest/globals";
|
|
2
|
-
// Mock child_process before importing the module under test
|
|
3
|
-
const mockedExecFileSync = jest.fn();
|
|
4
|
-
jest.unstable_mockModule("child_process", () => ({
|
|
5
|
-
execFileSync: mockedExecFileSync,
|
|
6
|
-
}));
|
|
7
|
-
const { parsePRComments } = await import("./pr-comment-parser.js");
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Fixture helpers — build PR comment bodies matching the real renderReport()
|
|
10
|
-
// output wrapped in the progress comment from testbot/src/progress.ts.
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
function progressWrapper(reportBody) {
|
|
13
|
-
return `### Skyramp Testbot Plan
|
|
14
|
-
Reviewing the Pull Request for test recommendations.
|
|
15
|
-
|
|
16
|
-
- [x] Analyzing code changes
|
|
17
|
-
- [x] Running tests
|
|
18
|
-
- [x] Generating report
|
|
19
|
-
|
|
20
|
-
${reportBody}`;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Realistic full report from commit 1 of a PR.
|
|
24
|
-
* Matches the exact markdown format produced by renderReport() in testbot.
|
|
25
|
-
*/
|
|
26
|
-
const FULL_REPORT_COMMIT_1 = progressWrapper(`### 📋 Business Case Analysis
|
|
27
|
-
This PR adds CRUD endpoints for the items resource and an orders endpoint.
|
|
28
|
-
|
|
29
|
-
### 💡 New Tests Created
|
|
30
|
-
- **Integration** for POST/GET/PUT/DELETE /api/v1/items/ — \`test_items_integration.py\`
|
|
31
|
-
Tests full CRUD lifecycle on items
|
|
32
|
-
📎 Scenario: \`tests/scenario_crud_items.json\`
|
|
33
|
-
- **Contract** for GET /api/v1/items/{item_id} — \`test_items_contract.py\`
|
|
34
|
-
Validates response schema for single item retrieval
|
|
35
|
-
- **Fuzz** for POST /api/v1/items/ — \`test_items_fuzz.py\`
|
|
36
|
-
Sends malformed payloads to item creation
|
|
37
|
-
|
|
38
|
-
### 🧪 Test Results
|
|
39
|
-
| Test Type | Endpoint | Status | Details |
|
|
40
|
-
|-----------|----------|--------|---------|
|
|
41
|
-
| Integration | POST/GET/PUT/DELETE /api/v1/items/ | Pass | All 4 assertions passed |
|
|
42
|
-
| Contract | GET /api/v1/items/{item_id} | Fail | Schema mismatch on price field |
|
|
43
|
-
| Fuzz | POST /api/v1/items/ | Pass | 50 payloads, 0 crashes |
|
|
44
|
-
|
|
45
|
-
### 📌 Additional Recommendations (3)
|
|
46
|
-
<details>
|
|
47
|
-
<summary>Expand to see recommended tests not generated in this run</summary>
|
|
48
|
-
|
|
49
|
-
#### Integration
|
|
50
|
-
|
|
51
|
-
**\`order-lifecycle\`**
|
|
52
|
-
|
|
53
|
-
Integration test for order creation workflow
|
|
54
|
-
|
|
55
|
-
1. \`POST /api/v1/items/\` — Create item
|
|
56
|
-
2. \`POST /api/v1/orders/\` — Create order referencing item
|
|
57
|
-
3. \`GET /api/v1/orders/{order_id}\` — Verify order details
|
|
58
|
-
|
|
59
|
-
#### Fuzz
|
|
60
|
-
|
|
61
|
-
**\`fuzz-orders\`**
|
|
62
|
-
|
|
63
|
-
Fuzz test for order creation
|
|
64
|
-
|
|
65
|
-
1. \`POST /api/v1/orders/\` — Send malformed order payloads
|
|
66
|
-
|
|
67
|
-
#### E2E
|
|
68
|
-
|
|
69
|
-
**\`checkout-flow\`**
|
|
70
|
-
|
|
71
|
-
End-to-end checkout flow requiring Playwright traces
|
|
72
|
-
|
|
73
|
-
1. \`POST /api/v1/cart/\` — Add item to cart
|
|
74
|
-
2. \`POST /api/v1/checkout/\` — Complete checkout
|
|
75
|
-
|
|
76
|
-
</details>
|
|
77
|
-
|
|
78
|
-
### ⚠️ Issues Found
|
|
79
|
-
- Item price field returns string instead of number`);
|
|
80
|
-
/**
|
|
81
|
-
* Minimal report: no tests generated, no results, only business case + recommendations.
|
|
82
|
-
*/
|
|
83
|
-
const MINIMAL_REPORT_NO_TESTS = progressWrapper(`### 📋 Business Case Analysis
|
|
84
|
-
Frontend-only PR. No backend changes detected.
|
|
85
|
-
|
|
86
|
-
### 📌 Additional Recommendations (2)
|
|
87
|
-
<details>
|
|
88
|
-
<summary>Expand to see recommended tests not generated in this run</summary>
|
|
89
|
-
|
|
90
|
-
#### E2E
|
|
91
|
-
|
|
92
|
-
**\`login-flow\`**
|
|
93
|
-
|
|
94
|
-
E2E test for login page
|
|
95
|
-
|
|
96
|
-
1. \`POST /api/v1/auth/login\` — Authenticate user
|
|
97
|
-
|
|
98
|
-
#### UI
|
|
99
|
-
|
|
100
|
-
**\`dashboard-layout\`**
|
|
101
|
-
|
|
102
|
-
UI test for dashboard responsiveness
|
|
103
|
-
|
|
104
|
-
</details>`);
|
|
105
|
-
/**
|
|
106
|
-
* Report with maintenance results and collapsed sections.
|
|
107
|
-
*/
|
|
108
|
-
const REPORT_WITH_MAINTENANCE = progressWrapper(`<details>
|
|
109
|
-
<summary>📋 Business Case Analysis</summary>
|
|
110
|
-
|
|
111
|
-
Minor endpoint rename from /products to /items.
|
|
112
|
-
|
|
113
|
-
</details>
|
|
114
|
-
|
|
115
|
-
<details>
|
|
116
|
-
<summary>💡 New Tests Created</summary>
|
|
117
|
-
|
|
118
|
-
- **Contract** for GET /api/v1/items/ — \`test_items_contract.py\`
|
|
119
|
-
Validates list items response schema
|
|
120
|
-
|
|
121
|
-
</details>
|
|
122
|
-
|
|
123
|
-
<details>
|
|
124
|
-
<summary>✅ Test Maintenance</summary>
|
|
125
|
-
|
|
126
|
-
| File | Change | Before | After |
|
|
127
|
-
|------|--------|--------|-------|
|
|
128
|
-
| \`test_products_smoke.py\` | Updated endpoint /products → /items | Fail (404 Not Found) | Pass (200 OK) |
|
|
129
|
-
|
|
130
|
-
</details>
|
|
131
|
-
|
|
132
|
-
<details>
|
|
133
|
-
<summary>🧪 Test Results</summary>
|
|
134
|
-
|
|
135
|
-
| Test Type | Endpoint | Status | Details |
|
|
136
|
-
|-----------|----------|--------|---------|
|
|
137
|
-
| Contract | GET /api/v1/items/ | Pass | Schema valid |
|
|
138
|
-
| Smoke | GET /api/v1/items/ | Pass | After maintenance fix |
|
|
139
|
-
|
|
140
|
-
</details>`);
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// Helper to build a gh CLI response (array of PR comments)
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
function ghResponse(comments) {
|
|
145
|
-
return JSON.stringify(comments.map((c) => ({
|
|
146
|
-
id: c.id,
|
|
147
|
-
body: c.body,
|
|
148
|
-
user: { login: c.login ?? "github-actions[bot]" },
|
|
149
|
-
created_at: new Date().toISOString(),
|
|
150
|
-
})));
|
|
151
|
-
}
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
// Tests
|
|
154
|
-
// ---------------------------------------------------------------------------
|
|
155
|
-
beforeEach(() => {
|
|
156
|
-
mockedExecFileSync.mockReset();
|
|
157
|
-
});
|
|
158
|
-
describe("parsePRComments — error handling", () => {
|
|
159
|
-
it("returns empty context when PR has no comments", async () => {
|
|
160
|
-
mockedExecFileSync.mockReturnValue("[]");
|
|
161
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
162
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
163
|
-
expect(ctx.implementedTestFiles).toEqual([]);
|
|
164
|
-
expect(ctx.executionResults).toEqual([]);
|
|
165
|
-
});
|
|
166
|
-
it("returns empty context when no Testbot comments exist", async () => {
|
|
167
|
-
mockedExecFileSync.mockReturnValue(ghResponse([
|
|
168
|
-
{ id: 1, body: "LGTM!", login: "reviewer" },
|
|
169
|
-
{ id: 2, body: "Please fix the typo", login: "reviewer" },
|
|
170
|
-
]));
|
|
171
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
172
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
173
|
-
expect(ctx.implementedTestFiles).toEqual([]);
|
|
174
|
-
expect(ctx.executionResults).toEqual([]);
|
|
175
|
-
});
|
|
176
|
-
it("uses only the latest Testbot comment when multiple exist", async () => {
|
|
177
|
-
const olderReport = progressWrapper(`### 📋 Business Case Analysis
|
|
178
|
-
Old report.
|
|
179
|
-
|
|
180
|
-
### 💡 New Tests Created
|
|
181
|
-
- **Smoke** for GET /api/v1/old/ — \`test_old_smoke.py\``);
|
|
182
|
-
mockedExecFileSync.mockReturnValue(ghResponse([
|
|
183
|
-
{ id: 100, body: olderReport },
|
|
184
|
-
{ id: 200, body: FULL_REPORT_COMMIT_1 },
|
|
185
|
-
]));
|
|
186
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
187
|
-
expect(ctx.implementedTestFiles).not.toContain("test_old_smoke.py");
|
|
188
|
-
expect(ctx.implementedTestFiles).toContain("test_items_integration.py");
|
|
189
|
-
});
|
|
190
|
-
it("returns empty context when gh CLI fails", async () => {
|
|
191
|
-
mockedExecFileSync.mockImplementation(() => {
|
|
192
|
-
throw new Error("gh: command not found");
|
|
193
|
-
});
|
|
194
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
195
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
it("returns empty context when gh returns invalid JSON", async () => {
|
|
198
|
-
mockedExecFileSync.mockReturnValue("not valid json{");
|
|
199
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
200
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
201
|
-
});
|
|
202
|
-
it("returns empty context when gh returns non-array JSON", async () => {
|
|
203
|
-
mockedExecFileSync.mockReturnValue('{"error": "not found"}');
|
|
204
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
205
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
206
|
-
});
|
|
207
|
-
it("handles gh CLI timeout gracefully", async () => {
|
|
208
|
-
mockedExecFileSync.mockImplementation(() => {
|
|
209
|
-
const err = new Error("Command timed out");
|
|
210
|
-
err.status = null;
|
|
211
|
-
throw err;
|
|
212
|
-
});
|
|
213
|
-
const ctx = await parsePRComments("owner", "repo", 42);
|
|
214
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
describe("full report parsing (commit 1 — non-collapsed)", () => {
|
|
218
|
-
let ctx;
|
|
219
|
-
beforeAll(async () => {
|
|
220
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: FULL_REPORT_COMMIT_1 }]));
|
|
221
|
-
ctx = await parsePRComments("owner", "repo", 42);
|
|
222
|
-
});
|
|
223
|
-
it("extracts implemented recommendations from New Tests Created", () => {
|
|
224
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
225
|
-
// 3 test entries: Integration (4 methods), Contract (1), Fuzz (1) = at least 6
|
|
226
|
-
expect(implemented.length).toBeGreaterThanOrEqual(3);
|
|
227
|
-
});
|
|
228
|
-
it("expands POST/GET/PUT/DELETE into separate entries", () => {
|
|
229
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
230
|
-
const methods = implemented
|
|
231
|
-
.filter((r) => r.endpoint.includes("/api/v1/items/") && r.testType === "integration")
|
|
232
|
-
.map((r) => r.endpoint.split(" ")[0]);
|
|
233
|
-
expect(methods).toContain("POST");
|
|
234
|
-
expect(methods).toContain("GET");
|
|
235
|
-
expect(methods).toContain("PUT");
|
|
236
|
-
expect(methods).toContain("DELETE");
|
|
237
|
-
});
|
|
238
|
-
it("assigns correct test types via proximity matching", () => {
|
|
239
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
240
|
-
const fuzzEntry = implemented.find((r) => r.endpoint === "POST /api/v1/items/" && r.testType === "fuzz");
|
|
241
|
-
expect(fuzzEntry).toBeDefined();
|
|
242
|
-
const contractEntry = implemented.find((r) => r.endpoint.includes("/api/v1/items/{item_id}") && r.testType === "contract");
|
|
243
|
-
expect(contractEntry).toBeDefined();
|
|
244
|
-
});
|
|
245
|
-
it("extracts recommended endpoints from Additional Recommendations", () => {
|
|
246
|
-
const recommended = ctx.previousRecommendations.filter((r) => r.status === "recommended");
|
|
247
|
-
expect(recommended.length).toBeGreaterThanOrEqual(1);
|
|
248
|
-
const orderEndpoints = recommended.filter((r) => r.endpoint.includes("/api/v1/orders/"));
|
|
249
|
-
expect(orderEndpoints.length).toBeGreaterThanOrEqual(1);
|
|
250
|
-
});
|
|
251
|
-
it("extracts scenario names from Additional Recommendations", () => {
|
|
252
|
-
const recommended = ctx.previousRecommendations.filter((r) => r.status === "recommended");
|
|
253
|
-
const orderLifecycle = recommended.find((r) => r.scenarioName === "order-lifecycle");
|
|
254
|
-
expect(orderLifecycle).toBeDefined();
|
|
255
|
-
expect(orderLifecycle?.testType).toBe("integration");
|
|
256
|
-
const fuzzOrders = recommended.find((r) => r.scenarioName === "fuzz-orders");
|
|
257
|
-
expect(fuzzOrders).toBeDefined();
|
|
258
|
-
});
|
|
259
|
-
it("extracts implemented file names only from New Tests Created", () => {
|
|
260
|
-
expect(ctx.implementedTestFiles).toContain("test_items_integration.py");
|
|
261
|
-
expect(ctx.implementedTestFiles).toContain("test_items_contract.py");
|
|
262
|
-
expect(ctx.implementedTestFiles).toContain("test_items_fuzz.py");
|
|
263
|
-
expect(ctx.implementedTestFiles).toHaveLength(3);
|
|
264
|
-
});
|
|
265
|
-
it("sets prNumber correctly", () => {
|
|
266
|
-
expect(ctx.prNumber).toBe(42);
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
describe("minimal report parsing (no tests generated)", () => {
|
|
270
|
-
let ctx;
|
|
271
|
-
beforeAll(async () => {
|
|
272
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 5, body: MINIMAL_REPORT_NO_TESTS }]));
|
|
273
|
-
ctx = await parsePRComments("owner", "repo", 10);
|
|
274
|
-
});
|
|
275
|
-
it("has no implemented recommendations", () => {
|
|
276
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
277
|
-
expect(implemented).toEqual([]);
|
|
278
|
-
});
|
|
279
|
-
it("has no implemented files", () => {
|
|
280
|
-
expect(ctx.implementedTestFiles).toEqual([]);
|
|
281
|
-
});
|
|
282
|
-
it("extracts recommended endpoints from Additional Recommendations", () => {
|
|
283
|
-
const recommended = ctx.previousRecommendations.filter((r) => r.status === "recommended");
|
|
284
|
-
expect(recommended.length).toBeGreaterThanOrEqual(1);
|
|
285
|
-
const loginRec = recommended.find((r) => r.endpoint.includes("/api/v1/auth/login"));
|
|
286
|
-
expect(loginRec).toBeDefined();
|
|
287
|
-
});
|
|
288
|
-
it("extracts scenario names", () => {
|
|
289
|
-
const recommended = ctx.previousRecommendations.filter((r) => r.status === "recommended");
|
|
290
|
-
expect(recommended.find((r) => r.scenarioName === "login-flow")).toBeDefined();
|
|
291
|
-
// dashboard-layout has no endpoint pattern — parser only captures recs with endpoints
|
|
292
|
-
expect(recommended.find((r) => r.scenarioName === "dashboard-layout")).toBeUndefined();
|
|
293
|
-
});
|
|
294
|
-
it("has no execution results", () => {
|
|
295
|
-
expect(ctx.executionResults).toEqual([]);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
describe("collapsed report parsing (details/summary tags)", () => {
|
|
299
|
-
let ctx;
|
|
300
|
-
beforeAll(async () => {
|
|
301
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 10, body: REPORT_WITH_MAINTENANCE }]));
|
|
302
|
-
ctx = await parsePRComments("owner", "repo", 99);
|
|
303
|
-
});
|
|
304
|
-
it("extracts implemented tests from collapsed sections", () => {
|
|
305
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
306
|
-
expect(implemented.length).toBeGreaterThanOrEqual(1);
|
|
307
|
-
const contractEntry = implemented.find((r) => r.endpoint.includes("/api/v1/items/"));
|
|
308
|
-
expect(contractEntry).toBeDefined();
|
|
309
|
-
});
|
|
310
|
-
it("extracts implemented file names from collapsed sections", () => {
|
|
311
|
-
expect(ctx.implementedTestFiles).toContain("test_items_contract.py");
|
|
312
|
-
});
|
|
313
|
-
it("does not extract maintenance files as implemented test files", () => {
|
|
314
|
-
expect(ctx.implementedTestFiles).not.toContain("test_products_smoke.py");
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
describe("edge cases", () => {
|
|
318
|
-
it("parses POST/GET without trailing backtick in path", async () => {
|
|
319
|
-
const report = progressWrapper(`### 💡 New Tests Created
|
|
320
|
-
- **Integration** for POST/GET /api/v1/products/ — \`test_products_integration.py\``);
|
|
321
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: report }]));
|
|
322
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
323
|
-
const implemented = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
324
|
-
const paths = implemented.map((r) => r.endpoint);
|
|
325
|
-
expect(paths).toContain("POST /api/v1/products/");
|
|
326
|
-
expect(paths).toContain("GET /api/v1/products/");
|
|
327
|
-
for (const p of paths) {
|
|
328
|
-
expect(p).not.toContain("`");
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
it("handles path params like {item_id}", async () => {
|
|
332
|
-
const report = progressWrapper(`### 💡 New Tests Created
|
|
333
|
-
- **Contract** for GET /api/v1/items/{item_id} — \`test_get_item_contract.py\``);
|
|
334
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: report }]));
|
|
335
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
336
|
-
const impl = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
337
|
-
expect(impl).toEqual(expect.arrayContaining([
|
|
338
|
-
expect.objectContaining({ endpoint: "GET /api/v1/items/{item_id}" }),
|
|
339
|
-
]));
|
|
340
|
-
});
|
|
341
|
-
it("de-duplicates implemented file names", async () => {
|
|
342
|
-
const report = progressWrapper(`### 💡 New Tests Created
|
|
343
|
-
- **Integration** for POST /api/v1/items/ — \`test_items_integration.py\`
|
|
344
|
-
- **Contract** for GET /api/v1/items/ — \`test_items_integration.py\``);
|
|
345
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: report }]));
|
|
346
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
347
|
-
const count = ctx.implementedTestFiles.filter((f) => f === "test_items_integration.py").length;
|
|
348
|
-
expect(count).toBe(1);
|
|
349
|
-
});
|
|
350
|
-
it("handles empty Testbot comment body", async () => {
|
|
351
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: "Skyramp Testbot — empty run" }]));
|
|
352
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
353
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
354
|
-
expect(ctx.implementedTestFiles).toEqual([]);
|
|
355
|
-
expect(ctx.executionResults).toEqual([]);
|
|
356
|
-
});
|
|
357
|
-
it("ignores non-bot comments interspersed with bot comments", async () => {
|
|
358
|
-
const botComment = progressWrapper(`### 💡 New Tests Created
|
|
359
|
-
- **Smoke** for GET /health — \`test_health_smoke.py\``);
|
|
360
|
-
mockedExecFileSync.mockReturnValue(ghResponse([
|
|
361
|
-
{ id: 1, body: "Looks good to me!", login: "reviewer" },
|
|
362
|
-
{ id: 2, body: botComment },
|
|
363
|
-
{ id: 3, body: "Can you add more tests?", login: "reviewer" },
|
|
364
|
-
]));
|
|
365
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
366
|
-
expect(ctx.implementedTestFiles).toContain("test_health_smoke.py");
|
|
367
|
-
});
|
|
368
|
-
it("ignores human comments that mention bot markers", async () => {
|
|
369
|
-
const humanMentioningBot = "The Skyramp Testbot output above looks wrong, can you rerun?";
|
|
370
|
-
const realBotComment = progressWrapper(`### 💡 New Tests Created
|
|
371
|
-
- **Contract** for GET /api/v1/items/ — \`test_items_contract.py\``);
|
|
372
|
-
mockedExecFileSync.mockReturnValue(ghResponse([
|
|
373
|
-
{ id: 1, body: realBotComment },
|
|
374
|
-
{ id: 2, body: humanMentioningBot, login: "reviewer" },
|
|
375
|
-
]));
|
|
376
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
377
|
-
expect(ctx.implementedTestFiles).toContain("test_items_contract.py");
|
|
378
|
-
expect(ctx.previousRecommendations.length).toBeGreaterThanOrEqual(1);
|
|
379
|
-
});
|
|
380
|
-
it("ignores all comments when only a human mentions bot markers", async () => {
|
|
381
|
-
const humanMentioningBot = "Hey team, check the Skyramp Testbot Plan from last time";
|
|
382
|
-
mockedExecFileSync.mockReturnValue(ghResponse([
|
|
383
|
-
{ id: 1, body: humanMentioningBot, login: "developer123" },
|
|
384
|
-
]));
|
|
385
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
386
|
-
expect(ctx.previousRecommendations).toEqual([]);
|
|
387
|
-
expect(ctx.implementedTestFiles).toEqual([]);
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
describe("roundtrip: renderReport format → parser extraction", () => {
|
|
391
|
-
it("parses the exact format from testbot report.test.ts validReport", async () => {
|
|
392
|
-
// Mirrors the validReport fixture from testbot/src/__tests__/report.test.ts
|
|
393
|
-
// rendered through renderReport() non-collapsed
|
|
394
|
-
const renderedReport = `### 📋 Business Case Analysis
|
|
395
|
-
Tests cover the checkout flow.
|
|
396
|
-
|
|
397
|
-
### 💡 New Tests Created
|
|
398
|
-
- **contract** for POST /orders — \`test_orders_contract.py\`
|
|
399
|
-
|
|
400
|
-
### ✅ Test Maintenance
|
|
401
|
-
- Updated auth header in existing tests
|
|
402
|
-
|
|
403
|
-
### 🧪 Test Results
|
|
404
|
-
| Test Type | Endpoint | Status | Details |
|
|
405
|
-
|-----------|----------|--------|---------|
|
|
406
|
-
| contract | POST /orders | PASS | All assertions passed |
|
|
407
|
-
| fuzz | GET /products | FAIL | Unexpected 500 |
|
|
408
|
-
|
|
409
|
-
### ⚠️ Issues Found
|
|
410
|
-
- Server returns 500 on empty query param`;
|
|
411
|
-
mockedExecFileSync.mockReturnValue(ghResponse([{ id: 1, body: progressWrapper(renderedReport) }]));
|
|
412
|
-
const ctx = await parsePRComments("owner", "repo", 1);
|
|
413
|
-
// Implemented: contract for POST /orders
|
|
414
|
-
const impl = ctx.previousRecommendations.filter((r) => r.status === "implemented");
|
|
415
|
-
expect(impl).toEqual(expect.arrayContaining([
|
|
416
|
-
expect.objectContaining({
|
|
417
|
-
testType: "contract",
|
|
418
|
-
endpoint: "POST /orders",
|
|
419
|
-
status: "implemented",
|
|
420
|
-
}),
|
|
421
|
-
]));
|
|
422
|
-
// File name from New Tests section only
|
|
423
|
-
expect(ctx.implementedTestFiles).toContain("test_orders_contract.py");
|
|
424
|
-
// No Additional Recommendations section → no recommended entries
|
|
425
|
-
const recommended = ctx.previousRecommendations.filter((r) => r.status === "recommended");
|
|
426
|
-
expect(recommended).toEqual([]);
|
|
427
|
-
});
|
|
428
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { jest } from "@jest/globals";
|
|
2
|
-
import { makeProgressReporter } from "./progress.js";
|
|
3
|
-
function makeExtra(progressToken) {
|
|
4
|
-
const sendNotification = jest.fn(async () => { });
|
|
5
|
-
const extra = {
|
|
6
|
-
_meta: progressToken !== undefined ? { progressToken } : {},
|
|
7
|
-
sendNotification,
|
|
8
|
-
};
|
|
9
|
-
return { extra, sendNotification };
|
|
10
|
-
}
|
|
11
|
-
describe("makeProgressReporter", () => {
|
|
12
|
-
it("sends a notifications/progress message when a progress token is present", async () => {
|
|
13
|
-
const { extra, sendNotification } = makeExtra("tok-1");
|
|
14
|
-
const report = makeProgressReporter(extra);
|
|
15
|
-
await report(40, 100, "halfway");
|
|
16
|
-
expect(sendNotification).toHaveBeenCalledTimes(1);
|
|
17
|
-
expect(sendNotification).toHaveBeenCalledWith({
|
|
18
|
-
method: "notifications/progress",
|
|
19
|
-
params: { progressToken: "tok-1", progress: 40, total: 100, message: "halfway" },
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
it("no-ops when the client supplied no progress token", async () => {
|
|
23
|
-
const { extra, sendNotification } = makeExtra(undefined);
|
|
24
|
-
const report = makeProgressReporter(extra);
|
|
25
|
-
await report(10, 100, "starting");
|
|
26
|
-
expect(sendNotification).not.toHaveBeenCalled();
|
|
27
|
-
});
|
|
28
|
-
it("passes a numeric progress token through unchanged", async () => {
|
|
29
|
-
const { extra, sendNotification } = makeExtra(0);
|
|
30
|
-
const report = makeProgressReporter(extra);
|
|
31
|
-
await report(100, 100, "done");
|
|
32
|
-
expect(sendNotification).toHaveBeenCalledWith({
|
|
33
|
-
method: "notifications/progress",
|
|
34
|
-
params: { progressToken: 0, progress: 100, total: 100, message: "done" },
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
|
-
import { detectProjectMetadata } from "./projectMetadata.js";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import * as os from "os";
|
|
6
|
-
function makeTempDir() {
|
|
7
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), "pm-test-"));
|
|
8
|
-
}
|
|
9
|
-
describe("detectProjectMetadata", () => {
|
|
10
|
-
it("detects Node.js + Express project", () => {
|
|
11
|
-
const dir = makeTempDir();
|
|
12
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ dependencies: { express: "^4.18.0" } }));
|
|
13
|
-
const meta = detectProjectMetadata(dir);
|
|
14
|
-
expect(meta.primaryLanguage).toBe("JavaScript");
|
|
15
|
-
expect(meta.frameworks).toContain("Express");
|
|
16
|
-
expect(meta.projectType).toBe("rest-api");
|
|
17
|
-
});
|
|
18
|
-
it("detects Node.js + Next.js as fullstack", () => {
|
|
19
|
-
const dir = makeTempDir();
|
|
20
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ dependencies: { next: "^14.0.0", react: "^18.0.0" } }));
|
|
21
|
-
const meta = detectProjectMetadata(dir);
|
|
22
|
-
expect(meta.frameworks).toContain("Next.js");
|
|
23
|
-
expect(meta.frameworks).toContain("React");
|
|
24
|
-
expect(["frontend", "full-stack"]).toContain(meta.projectType);
|
|
25
|
-
});
|
|
26
|
-
it("detects Python + FastAPI project", () => {
|
|
27
|
-
const dir = makeTempDir();
|
|
28
|
-
fs.writeFileSync(path.join(dir, "requirements.txt"), "fastapi==0.100.0\nuvicorn\n");
|
|
29
|
-
const meta = detectProjectMetadata(dir);
|
|
30
|
-
expect(meta.primaryLanguage).toBe("Python");
|
|
31
|
-
expect(meta.frameworks).toContain("FastAPI");
|
|
32
|
-
});
|
|
33
|
-
it("detects Python + Django project", () => {
|
|
34
|
-
const dir = makeTempDir();
|
|
35
|
-
fs.writeFileSync(path.join(dir, "requirements.txt"), "django==4.2\n");
|
|
36
|
-
const meta = detectProjectMetadata(dir);
|
|
37
|
-
expect(meta.primaryLanguage).toBe("Python");
|
|
38
|
-
expect(meta.frameworks).toContain("Django");
|
|
39
|
-
});
|
|
40
|
-
it("detects Java + Spring project", () => {
|
|
41
|
-
const dir = makeTempDir();
|
|
42
|
-
fs.writeFileSync(path.join(dir, "pom.xml"), "<project></project>");
|
|
43
|
-
const meta = detectProjectMetadata(dir);
|
|
44
|
-
expect(meta.primaryLanguage).toBe("Java");
|
|
45
|
-
expect(meta.frameworks).toContain("Spring");
|
|
46
|
-
});
|
|
47
|
-
it("detects Go + Gin project", () => {
|
|
48
|
-
const dir = makeTempDir();
|
|
49
|
-
fs.writeFileSync(path.join(dir, "go.mod"), "module example.com/app\nrequire github.com/gin-gonic/gin v1.9.0\n");
|
|
50
|
-
const meta = detectProjectMetadata(dir);
|
|
51
|
-
expect(meta.primaryLanguage).toBe("Go");
|
|
52
|
-
expect(meta.frameworks).toContain("Gin");
|
|
53
|
-
});
|
|
54
|
-
it("detects Ruby + Rails project", () => {
|
|
55
|
-
const dir = makeTempDir();
|
|
56
|
-
fs.writeFileSync(path.join(dir, "Gemfile"), 'source "https://rubygems.org"\ngem "rails", "~> 7.0"\n');
|
|
57
|
-
const meta = detectProjectMetadata(dir);
|
|
58
|
-
expect(meta.primaryLanguage).toBe("Ruby");
|
|
59
|
-
expect(meta.frameworks).toContain("Rails");
|
|
60
|
-
});
|
|
61
|
-
it("detects PHP + Laravel project", () => {
|
|
62
|
-
const dir = makeTempDir();
|
|
63
|
-
fs.writeFileSync(path.join(dir, "composer.json"), JSON.stringify({ require: { "laravel/framework": "^10.0" } }));
|
|
64
|
-
const meta = detectProjectMetadata(dir);
|
|
65
|
-
expect(meta.primaryLanguage).toBe("PHP");
|
|
66
|
-
expect(meta.frameworks).toContain("Laravel");
|
|
67
|
-
});
|
|
68
|
-
it("detects Rust + Actix project", () => {
|
|
69
|
-
const dir = makeTempDir();
|
|
70
|
-
fs.writeFileSync(path.join(dir, "Cargo.toml"), '[dependencies]\nactix-web = "4"\n');
|
|
71
|
-
const meta = detectProjectMetadata(dir);
|
|
72
|
-
expect(meta.primaryLanguage).toBe("Rust");
|
|
73
|
-
expect(meta.frameworks).toContain("Actix");
|
|
74
|
-
});
|
|
75
|
-
it("returns unknown for empty directory", () => {
|
|
76
|
-
const dir = makeTempDir();
|
|
77
|
-
const meta = detectProjectMetadata(dir);
|
|
78
|
-
expect(meta.primaryLanguage).toBe("unknown");
|
|
79
|
-
expect(meta.primaryFramework).toBe("unknown");
|
|
80
|
-
});
|
|
81
|
-
it("detects Angular single-package project as frontend", () => {
|
|
82
|
-
const dir = makeTempDir();
|
|
83
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ dependencies: { "@angular/core": "^17.0.0" } }));
|
|
84
|
-
const meta = detectProjectMetadata(dir);
|
|
85
|
-
expect(meta.frameworks).toContain("Angular");
|
|
86
|
-
expect(meta.projectType).toBe("frontend");
|
|
87
|
-
});
|
|
88
|
-
it("detects Nuxt single-package project as frontend", () => {
|
|
89
|
-
const dir = makeTempDir();
|
|
90
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ dependencies: { nuxt: "^3.0.0" } }));
|
|
91
|
-
const meta = detectProjectMetadata(dir);
|
|
92
|
-
expect(meta.frameworks).toContain("Nuxt");
|
|
93
|
-
expect(meta.projectType).toBe("frontend");
|
|
94
|
-
});
|
|
95
|
-
// Regression for SKYR-3920: monorepos keep framework deps in nested manifests,
|
|
96
|
-
// so root-only inspection mis-classified them as "other" and suppressed UI/E2E.
|
|
97
|
-
it("detects a pnpm-workspace monorepo (NestJS + SvelteKit) as full-stack", () => {
|
|
98
|
-
const dir = makeTempDir();
|
|
99
|
-
// Root manifest carries only tooling deps — no framework signal.
|
|
100
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({
|
|
101
|
-
name: "monorepo",
|
|
102
|
-
private: true,
|
|
103
|
-
devDependencies: { prettier: "^3", vitest: "^2" },
|
|
104
|
-
}));
|
|
105
|
-
fs.writeFileSync(path.join(dir, "pnpm-workspace.yaml"), "packages:\n - server\n - web\n");
|
|
106
|
-
fs.mkdirSync(path.join(dir, "server"));
|
|
107
|
-
fs.writeFileSync(path.join(dir, "server", "package.json"), JSON.stringify({
|
|
108
|
-
dependencies: { "@nestjs/core": "^10", express: "^5" },
|
|
109
|
-
devDependencies: { typescript: "^5" },
|
|
110
|
-
}));
|
|
111
|
-
fs.mkdirSync(path.join(dir, "web"));
|
|
112
|
-
fs.writeFileSync(path.join(dir, "web", "package.json"), JSON.stringify({
|
|
113
|
-
devDependencies: {
|
|
114
|
-
"@sveltejs/kit": "^2",
|
|
115
|
-
svelte: "^5",
|
|
116
|
-
typescript: "^5",
|
|
117
|
-
},
|
|
118
|
-
}));
|
|
119
|
-
const meta = detectProjectMetadata(dir);
|
|
120
|
-
expect(meta.projectType).toBe("full-stack");
|
|
121
|
-
expect(meta.frameworks).toContain("NestJS");
|
|
122
|
-
expect(meta.frameworks).toContain("SvelteKit");
|
|
123
|
-
expect(meta.primaryFramework).toBe("NestJS");
|
|
124
|
-
expect(meta.primaryLanguage).toBe("TypeScript");
|
|
125
|
-
});
|
|
126
|
-
it("detects an npm-workspaces monorepo (Express + React) as full-stack", () => {
|
|
127
|
-
const dir = makeTempDir();
|
|
128
|
-
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({
|
|
129
|
-
name: "monorepo",
|
|
130
|
-
private: true,
|
|
131
|
-
workspaces: ["apps/*"],
|
|
132
|
-
}));
|
|
133
|
-
fs.mkdirSync(path.join(dir, "apps", "api"), { recursive: true });
|
|
134
|
-
fs.writeFileSync(path.join(dir, "apps", "api", "package.json"), JSON.stringify({ dependencies: { express: "^4" } }));
|
|
135
|
-
fs.mkdirSync(path.join(dir, "apps", "web"), { recursive: true });
|
|
136
|
-
fs.writeFileSync(path.join(dir, "apps", "web", "package.json"), JSON.stringify({ dependencies: { react: "^18" } }));
|
|
137
|
-
const meta = detectProjectMetadata(dir);
|
|
138
|
-
expect(meta.projectType).toBe("full-stack");
|
|
139
|
-
expect(meta.frameworks).toContain("Express");
|
|
140
|
-
expect(meta.frameworks).toContain("React");
|
|
141
|
-
});
|
|
142
|
-
it("detects a common-shape monorepo (server/ + web/) without workspace config", () => {
|
|
143
|
-
const dir = makeTempDir();
|
|
144
|
-
// No root package.json / workspace file — rely on common-shape dir fallback.
|
|
145
|
-
fs.mkdirSync(path.join(dir, "server"));
|
|
146
|
-
fs.writeFileSync(path.join(dir, "server", "package.json"), JSON.stringify({ dependencies: { "@nestjs/core": "^10" } }));
|
|
147
|
-
fs.mkdirSync(path.join(dir, "web"));
|
|
148
|
-
fs.writeFileSync(path.join(dir, "web", "package.json"), JSON.stringify({ dependencies: { "@sveltejs/kit": "^2" } }));
|
|
149
|
-
const meta = detectProjectMetadata(dir);
|
|
150
|
-
expect(meta.projectType).toBe("full-stack");
|
|
151
|
-
expect(meta.frameworks).toContain("NestJS");
|
|
152
|
-
expect(meta.frameworks).toContain("SvelteKit");
|
|
153
|
-
});
|
|
154
|
-
it("does not scan workspace dirs that resolve outside the repository", () => {
|
|
155
|
-
const parent = makeTempDir();
|
|
156
|
-
// A sibling dir outside the scanned repo, with a detectable framework.
|
|
157
|
-
fs.mkdirSync(path.join(parent, "sibling"));
|
|
158
|
-
fs.writeFileSync(path.join(parent, "sibling", "package.json"), JSON.stringify({ dependencies: { "@nestjs/core": "^10" } }));
|
|
159
|
-
// The repo declares a hostile workspace glob escaping its own root.
|
|
160
|
-
const repo = path.join(parent, "repo");
|
|
161
|
-
fs.mkdirSync(repo);
|
|
162
|
-
fs.writeFileSync(path.join(repo, "package.json"), JSON.stringify({
|
|
163
|
-
name: "repo",
|
|
164
|
-
private: true,
|
|
165
|
-
workspaces: ["../sibling"],
|
|
166
|
-
}));
|
|
167
|
-
const meta = detectProjectMetadata(repo);
|
|
168
|
-
// The out-of-repo sibling's NestJS must NOT leak into detection.
|
|
169
|
-
expect(meta.frameworks).not.toContain("NestJS");
|
|
170
|
-
expect(meta.projectType).toBe("other");
|
|
171
|
-
});
|
|
172
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|