@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.
Files changed (289) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -0
  2. package/build/commands/commandLibrary.js +19 -13
  3. package/build/commands/localDevTestChangesCommand.d.ts +15 -0
  4. package/build/commands/localDevTestChangesCommand.js +201 -0
  5. package/build/index.js +80 -6
  6. package/build/prompts/code-reuse.js +3 -0
  7. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
  8. package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
  9. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
  10. package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
  11. package/build/prompts/local-dev/local-dev-plan.js +429 -0
  12. package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
  13. package/build/prompts/local-dev/local-dev-prompts.js +190 -0
  14. package/build/prompts/pom-aware-code-reuse.js +12 -0
  15. package/build/prompts/prompt-utils.d.ts +8 -0
  16. package/build/prompts/prompt-utils.js +33 -0
  17. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +37 -14
  18. package/build/prompts/sut-setup/shared.js +16 -12
  19. package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
  20. package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
  21. package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
  22. package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
  23. package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
  24. package/build/prompts/testbot/testbot-prompts.js +32 -52
  25. package/build/recommendation/planRanker.d.ts +15 -2
  26. package/build/recommendation/planRanker.js +76 -5
  27. package/build/resources/testbotResource.js +2 -1
  28. package/build/services/AnalyticsService.d.ts +1 -1
  29. package/build/services/TestExecutionService.d.ts +2 -1
  30. package/build/services/TestExecutionService.js +8 -3
  31. package/build/services/TestGenerationService.d.ts +2 -2
  32. package/build/services/TestGenerationService.js +39 -21
  33. package/build/services/containerEnv.js +3 -1
  34. package/build/tool-phases.js +6 -0
  35. package/build/tools/code-refactor/codeReuseTool.js +43 -4
  36. package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
  37. package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
  38. package/build/tools/code-refactor/reuse-outcome.js +158 -0
  39. package/build/tools/code-refactor/reuse-state.d.ts +45 -0
  40. package/build/tools/code-refactor/reuse-state.js +140 -0
  41. package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
  42. package/build/tools/enrichTestWithMocksTool.js +726 -0
  43. package/build/tools/executeSkyrampTestTool.d.ts +11 -0
  44. package/build/tools/executeSkyrampTestTool.js +62 -21
  45. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
  46. package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
  47. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
  48. package/build/tools/generate-tests/generateContractRestTool.js +2 -2
  49. package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
  50. package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
  51. package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
  52. package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
  53. package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
  54. package/build/tools/generate-tests/generateMockRestTool.js +391 -22
  55. package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
  56. package/build/tools/generate-tests/planGuard.js +2 -22
  57. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
  58. package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
  59. package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
  60. package/build/tools/localDevWorkerComposeTool.js +264 -0
  61. package/build/tools/one-click/oneClickTool.d.ts +13 -0
  62. package/build/tools/one-click/oneClickTool.js +195 -24
  63. package/build/tools/preflightMockCheckTool.d.ts +2 -0
  64. package/build/tools/preflightMockCheckTool.js +96 -0
  65. package/build/tools/queryProxyMocksTool.d.ts +70 -0
  66. package/build/tools/queryProxyMocksTool.js +522 -0
  67. package/build/tools/runExistingTestsTool.d.ts +31 -0
  68. package/build/tools/runExistingTestsTool.js +214 -13
  69. package/build/tools/submitReportTool.js +38 -3
  70. package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
  71. package/build/tools/test-management/analyzeChangesTool.js +63 -40
  72. package/build/tools/test-management/registerTestPlanTool.js +55 -7
  73. package/build/tools/trace/startTraceCollectionTool.js +3 -3
  74. package/build/types/FrontendIntegration.d.ts +3 -0
  75. package/build/types/FrontendIntegration.js +3 -0
  76. package/build/types/OneClickCommands.d.ts +1 -1
  77. package/build/types/Recommendation.d.ts +20 -0
  78. package/build/types/Recommendation.js +32 -6
  79. package/build/types/RepositoryAnalysis.d.ts +131 -14
  80. package/build/types/RepositoryAnalysis.js +16 -2
  81. package/build/types/ReuseOutcome.d.ts +63 -0
  82. package/build/types/ReuseOutcome.js +33 -0
  83. package/build/types/TestExecution.d.ts +1 -0
  84. package/build/types/TestTypes.d.ts +25 -7
  85. package/build/types/TestTypes.js +22 -6
  86. package/build/types/TestbotReport.d.ts +7 -0
  87. package/build/types/index.d.ts +2 -0
  88. package/build/types/index.js +1 -0
  89. package/build/utils/AnalysisStateManager.d.ts +26 -0
  90. package/build/utils/AnalysisStateManager.js +31 -1
  91. package/build/utils/analyze-openapi.js +18 -1
  92. package/build/utils/branchDiff.d.ts +17 -1
  93. package/build/utils/branchDiff.js +99 -14
  94. package/build/utils/featureFlags.d.ts +31 -0
  95. package/build/utils/featureFlags.js +37 -0
  96. package/build/utils/frontendIntegration.js +8 -1
  97. package/build/utils/grpcMockValidation.d.ts +1 -0
  98. package/build/utils/grpcMockValidation.js +49 -0
  99. package/build/utils/httpMethodValidation.d.ts +4 -0
  100. package/build/utils/httpMethodValidation.js +15 -0
  101. package/build/utils/logger.js +1 -1
  102. package/build/utils/mockCompatibility.d.ts +49 -0
  103. package/build/utils/mockCompatibility.js +82 -0
  104. package/build/utils/pom-scope/pom-files.js +7 -0
  105. package/build/utils/pom-verify/bindings.js +11 -1
  106. package/build/utils/pom-verify/re-exports.d.ts +10 -0
  107. package/build/utils/pom-verify/re-exports.js +61 -0
  108. package/build/utils/pom-verify/resolve.d.ts +4 -1
  109. package/build/utils/pom-verify/resolve.js +7 -4
  110. package/build/utils/pom-verify/verify.d.ts +5 -0
  111. package/build/utils/pom-verify/verify.js +25 -3
  112. package/build/utils/progress.js +10 -5
  113. package/build/utils/proxy-terminal.js +3 -3
  114. package/build/utils/routeParsers.d.ts +3 -9
  115. package/build/utils/routeParsers.js +79 -4
  116. package/build/utils/utils.js +2 -2
  117. package/build/utils/versions.d.ts +4 -3
  118. package/build/utils/versions.js +3 -1
  119. package/build/utils/workspaceAuth.d.ts +46 -0
  120. package/build/utils/workspaceAuth.js +156 -1
  121. package/build/workspace/testSuites.d.ts +2 -1
  122. package/build/workspace/testSuites.js +1 -1
  123. package/build/workspace/workspace.d.ts +108 -32
  124. package/build/workspace/workspace.js +32 -4
  125. package/package.json +5 -2
  126. package/build/adapters/jestAdapter.test.d.ts +0 -1
  127. package/build/adapters/jestAdapter.test.js +0 -93
  128. package/build/adapters/mochaAdapter.test.d.ts +0 -1
  129. package/build/adapters/mochaAdapter.test.js +0 -63
  130. package/build/adapters/playwrightAdapter.test.d.ts +0 -1
  131. package/build/adapters/playwrightAdapter.test.js +0 -169
  132. package/build/adapters/pytestAdapter.test.d.ts +0 -1
  133. package/build/adapters/pytestAdapter.test.js +0 -90
  134. package/build/prompts/code-reuse.test.d.ts +0 -1
  135. package/build/prompts/code-reuse.test.js +0 -62
  136. package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
  137. package/build/prompts/pom-aware-code-reuse.test.js +0 -11
  138. package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
  139. package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
  140. package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
  141. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
  142. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
  143. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
  144. package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
  145. package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
  146. package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
  147. package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
  148. package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
  149. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
  150. package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
  151. package/build/prompts/testbot/testbot-prompts.test.js +0 -451
  152. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
  153. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
  154. package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
  155. package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
  156. package/build/recommendation/discriminators.test.d.ts +0 -1
  157. package/build/recommendation/discriminators.test.js +0 -324
  158. package/build/recommendation/diversity.test.d.ts +0 -1
  159. package/build/recommendation/diversity.test.js +0 -77
  160. package/build/recommendation/planRanker.test.d.ts +0 -1
  161. package/build/recommendation/planRanker.test.js +0 -110
  162. package/build/resources/testbotResource.test.d.ts +0 -1
  163. package/build/resources/testbotResource.test.js +0 -44
  164. package/build/services/AnalyticsService.test.d.ts +0 -1
  165. package/build/services/AnalyticsService.test.js +0 -86
  166. package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
  167. package/build/services/ScenarioGenerationService.integration.test.js +0 -162
  168. package/build/services/ScenarioGenerationService.test.d.ts +0 -1
  169. package/build/services/ScenarioGenerationService.test.js +0 -414
  170. package/build/services/TestDiscoveryService.test.d.ts +0 -1
  171. package/build/services/TestDiscoveryService.test.js +0 -983
  172. package/build/services/TestExecutionService.test.d.ts +0 -1
  173. package/build/services/TestExecutionService.test.js +0 -1032
  174. package/build/services/TestGenerationService.test.d.ts +0 -1
  175. package/build/services/TestGenerationService.test.js +0 -578
  176. package/build/tool-phase-coverage.test.d.ts +0 -1
  177. package/build/tool-phase-coverage.test.js +0 -49
  178. package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
  179. package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
  180. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
  181. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
  182. package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
  183. package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
  184. package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
  185. package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
  186. package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
  187. package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
  188. package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
  189. package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
  190. package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
  191. package/build/tools/generate-tests/planGuard.test.js +0 -185
  192. package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
  193. package/build/tools/generate-tests/scenarioLint.test.js +0 -100
  194. package/build/tools/runExistingTestsTool.test.d.ts +0 -1
  195. package/build/tools/runExistingTestsTool.test.js +0 -379
  196. package/build/tools/submitReportTool.test.d.ts +0 -1
  197. package/build/tools/submitReportTool.test.js +0 -1428
  198. package/build/tools/test-management/actionsTool.test.d.ts +0 -1
  199. package/build/tools/test-management/actionsTool.test.js +0 -436
  200. package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
  201. package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
  202. package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
  203. package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
  204. package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
  205. package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
  206. package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
  207. package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
  208. package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
  209. package/build/tools/trace/resolveSessionPaths.test.js +0 -104
  210. package/build/tools/trace/sessionState.test.d.ts +0 -1
  211. package/build/tools/trace/sessionState.test.js +0 -17
  212. package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
  213. package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
  214. package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
  215. package/build/tools/workspace/serviceUpsert.test.js +0 -50
  216. package/build/utils/AnalysisStateManager.test.d.ts +0 -1
  217. package/build/utils/AnalysisStateManager.test.js +0 -134
  218. package/build/utils/dartRouteExtractor.test.d.ts +0 -1
  219. package/build/utils/dartRouteExtractor.test.js +0 -307
  220. package/build/utils/docker.test.d.ts +0 -1
  221. package/build/utils/docker.test.js +0 -114
  222. package/build/utils/featureFlags.test.d.ts +0 -1
  223. package/build/utils/featureFlags.test.js +0 -81
  224. package/build/utils/fileWalk.test.d.ts +0 -1
  225. package/build/utils/fileWalk.test.js +0 -252
  226. package/build/utils/frontendIntegration.test.d.ts +0 -1
  227. package/build/utils/frontendIntegration.test.js +0 -229
  228. package/build/utils/frontendSelectors.test.d.ts +0 -1
  229. package/build/utils/frontendSelectors.test.js +0 -118
  230. package/build/utils/gitStaging.test.d.ts +0 -1
  231. package/build/utils/gitStaging.test.js +0 -111
  232. package/build/utils/httpDefaults.test.d.ts +0 -1
  233. package/build/utils/httpDefaults.test.js +0 -21
  234. package/build/utils/importerHop.test.d.ts +0 -1
  235. package/build/utils/importerHop.test.js +0 -469
  236. package/build/utils/pathAffinityClassification.test.d.ts +0 -1
  237. package/build/utils/pathAffinityClassification.test.js +0 -208
  238. package/build/utils/planMatchKeys.test.d.ts +0 -1
  239. package/build/utils/planMatchKeys.test.js +0 -123
  240. package/build/utils/pom-scope/index.test.d.ts +0 -1
  241. package/build/utils/pom-scope/index.test.js +0 -278
  242. package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
  243. package/build/utils/pom-scope/pom-files.test.js +0 -29
  244. package/build/utils/pom-scope/scoring.test.d.ts +0 -1
  245. package/build/utils/pom-scope/scoring.test.js +0 -39
  246. package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
  247. package/build/utils/pom-scope/selector-extractor.test.js +0 -67
  248. package/build/utils/pom-verify/bindings.test.d.ts +0 -1
  249. package/build/utils/pom-verify/bindings.test.js +0 -164
  250. package/build/utils/pom-verify/calls.test.d.ts +0 -1
  251. package/build/utils/pom-verify/calls.test.js +0 -61
  252. package/build/utils/pom-verify/resolve.test.d.ts +0 -1
  253. package/build/utils/pom-verify/resolve.test.js +0 -114
  254. package/build/utils/pom-verify/verify.test.d.ts +0 -1
  255. package/build/utils/pom-verify/verify.test.js +0 -374
  256. package/build/utils/pr-comment-parser.test.d.ts +0 -1
  257. package/build/utils/pr-comment-parser.test.js +0 -428
  258. package/build/utils/progress.test.d.ts +0 -1
  259. package/build/utils/progress.test.js +0 -37
  260. package/build/utils/projectMetadata.test.d.ts +0 -1
  261. package/build/utils/projectMetadata.test.js +0 -172
  262. package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
  263. package/build/utils/pythonMountPrefixes.test.js +0 -113
  264. package/build/utils/repoScanner.test.d.ts +0 -1
  265. package/build/utils/repoScanner.test.js +0 -190
  266. package/build/utils/reportVerification.test.d.ts +0 -1
  267. package/build/utils/reportVerification.test.js +0 -185
  268. package/build/utils/routeParsers.test.d.ts +0 -1
  269. package/build/utils/routeParsers.test.js +0 -1011
  270. package/build/utils/scenarioDrafting.test.d.ts +0 -1
  271. package/build/utils/scenarioDrafting.test.js +0 -876
  272. package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
  273. package/build/utils/sourceRouteExtractor.test.js +0 -738
  274. package/build/utils/telemetry.test.d.ts +0 -1
  275. package/build/utils/telemetry.test.js +0 -70
  276. package/build/utils/trace-parser.test.d.ts +0 -6
  277. package/build/utils/trace-parser.test.js +0 -140
  278. package/build/utils/uiPageEnumerator.test.d.ts +0 -1
  279. package/build/utils/uiPageEnumerator.test.js +0 -824
  280. package/build/utils/utils.test.d.ts +0 -1
  281. package/build/utils/utils.test.js +0 -103
  282. package/build/utils/walkerCharacterization.test.d.ts +0 -1
  283. package/build/utils/walkerCharacterization.test.js +0 -233
  284. package/build/utils/workspaceAuth.test.d.ts +0 -1
  285. package/build/utils/workspaceAuth.test.js +0 -260
  286. package/build/workspace/testSuites.test.d.ts +0 -1
  287. package/build/workspace/testSuites.test.js +0 -23
  288. package/build/workspace/workspace.test.d.ts +0 -1
  289. 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 {};