@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,433 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest } from "@jest/globals";
3
- import fs from "fs";
4
- jest.unstable_mockModule("../../services/ScenarioGenerationService.js", () => ({
5
- ScenarioGenerationService: jest.fn().mockImplementation(() => ({
6
- generateTraceRequestFromInput: jest.fn().mockReturnValue(null),
7
- })),
8
- }));
9
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({ AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) } }));
10
- jest.unstable_mockModule("../../utils/workspaceAuth.js", () => ({
11
- getWorkspaceAuthConfig: jest.fn().mockResolvedValue({ authHeader: undefined, authType: "none" }),
12
- WorkspaceAuthType: { None: "none" },
13
- getDefaultAuthHeader: jest.fn().mockReturnValue(""),
14
- isAuthorizationHeaderName: jest.fn().mockReturnValue(false),
15
- getAuthScheme: jest.fn().mockReturnValue(""),
16
- readWorkspaceConfigRaw: jest.fn().mockResolvedValue(null),
17
- }));
18
- jest.unstable_mockModule("../../utils/logger.js", () => ({ logger: { info: jest.fn(), warning: jest.fn(), error: jest.fn() } }));
19
- jest.unstable_mockModule("../../prompts/personas.js", () => ({ getPersonaPrefix: () => "" }));
20
- const { stepSchema, registerBatchScenarioTestTool } = await import("./generateBatchScenarioRestTool.js");
21
- const { ScenarioGenerationService } = await import("../../services/ScenarioGenerationService.js");
22
- /** Build a minimal mock McpServer, register the tool, and return the captured handler. */
23
- function captureHandler() {
24
- let capturedHandler;
25
- const mockServer = {
26
- registerTool: jest.fn((_name, _meta, handler) => {
27
- capturedHandler = handler;
28
- }),
29
- };
30
- registerBatchScenarioTestTool(mockServer);
31
- return capturedHandler;
32
- }
33
- // ─────────────────────────────────────────────────────────────────────────────
34
- // stepSchema — requestBody validation
35
- // ─────────────────────────────────────────────────────────────────────────────
36
- describe("stepSchema — requestBody validation", () => {
37
- it("accepts a POST step with a non-empty requestBody", () => {
38
- const result = stepSchema.safeParse({
39
- method: "POST",
40
- path: "/api/v1/products",
41
- requestBody: JSON.stringify({ name: "Widget-123", price: 9.99 }),
42
- });
43
- expect(result.success).toBe(true);
44
- });
45
- it("rejects a POST step with an empty requestBody {}", () => {
46
- const result = stepSchema.safeParse({
47
- method: "POST",
48
- path: "/api/v1/products",
49
- requestBody: "{}",
50
- });
51
- expect(result.success).toBe(false);
52
- const issue = result.error?.issues.find(i => i.path.includes("requestBody"));
53
- expect(issue?.message).toContain("empty object {}");
54
- expect(issue?.message).toContain("POST");
55
- });
56
- it("rejects a PATCH step with an empty requestBody {}", () => {
57
- const result = stepSchema.safeParse({
58
- method: "PATCH",
59
- path: "/api/v1/orders/1",
60
- requestBody: "{}",
61
- });
62
- expect(result.success).toBe(false);
63
- expect(result.error?.issues.some(i => i.message.includes("empty object {}"))).toBe(true);
64
- });
65
- it("rejects a PUT step with an empty requestBody {}", () => {
66
- const result = stepSchema.safeParse({
67
- method: "PUT",
68
- path: "/api/v1/products/1",
69
- requestBody: "{}",
70
- });
71
- expect(result.success).toBe(false);
72
- const issue = result.error?.issues.find(i => i.path.includes("requestBody"));
73
- expect(issue?.message).toContain("empty object {}");
74
- expect(issue?.message).toContain("PUT");
75
- });
76
- it("accepts a POST step with requestBody omitted entirely", () => {
77
- const result = stepSchema.safeParse({
78
- method: "POST",
79
- path: "/api/v1/products",
80
- });
81
- expect(result.success).toBe(true);
82
- });
83
- it("does not throw on requestBody: 'null' (valid JSON null)", () => {
84
- const result = stepSchema.safeParse({
85
- method: "POST",
86
- path: "/api/v1/products",
87
- requestBody: "null",
88
- });
89
- expect(result.success).toBe(true);
90
- });
91
- it("accepts a GET step with no requestBody", () => {
92
- const result = stepSchema.safeParse({
93
- method: "GET",
94
- path: "/api/v1/products/1",
95
- });
96
- expect(result.success).toBe(true);
97
- });
98
- it("accepts a DELETE step with no requestBody", () => {
99
- const result = stepSchema.safeParse({
100
- method: "DELETE",
101
- path: "/api/v1/products/1",
102
- });
103
- expect(result.success).toBe(true);
104
- });
105
- it("does not reject a GET step that happens to have an empty requestBody", () => {
106
- const result = stepSchema.safeParse({
107
- method: "GET",
108
- path: "/api/v1/products",
109
- requestBody: "{}",
110
- });
111
- expect(result.success).toBe(true);
112
- });
113
- });
114
- // ─────────────────────────────────────────────────────────────────────────────
115
- // GraphQL step rejection (Change 10b)
116
- // ─────────────────────────────────────────────────────────────────────────────
117
- describe("generateBatchScenarioRestTool — GraphQL step rejection (Change 10b)", () => {
118
- let handler;
119
- const baseParams = {
120
- scenarioName: "test_scenario",
121
- destination: "localhost",
122
- outputDir: "/tmp/skyramp-test",
123
- };
124
- beforeEach(() => {
125
- jest.clearAllMocks();
126
- handler = captureHandler();
127
- });
128
- it("rejects a step list containing /graphql", async () => {
129
- const result = await handler({
130
- ...baseParams,
131
- steps: [{ method: "POST", path: "/graphql", requestBody: '{"query":"{ users { id } }"}' }],
132
- });
133
- expect(result.isError).toBe(true);
134
- expect(result.content[0].text).toContain("GraphQL endpoints are not supported");
135
- expect(result.content[0].text).toContain("POST /graphql");
136
- });
137
- it("rejects a step list containing /api/graphql (nested segment)", async () => {
138
- const result = await handler({
139
- ...baseParams,
140
- steps: [
141
- { method: "GET", path: "/api/v1/users" },
142
- { method: "POST", path: "/api/graphql", requestBody: '{"query":"{ id }"}' },
143
- ],
144
- });
145
- expect(result.isError).toBe(true);
146
- expect(result.content[0].text).toContain("GraphQL endpoints are not supported");
147
- expect(result.content[0].text).toContain("POST /api/graphql");
148
- });
149
- it("does not reject a path whose segment is 'graphql-config' (not an exact segment match)", async () => {
150
- // 'graphql-config' !== 'graphql' — the guard uses exact segment comparison.
151
- // The GraphQL rejection message must never appear, regardless of whether the
152
- // handler succeeds or fails for some other reason.
153
- const result = await handler({
154
- ...baseParams,
155
- steps: [{ method: "GET", path: "/api/v1/graphql-config" }],
156
- });
157
- expect(result.content[0].text).not.toContain("GraphQL endpoints are not supported");
158
- });
159
- it("does not throw on a non-string path (typeof guard)", async () => {
160
- // typeof guard returns false for non-string — no crash from .replace()
161
- const result = await handler({
162
- ...baseParams,
163
- steps: [{ method: "GET", path: undefined }],
164
- });
165
- expect(result).toBeDefined();
166
- if (result.isError) {
167
- expect(result.content[0].text).not.toContain("Cannot read");
168
- }
169
- });
170
- });
171
- // ─────────────────────────────────────────────────────────────────────────────
172
- // Spec path validation (Change 3)
173
- // ─────────────────────────────────────────────────────────────────────────────
174
- describe("generateBatchScenarioRestTool — spec path validation (Change 3)", () => {
175
- let handler;
176
- let readFileSyncSpy;
177
- const fakeSpec = JSON.stringify({
178
- openapi: "3.0.0",
179
- paths: {
180
- "/api/v1/users": {},
181
- "/api/v1/users/{id}": {},
182
- },
183
- });
184
- const baseParams = {
185
- scenarioName: "test_scenario",
186
- destination: "localhost",
187
- outputDir: "/tmp/skyramp-test",
188
- apiSchema: "/tmp/openapi.json",
189
- };
190
- let writeFileSyncSpy;
191
- beforeEach(() => {
192
- jest.clearAllMocks();
193
- handler = captureHandler();
194
- readFileSyncSpy = jest.spyOn(fs, "readFileSync").mockReturnValue(fakeSpec);
195
- writeFileSyncSpy = jest.spyOn(fs, "writeFileSync").mockImplementation(() => { });
196
- // Return a valid trace so tests reach the success path (where the warning is appended)
197
- ScenarioGenerationService.mockImplementation(() => ({
198
- generateTraceRequestFromInput: jest.fn().mockReturnValue({ method: "GET", path: "/mock" }),
199
- }));
200
- });
201
- afterEach(() => {
202
- readFileSyncSpy.mockRestore();
203
- writeFileSyncSpy.mockRestore();
204
- });
205
- it("warns (does not hard-reject) when step paths are not in the OpenAPI spec", async () => {
206
- // Spec may lag code — missing path != phantom path, so we warn and proceed.
207
- const result = await handler({
208
- ...baseParams,
209
- steps: [{ method: "GET", path: "/api/v1/phantom-endpoint" }],
210
- });
211
- expect(result.isError).toBeFalsy();
212
- expect(result.content[0].text).toContain("Spec warning");
213
- expect(result.content[0].text).toContain("/api/v1/phantom-endpoint");
214
- expect(result.content[0].text).toContain("Known spec paths");
215
- });
216
- it("includes no spec warning when step path is present in the OpenAPI spec", async () => {
217
- const result = await handler({
218
- ...baseParams,
219
- steps: [{ method: "GET", path: "/api/v1/users" }],
220
- });
221
- expect(result.content[0].text).not.toContain("Spec warning");
222
- });
223
- it("does not warn for Express-style :param paths that normalise to a spec path", async () => {
224
- const result = await handler({
225
- ...baseParams,
226
- steps: [{ method: "GET", path: "/api/v1/users/:id" }],
227
- });
228
- expect(result.content[0].text).not.toContain("Spec warning");
229
- });
230
- it("does not throw on a non-string path (typeof guard)", async () => {
231
- const result = await handler({
232
- ...baseParams,
233
- steps: [{ method: "GET", path: undefined }],
234
- });
235
- expect(result).toBeDefined();
236
- expect(result.content[0].text).not.toContain("TypeError");
237
- expect(result.content[0].text).not.toContain("Cannot read");
238
- });
239
- it("includes known spec paths hint in the warning message", async () => {
240
- const result = await handler({
241
- ...baseParams,
242
- steps: [{ method: "POST", path: "/does/not/exist" }],
243
- });
244
- expect(result.isError).toBeFalsy();
245
- expect(result.content[0].text).toContain("/api/v1/users");
246
- });
247
- });
248
- describe("generateBatchScenarioRestTool — spec URL fetch safety (comment 3203006665)", () => {
249
- let handler;
250
- const baseParams = {
251
- scenarioName: "test_scenario",
252
- destination: "localhost",
253
- outputDir: "/tmp/skyramp-test",
254
- apiSchema: "https://example.com/openapi.json",
255
- steps: [{ method: "GET", path: "/api/v1/users" }],
256
- };
257
- beforeEach(() => {
258
- jest.clearAllMocks();
259
- handler = captureHandler();
260
- });
261
- it("skips spec check (no warning emitted) when URL returns non-2xx", async () => {
262
- global.fetch = jest.fn().mockResolvedValue({
263
- ok: false,
264
- status: 404,
265
- statusText: "Not Found",
266
- text: async () => "<html>Not Found</html>",
267
- });
268
- const result = await handler({ ...baseParams });
269
- // Non-2xx throws → caught → spec check skipped entirely, no warning in output
270
- expect(result.content[0].text).not.toContain("Spec warning");
271
- });
272
- it("skips spec check (no warning emitted) when spec has no paths", async () => {
273
- global.fetch = jest.fn().mockResolvedValue({
274
- ok: true,
275
- status: 200,
276
- statusText: "OK",
277
- text: async () => JSON.stringify({ openapi: "3.0.0", info: { title: "Test", version: "1.0" } }),
278
- });
279
- const result = await handler({ ...baseParams });
280
- expect(result.content[0].text).not.toContain("Spec warning");
281
- });
282
- afterEach(() => {
283
- // Restore fetch
284
- delete global.fetch;
285
- });
286
- });
287
- describe("stepSchema uniqueFields validation (SKYR-3882)", () => {
288
- const base = { method: "POST", path: "/api/v1/providers/", requestBody: '{"name":"x"}' };
289
- it("accepts uniqueFields on a POST with a body", () => {
290
- expect(stepSchema.safeParse({ ...base, uniqueFields: ["name"] }).success).toBe(true);
291
- });
292
- it("rejects an empty uniqueFields array", () => {
293
- expect(stepSchema.safeParse({ ...base, uniqueFields: [] }).success).toBe(false);
294
- });
295
- it("rejects empty-string paths in uniqueFields", () => {
296
- expect(stepSchema.safeParse({ ...base, uniqueFields: [""] }).success).toBe(false);
297
- });
298
- it("rejects uniqueFields on a non-body method (GET)", () => {
299
- expect(stepSchema.safeParse({ method: "GET", path: "/x", uniqueFields: ["name"] }).success).toBe(false);
300
- });
301
- it("rejects uniqueFields on a POST with no requestBody", () => {
302
- expect(stepSchema.safeParse({ method: "POST", path: "/api/v1/providers/", uniqueFields: ["name"] }).success).toBe(false);
303
- });
304
- });
305
- // ─────────────────────────────────────────────────────────────────────────────
306
- // SKYR-3884 write-side validations
307
- // ─────────────────────────────────────────────────────────────────────────────
308
- describe("stepSchema — responseBody JSON validation (SKYR-3884)", () => {
309
- it("rejects a responseBody that is not valid JSON", () => {
310
- const result = stepSchema.safeParse({
311
- method: "POST",
312
- path: "/api/v1/products",
313
- requestBody: JSON.stringify({ name: "W" }),
314
- responseBody: "{id: 1, oops",
315
- });
316
- expect(result.success).toBe(false);
317
- const issue = result.error?.issues.find((i) => i.path.includes("responseBody"));
318
- expect(issue?.message).toContain("valid JSON");
319
- });
320
- it("accepts a valid JSON responseBody and an omitted one", () => {
321
- expect(stepSchema.safeParse({
322
- method: "POST",
323
- path: "/api/v1/products",
324
- requestBody: JSON.stringify({ name: "W" }),
325
- responseBody: JSON.stringify({ id: 1 }),
326
- }).success).toBe(true);
327
- expect(stepSchema.safeParse({ method: "GET", path: "/api/v1/products" }).success).toBe(true);
328
- });
329
- });
330
- describe("stepSchema — uniqueFields paths must resolve in requestBody (SKYR-3884)", () => {
331
- it("rejects a uniqueFields path that does not resolve in the requestBody", () => {
332
- const result = stepSchema.safeParse({
333
- method: "POST",
334
- path: "/api/v1/products",
335
- requestBody: JSON.stringify({ name: "W", slug: "w" }),
336
- uniqueFields: ["nmae"],
337
- });
338
- expect(result.success).toBe(false);
339
- const issue = result.error?.issues.find((i) => i.path.includes("uniqueFields"));
340
- expect(issue?.message).toContain("nmae");
341
- });
342
- it("accepts top-level, nested, and array-indexed uniqueFields paths that resolve", () => {
343
- const result = stepSchema.safeParse({
344
- method: "POST",
345
- path: "/api/v1/products",
346
- requestBody: JSON.stringify({ name: "W", nested: { slug: "w" }, items: [{ sku: "S1" }] }),
347
- uniqueFields: ["name", "nested.slug", "items.0.sku"],
348
- });
349
- expect(result.success).toBe(true);
350
- });
351
- });
352
- describe("generateBatchScenarioRestTool — chaining lint (SKYR-3884)", () => {
353
- let handler;
354
- let writeFileSyncSpy;
355
- const baseParams = {
356
- scenarioName: "test_scenario",
357
- destination: "localhost",
358
- outputDir: "/tmp/skyramp-test",
359
- };
360
- beforeEach(() => {
361
- jest.clearAllMocks();
362
- handler = captureHandler();
363
- writeFileSyncSpy = jest.spyOn(fs, "writeFileSync").mockImplementation(() => { });
364
- // The lint inspects the BUILT TraceRequests (the artifact the Go reader
365
- // consumes), so the mock must pass the step through in TraceRequest shape.
366
- ScenarioGenerationService.mockImplementation(() => ({
367
- generateTraceRequestFromInput: jest.fn((p) => ({
368
- Method: p.method,
369
- Path: p.path,
370
- RequestBody: p.requestBody ?? "",
371
- ResponseBody: p.responseBody ?? "",
372
- })),
373
- }));
374
- });
375
- afterEach(() => {
376
- writeFileSyncSpy.mockRestore();
377
- });
378
- it("hard-rejects a scenario whose FK reference is produced only by a later step", async () => {
379
- const result = await handler({
380
- ...baseParams,
381
- steps: [
382
- {
383
- method: "POST",
384
- path: "/api/circuits/provider-accounts/",
385
- requestBody: JSON.stringify({ provider: 1, name: "A" }),
386
- responseBody: JSON.stringify({ id: 7 }),
387
- },
388
- {
389
- method: "POST",
390
- path: "/api/circuits/providers/",
391
- requestBody: JSON.stringify({ name: "P" }),
392
- responseBody: JSON.stringify({ id: 1 }),
393
- },
394
- ],
395
- });
396
- expect(result.isError).toBe(true);
397
- expect(result.content[0].text).toContain("later step");
398
- expect(writeFileSyncSpy).not.toHaveBeenCalled();
399
- });
400
- it("appends a chaining warning (does not reject) for unchainable path values", async () => {
401
- const result = await handler({
402
- ...baseParams,
403
- steps: [
404
- {
405
- method: "POST",
406
- path: "/api/circuits/providers/",
407
- requestBody: JSON.stringify({ name: "P" }),
408
- responseBody: JSON.stringify({ id: 1 }),
409
- },
410
- { method: "GET", path: "/api/circuits/providers/999/summary/" },
411
- ],
412
- });
413
- expect(result.isError).toBeFalsy();
414
- expect(result.content[0].text).toContain("Chaining warning");
415
- expect(result.content[0].text).toContain("999");
416
- });
417
- it("emits no chaining output for a well-chained scenario", async () => {
418
- const result = await handler({
419
- ...baseParams,
420
- steps: [
421
- {
422
- method: "POST",
423
- path: "/api/circuits/providers/",
424
- requestBody: JSON.stringify({ name: "P" }),
425
- responseBody: JSON.stringify({ id: 1 }),
426
- },
427
- { method: "GET", path: "/api/circuits/providers/1/summary/" },
428
- ],
429
- });
430
- expect(result.isError).toBeFalsy();
431
- expect(result.content[0].text).not.toContain("Chaining warning");
432
- });
433
- });
@@ -1,142 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest } from "@jest/globals";
3
- import * as fs from "fs/promises";
4
- import * as path from "path";
5
- import * as os from "os";
6
- import { z } from "zod";
7
- // TestGenerationService transitively loads the @skyramp/skyramp native library
8
- // (blocks 180s waiting for the platform dylib) — stub the base class out.
9
- jest.unstable_mockModule("../../services/TestGenerationService.js", () => ({
10
- TestGenerationService: class {
11
- async generateTest() {
12
- return { content: [{ type: "text", text: "generated" }] };
13
- }
14
- buildBaseGenerationOptions() {
15
- return {};
16
- }
17
- },
18
- }));
19
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
20
- AnalyticsService: {
21
- pushTestGenerationToolEvent: jest.fn().mockResolvedValue(undefined),
22
- },
23
- }));
24
- jest.unstable_mockModule("../../utils/logger.js", () => ({
25
- logger: {
26
- debug: jest.fn(),
27
- info: jest.fn(),
28
- warning: jest.fn(),
29
- error: jest.fn(),
30
- },
31
- }));
32
- const { registerContractTestTool } = await import("./generateContractRestTool.js");
33
- const { setActiveRunStatePath, clearActiveRunStatePath } = await import("../../utils/AnalysisStateManager.js");
34
- const { CandidateSource } = await import("../../types/Recommendation.js");
35
- /** Capture the tool handler AND its input schema so calls exercise the real
36
- * Zod contract, mirroring the MCP SDK's registerTool validation. */
37
- function captureHandler() {
38
- let capturedHandler;
39
- let capturedSchema;
40
- const mockServer = {
41
- registerTool: jest.fn((_name, meta, handler) => {
42
- capturedHandler = handler;
43
- capturedSchema = meta.inputSchema;
44
- }),
45
- };
46
- registerContractTestTool(mockServer);
47
- return async (params) => {
48
- const parsed = z.object(capturedSchema).safeParse(params);
49
- if (!parsed.success) {
50
- return {
51
- isError: true,
52
- content: [{ type: "text", text: parsed.error.message }],
53
- };
54
- }
55
- return capturedHandler(parsed.data);
56
- };
57
- }
58
- async function writeStateFile(dir, approvedPlan) {
59
- const stateFile = path.join(dir, "analyze-changes-state.json");
60
- await fs.writeFile(stateFile, JSON.stringify({
61
- existingTests: [],
62
- analysisScope: "current_branch_diff",
63
- newEndpoints: [],
64
- approvedPlan,
65
- metadata: {
66
- sessionId: "test-session",
67
- stateType: "analysis",
68
- createdAt: "2026-01-01T00:00:00.000Z",
69
- updatedAt: "2026-01-01T00:00:00.000Z",
70
- },
71
- }), "utf-8");
72
- return stateFile;
73
- }
74
- // A plan item whose ep key points at a DIFFERENT resource than the calls
75
- // below, so the only viable match channel is the scenarioName. Mirrors the
76
- // SKYR-4019 shape: a server-drafted candidate whose step path drifted from
77
- // the live endpoint the agent generates against.
78
- const NAME_ONLY_ITEM = {
79
- candidateId: "order-response-shape-key-673b87b1",
80
- scenarioName: "order-response-shape-key",
81
- testType: "contract",
82
- category: "breaking_change",
83
- source: CandidateSource.SERVER,
84
- matchKeys: ["name:order-response-shape-key", "ep:contract:GET:/inventory/:p"],
85
- };
86
- const BASE_PARAMS = {
87
- endpointURL: "http://localhost:8000/api/v1/orders/{order_id}",
88
- method: "GET",
89
- language: "python",
90
- framework: "pytest",
91
- outputDir: "/tmp/out",
92
- prompt: "Verify GET /api/v1/orders/{order_id} response shape",
93
- };
94
- describe("skyramp_contract_test_generation — plan-guard scenarioName channel (SKYR-4019)", () => {
95
- let tmpDirs = [];
96
- afterEach(async () => {
97
- clearActiveRunStatePath();
98
- for (const dir of tmpDirs) {
99
- await fs.rm(dir, { recursive: true, force: true });
100
- }
101
- tmpDirs = [];
102
- });
103
- async function mkStateDir(approvedPlan) {
104
- const dir = await fs.mkdtemp(path.join(os.tmpdir(), "contract-guard-test-"));
105
- tmpDirs.push(dir);
106
- const stateFile = await writeStateFile(dir, approvedPlan);
107
- setActiveRunStatePath(stateFile);
108
- }
109
- it("rejects when neither name nor endpoint matches the plan", async () => {
110
- await mkStateDir({
111
- planId: "plan-1",
112
- generate: [NAME_ONLY_ITEM],
113
- additional: [],
114
- demotions: [],
115
- });
116
- const handler = captureHandler();
117
- const result = await handler(BASE_PARAMS);
118
- expect(result.isError).toBe(true);
119
- expect(result.content[0].text).toContain("not in the approved plan");
120
- });
121
- it("allows when the exact scenarioName from the plan is passed (SKYR-4019)", async () => {
122
- await mkStateDir({
123
- planId: "plan-1",
124
- generate: [NAME_ONLY_ITEM],
125
- additional: [],
126
- demotions: [],
127
- });
128
- const handler = captureHandler();
129
- const result = await handler({
130
- ...BASE_PARAMS,
131
- scenarioName: "order-response-shape-key",
132
- });
133
- expect(result.isError).toBeUndefined();
134
- expect(result.content[0].text).toBe("generated");
135
- });
136
- it("allows without scenarioName when no plan is active (standalone/IDE usage)", async () => {
137
- const handler = captureHandler();
138
- const result = await handler(BASE_PARAMS);
139
- expect(result.isError).toBeUndefined();
140
- expect(result.content[0].text).toBe("generated");
141
- });
142
- });