@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,385 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest, describe, it, expect, beforeEach } from "@jest/globals";
3
- jest.unstable_mockModule("@modelcontextprotocol/sdk/server/mcp.js", () => ({
4
- McpServer: jest.fn(),
5
- }));
6
- jest.unstable_mockModule("@modelcontextprotocol/sdk/types.js", () => ({
7
- CallToolResult: {},
8
- }));
9
- jest.unstable_mockModule("../../utils/logger.js", () => ({
10
- logger: {
11
- info: jest.fn(),
12
- debug: jest.fn(),
13
- error: jest.fn(),
14
- warning: jest.fn(),
15
- },
16
- }));
17
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
18
- AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
19
- }));
20
- const mockBuildDriftAnalysisPrompt = jest.fn().mockImplementation((_stateFile, apiTests, ui) => {
21
- const parts = [];
22
- if ((apiTests?.length ?? 0) > 0 || !ui)
23
- parts.push("<drift_analysis_rules>API drift</drift_analysis_rules>");
24
- if (ui)
25
- parts.push("<ui_drift_analysis_rules>UI drift</ui_drift_analysis_rules>");
26
- return parts.join("\n\n");
27
- });
28
- jest.unstable_mockModule("../../prompts/test-maintenance/drift-analysis-prompt.js", () => ({
29
- buildDriftAnalysisPrompt: mockBuildDriftAnalysisPrompt,
30
- }));
31
- const mockFromStatePath = jest.fn();
32
- const mockCleanupOldFiles = jest.fn().mockImplementation(() => Promise.resolve());
33
- jest.unstable_mockModule("../../utils/AnalysisStateManager.js", () => ({
34
- StateManager: {
35
- fromStatePath: mockFromStatePath,
36
- cleanupOldFiles: mockCleanupOldFiles,
37
- },
38
- TestSource: { Skyramp: "skyramp", External: "external" },
39
- }));
40
- jest.unstable_mockModule("../../utils/utils.js", () => ({
41
- toolError: (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }] }),
42
- }));
43
- // Confirm-gate dep: the workspace read decides whether the repo has a runnable
44
- // external-test suite (a Playwright service with a runtimeDetails.testSuites entry).
45
- // Default to "no services" so tests that don't opt in keep today's behaviour
46
- // (gate never fires).
47
- const mockWsRead = jest.fn(() => Promise.resolve({ services: [] }));
48
- jest.unstable_mockModule("../../workspace/workspace.js", () => ({
49
- WorkspaceConfigManager: jest.fn().mockImplementation(() => ({ read: mockWsRead })),
50
- }));
51
- const { registerAnalyzeTestHealthTool } = await import("./analyzeTestHealthTool.js");
52
- // ---------------------------------------------------------------------------
53
- // Helpers
54
- // ---------------------------------------------------------------------------
55
- function makeFakeServer() {
56
- let capturedHandler = null;
57
- let capturedSchema = null;
58
- const server = {
59
- registerTool: jest.fn((_name, config, handler) => {
60
- capturedSchema = config.inputSchema;
61
- capturedHandler = handler;
62
- }),
63
- getHandler: () => capturedHandler,
64
- getSchema: () => capturedSchema,
65
- };
66
- return server;
67
- }
68
- function makeStateManager(stateData) {
69
- return {
70
- getStatePath: jest.fn().mockReturnValue("/tmp/skyramp-analysis-test.json"),
71
- readData: jest.fn().mockResolvedValue(stateData),
72
- readFullState: jest.fn().mockResolvedValue({ metadata: { repositoryPath: "/repo" } }),
73
- // Multi-repo API added in SKYR-3786 — analyzeTestHealthTool now uses these.
74
- readRepoData: jest.fn().mockResolvedValue(stateData),
75
- getRepoRepositoryPath: jest.fn().mockResolvedValue("/repo"),
76
- listRelatedRepos: jest.fn().mockResolvedValue([]),
77
- };
78
- }
79
- // ---------------------------------------------------------------------------
80
- // Tests
81
- // ---------------------------------------------------------------------------
82
- describe("analyzeTestHealthTool — blueprintCaptured schema", () => {
83
- it("registers blueprintCaptured as an optional boolean parameter", () => {
84
- const server = makeFakeServer();
85
- registerAnalyzeTestHealthTool(server);
86
- const schema = server.getSchema();
87
- expect(schema).toHaveProperty("blueprintCaptured");
88
- expect(schema.blueprintCaptured.safeParse(undefined).success).toBe(true);
89
- expect(schema.blueprintCaptured.safeParse(true).success).toBe(true);
90
- });
91
- });
92
- describe("analyzeTestHealthTool — UI drift gate logic", () => {
93
- let server;
94
- beforeEach(() => {
95
- jest.clearAllMocks();
96
- server = makeFakeServer();
97
- registerAnalyzeTestHealthTool(server);
98
- });
99
- it("includes UI drift when uiContext has changedFrontendFiles and a UI test", async () => {
100
- const mockStateManager = makeStateManager({
101
- existingTests: [
102
- { testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" },
103
- ],
104
- uiContext: {
105
- changedFrontendFiles: ["/repo/src/components/Cart.tsx"],
106
- candidateUiPages: [],
107
- },
108
- });
109
- mockFromStatePath.mockReturnValue(mockStateManager);
110
- const result = await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
111
- expect(result).not.toHaveProperty("isError", true);
112
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeDefined();
113
- const testsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][2].tests;
114
- expect(testsArg).toContainEqual(expect.objectContaining({ testFile: "/repo/tests/cart.spec.ts" }));
115
- });
116
- it("passes blueprintCaptured=true to buildUiDriftAnalysisPrompt", async () => {
117
- const mockStateManager = makeStateManager({
118
- existingTests: [{ testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" }],
119
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
120
- });
121
- mockFromStatePath.mockReturnValue(mockStateManager);
122
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
123
- const blueprintArg = mockBuildDriftAnalysisPrompt.mock.calls[0][2].blueprintCaptured;
124
- expect(blueprintArg).toBe(true);
125
- });
126
- it("passes blueprintCaptured=false to buildUiDriftAnalysisPrompt", async () => {
127
- const mockStateManager = makeStateManager({
128
- existingTests: [{ testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" }],
129
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
130
- });
131
- mockFromStatePath.mockReturnValue(mockStateManager);
132
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: false });
133
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeDefined();
134
- const blueprintArg = mockBuildDriftAnalysisPrompt.mock.calls[0][2].blueprintCaptured;
135
- expect(blueprintArg).toBe(false);
136
- });
137
- it("excludes UI drift when uiContext is absent", async () => {
138
- const mockStateManager = makeStateManager({ existingTests: [] });
139
- mockFromStatePath.mockReturnValue(mockStateManager);
140
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
141
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeUndefined();
142
- });
143
- it("excludes UI drift when changedFrontendFiles is empty", async () => {
144
- const mockStateManager = makeStateManager({
145
- existingTests: [{ testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui" }],
146
- uiContext: { changedFrontendFiles: [], candidateUiPages: [] },
147
- });
148
- mockFromStatePath.mockReturnValue(mockStateManager);
149
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
150
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeUndefined();
151
- });
152
- it("includes UI drift (empty test list) when frontend changed but no UI tests found — grep pre-scan will discover them", async () => {
153
- const mockStateManager = makeStateManager({
154
- existingTests: [
155
- { testFile: "/repo/tests/products_contract_test.py", source: "skyramp", testType: "contract", framework: "pytest" },
156
- { testFile: "/repo/tests/orders_smoke_test.py", source: "skyramp", testType: "smoke", framework: "pytest" },
157
- ],
158
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
159
- });
160
- mockFromStatePath.mockReturnValue(mockStateManager);
161
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
162
- // UI drift fires even with 0 UI tests — grep pre-scan discovers component tests dynamically
163
- const uiParam = mockBuildDriftAnalysisPrompt.mock.calls[0][2];
164
- expect(uiParam).toBeDefined();
165
- expect(uiParam.tests).toHaveLength(0);
166
- expect(uiParam.changedFrontendFiles).toEqual(["/repo/src/Cart.tsx"]);
167
- });
168
- it("includes UI drift for RTL tests (framework=rtl, testType=unknown)", async () => {
169
- const mockStateManager = makeStateManager({
170
- existingTests: [
171
- { testFile: "/repo/src/components/Cart.test.tsx", source: "external", testType: "unknown", framework: "rtl" },
172
- { testFile: "/repo/tests/orders_smoke_test.py", source: "skyramp", testType: "smoke", framework: "pytest" },
173
- ],
174
- repositoryAnalysis: { relevantExternalTestPaths: ["src/components/Cart.test.tsx"] },
175
- uiContext: { changedFrontendFiles: ["/repo/src/components/Cart.tsx"], candidateUiPages: [] },
176
- });
177
- mockFromStatePath.mockReturnValue(mockStateManager);
178
- mockFromStatePath.mock.results[0]?.value?.readFullState?.mockResolvedValue({
179
- metadata: { repositoryPath: "/repo" },
180
- });
181
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: false });
182
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeDefined();
183
- const testsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][2].tests;
184
- expect(testsArg).toHaveLength(1);
185
- expect(testsArg[0]).toMatchObject({ testFile: "/repo/src/components/Cart.test.tsx" });
186
- });
187
- it("excludes RTL test from API drift prompt when UI drift is running", async () => {
188
- const mockStateManager = makeStateManager({
189
- existingTests: [
190
- { testFile: "/repo/src/components/Cart.test.tsx", source: "external", testType: "unknown", framework: "rtl" },
191
- { testFile: "/repo/tests/orders_smoke_test.py", source: "skyramp", testType: "smoke", framework: "pytest" },
192
- ],
193
- repositoryAnalysis: { relevantExternalTestPaths: ["src/components/Cart.test.tsx"] },
194
- uiContext: { changedFrontendFiles: ["/repo/src/components/Cart.tsx"], candidateUiPages: [] },
195
- });
196
- mockFromStatePath.mockReturnValue(mockStateManager);
197
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: false });
198
- const apiTestsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][1];
199
- expect(apiTestsArg).toHaveLength(1);
200
- expect(apiTestsArg[0]).toMatchObject({ testFile: "/repo/tests/orders_smoke_test.py" });
201
- });
202
- it("passes only UI tests to buildUiDriftAnalysisPrompt when mixed test types exist", async () => {
203
- const mockStateManager = makeStateManager({
204
- existingTests: [
205
- { testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" },
206
- { testFile: "/repo/tests/products_contract.py", source: "skyramp", testType: "contract", framework: "pytest" },
207
- { testFile: "/repo/tests/smoke.py", source: "skyramp", testType: "smoke", framework: "pytest" },
208
- ],
209
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
210
- });
211
- mockFromStatePath.mockReturnValue(mockStateManager);
212
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
213
- expect(mockBuildDriftAnalysisPrompt.mock.calls[0][2]).toBeDefined();
214
- const testsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][2].tests;
215
- expect(testsArg).toHaveLength(1);
216
- expect(testsArg[0]).toMatchObject({ testFile: "/repo/tests/cart.spec.ts" });
217
- });
218
- it("excludes UI tests from API drift prompt when UI drift is also running", async () => {
219
- const mockStateManager = makeStateManager({
220
- existingTests: [
221
- { testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" },
222
- { testFile: "/repo/tests/products_contract.py", source: "skyramp", testType: "contract", framework: "pytest" },
223
- ],
224
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
225
- });
226
- mockFromStatePath.mockReturnValue(mockStateManager);
227
- await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
228
- const apiTestsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][1];
229
- expect(apiTestsArg).toHaveLength(1);
230
- expect(apiTestsArg[0]).toMatchObject({ testFile: "/repo/tests/products_contract.py" });
231
- });
232
- it("includes UI tests in API drift prompt when no frontend changes", async () => {
233
- const mockStateManager = makeStateManager({
234
- existingTests: [
235
- { testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" },
236
- { testFile: "/repo/tests/products_contract.py", source: "skyramp", testType: "contract", framework: "pytest" },
237
- ],
238
- });
239
- mockFromStatePath.mockReturnValue(mockStateManager);
240
- await server.getHandler()({ stateFile: "/tmp/state.json" });
241
- // No frontend changes → no UI drift section → UI tests fall through to API drift
242
- // so they are not silently dropped on API-only PRs.
243
- const apiTestsArg = mockBuildDriftAnalysisPrompt.mock.calls[0][1];
244
- expect(apiTestsArg).toHaveLength(2);
245
- expect(apiTestsArg).toContainEqual(expect.objectContaining({ testFile: "/repo/tests/cart.spec.ts" }));
246
- expect(apiTestsArg).toContainEqual(expect.objectContaining({ testFile: "/repo/tests/products_contract.py" }));
247
- });
248
- });
249
- describe("analyzeTestHealthTool — prompt concatenation", () => {
250
- let server;
251
- beforeEach(() => {
252
- jest.clearAllMocks();
253
- server = makeFakeServer();
254
- registerAnalyzeTestHealthTool(server);
255
- });
256
- it("concatenates API and UI drift prompts when both are included", async () => {
257
- const mockStateManager = makeStateManager({
258
- existingTests: [
259
- { testFile: "/repo/tests/cart.spec.ts", source: "skyramp", testType: "ui", framework: "playwright-ui" },
260
- { testFile: "/repo/tests/contract.py", source: "skyramp", testType: "contract", framework: "pytest" },
261
- ],
262
- uiContext: { changedFrontendFiles: ["/repo/src/Cart.tsx"], candidateUiPages: [] },
263
- });
264
- mockFromStatePath.mockReturnValue(mockStateManager);
265
- const result = await server.getHandler()({ stateFile: "/tmp/state.json", blueprintCaptured: true });
266
- expect(result.structuredContent?.prompt).toContain("API drift");
267
- expect(result.structuredContent?.prompt).toContain("UI drift");
268
- });
269
- it("returns only API drift prompt when uiContext is absent", async () => {
270
- const mockStateManager = makeStateManager({ existingTests: [] });
271
- mockFromStatePath.mockReturnValue(mockStateManager);
272
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
273
- expect(result.structuredContent?.prompt).toContain("API drift");
274
- expect(result.structuredContent?.prompt).not.toContain("UI drift");
275
- });
276
- });
277
- describe("analyzeTestHealthTool — external-test confirm gate", () => {
278
- let server;
279
- beforeEach(() => {
280
- jest.clearAllMocks();
281
- server = makeFakeServer();
282
- registerAnalyzeTestHealthTool(server);
283
- });
284
- const externalTestState = (extra = {}) => ({
285
- existingTests: [
286
- { testFile: "/repo/e2e/tests/publish-flow.test.ts", source: "external", testType: "e2e", framework: "playwright" },
287
- { testFile: "/repo/tests/orders_contract.py", source: "skyramp", testType: "contract", framework: "pytest" },
288
- ],
289
- ...extra,
290
- });
291
- const pwService = (name) => ({
292
- serviceName: name,
293
- framework: "playwright",
294
- runtimeDetails: { testSuites: [{ testRunCommand: "bash ./e2e/run.sh" }] },
295
- });
296
- it("gates until a confirm run is recorded when external tests + runnable test-env exist", async () => {
297
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState()));
298
- mockWsRead.mockResolvedValue({ services: [pwService("ghost")] });
299
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
300
- // Refuses to produce the drift prompt; instructs the confirm run instead.
301
- expect(mockBuildDriftAnalysisPrompt).not.toHaveBeenCalled();
302
- expect(result.structuredContent).toBeUndefined();
303
- expect(result.content[0].text).toContain("skyramp_run_existing_tests");
304
- expect(result.content[0].text).toContain('mode: "confirm"');
305
- expect(result.content[0].text).toContain("/repo/e2e/tests/publish-flow.test.ts");
306
- });
307
- it("gates when MULTIPLE Playwright services are configured (regression: resolveRunConfig throw no longer fails-open)", async () => {
308
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState()));
309
- // Two Playwright services with test* — e.g. Ghost's ghost + ghost-content.
310
- // The old resolveRunConfig-based check threw here and silently skipped the gate.
311
- mockWsRead.mockResolvedValue({ services: [pwService("ghost"), pwService("ghost-content")] });
312
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
313
- expect(mockBuildDriftAnalysisPrompt).not.toHaveBeenCalled();
314
- expect(result.content[0].text).toContain("skyramp_run_existing_tests");
315
- // Instruction disambiguates the service for the multi-service case.
316
- expect(result.content[0].text).toContain('service: "ghost"');
317
- });
318
- it("does not gate once a confirm run has been recorded (loop-safe)", async () => {
319
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState({ externalTestResults: [{ mode: "confirm", ranAt: "t", results: [] }] })));
320
- mockWsRead.mockResolvedValue({ services: [pwService("ghost")] });
321
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
322
- expect(mockBuildDriftAnalysisPrompt).toHaveBeenCalled();
323
- expect(result.structuredContent?.prompt).toContain("API drift");
324
- });
325
- it("still gates when only a verify run is recorded (verify must not clear the confirm gate)", async () => {
326
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState({ externalTestResults: [{ mode: "verify", ranAt: "t", results: [] }] })));
327
- mockWsRead.mockResolvedValue({ services: [pwService("ghost")] });
328
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
329
- expect(mockBuildDriftAnalysisPrompt).not.toHaveBeenCalled();
330
- expect(result.content[0].text).toContain('mode: "confirm"');
331
- });
332
- it("does not gate when the repo has no runnable test-env config (Playwright service without testRunCommand)", async () => {
333
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState()));
334
- mockWsRead.mockResolvedValue({ services: [{ serviceName: "ghost", framework: "playwright" }] });
335
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
336
- expect(mockBuildDriftAnalysisPrompt).toHaveBeenCalled();
337
- expect(result.structuredContent?.prompt).toContain("API drift");
338
- });
339
- it("does not gate when there are no external tests relevant to the diff", async () => {
340
- mockFromStatePath.mockReturnValue(makeStateManager({
341
- existingTests: [{ testFile: "/repo/tests/orders_contract.py", source: "skyramp", testType: "contract", framework: "pytest" }],
342
- }));
343
- mockWsRead.mockResolvedValue({ services: [pwService("ghost")] });
344
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
345
- expect(mockBuildDriftAnalysisPrompt).toHaveBeenCalled();
346
- expect(result.structuredContent?.prompt).toContain("API drift");
347
- });
348
- it("fails open (produces drift prompt) when the workspace config cannot be read", async () => {
349
- mockFromStatePath.mockReturnValue(makeStateManager(externalTestState()));
350
- mockWsRead.mockRejectedValue(new Error("workspace.yml missing"));
351
- const result = await server.getHandler()({ stateFile: "/tmp/state.json" });
352
- expect(mockBuildDriftAnalysisPrompt).toHaveBeenCalled();
353
- expect(result.structuredContent?.prompt).toContain("API drift");
354
- });
355
- });
356
- describe("runnableTestEnvServices — multi-suite recognition (SKYR-4002)", () => {
357
- // NOTE: this file already mocks the whole `workspace.js` module (see
358
- // jest.unstable_mockModule above) with a factory that returns a plain object
359
- // literal `{ read: mockWsRead }` rather than a `new`-constructed instance, so
360
- // `WorkspaceConfigManager.prototype.read` is never consulted by the code under
361
- // test. jest.spyOn(WorkspaceConfigManager.prototype, "read") was attempted and
362
- // confirmed to be a no-op here (spied prototype method never invoked; the real
363
- // module-level mockWsRead default of `{ services: [] }` won instead). Using the
364
- // file's own established mock control point (mockWsRead) for parity with the 6
365
- // sibling gate tests below — same assertion, different injection point.
366
- it("recognizes a Playwright suite declared under testSuites, AND a reader-backed jest suite (SKYR-3990/3991: gate widened beyond Playwright-only)", async () => {
367
- const { runnableTestEnvServices } = await import("./analyzeTestHealthTool.js");
368
- mockWsRead.mockResolvedValue({
369
- services: [
370
- { serviceName: "ui", framework: "playwright", runtimeDetails: { runtime: "local", testSuites: [{ framework: "playwright", testRunCommand: "pw" }] } },
371
- { serviceName: "api", framework: "jest", runtimeDetails: { runtime: "local", testSuites: [{ testRunCommand: "jest" }] } },
372
- ],
373
- });
374
- await expect(runnableTestEnvServices("/x")).resolves.toEqual(["ui", "api"]);
375
- });
376
- it("excludes a framework with no reader (e.g. go) even with a testRunCommand", async () => {
377
- const { runnableTestEnvServices } = await import("./analyzeTestHealthTool.js");
378
- mockWsRead.mockResolvedValue({
379
- services: [
380
- { serviceName: "svc", framework: "go", runtimeDetails: { runtime: "local", testSuites: [{ testRunCommand: "go test ./..." }] } },
381
- ],
382
- });
383
- await expect(runnableTestEnvServices("/x")).resolves.toEqual([]);
384
- });
385
- });