@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,296 +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
- jest.unstable_mockModule("../../utils/logger.js", () => ({
8
- logger: { info: jest.fn(), error: jest.fn(), warning: jest.fn(), debug: jest.fn() },
9
- }));
10
- const { registerRegisterTestPlanTool } = await import("./registerTestPlanTool.js");
11
- const { computeCandidateId, DiscriminatorKind, CandidateSource } = await import("../../types/Recommendation.js");
12
- function captureToolHandler() {
13
- let handler;
14
- let inputSchema;
15
- const fakeServer = {
16
- registerTool: (_name, opts, fn) => {
17
- handler = fn;
18
- inputSchema = opts.inputSchema;
19
- },
20
- };
21
- registerRegisterTestPlanTool(fakeServer);
22
- // Mirrors the real MCP SDK's registerTool: validates args against inputSchema via Zod
23
- // before invoking the handler, so tests exercise the real tool contract.
24
- return async (params) => {
25
- const parsed = z.object(inputSchema).safeParse(params);
26
- if (!parsed.success) {
27
- return { isError: true, content: [{ type: "text", text: parsed.error.message }] };
28
- }
29
- return handler(parsed.data);
30
- };
31
- }
32
- async function writeStateFile(dir, overrides = {}) {
33
- const stateFile = path.join(dir, "analyze-changes-state.json");
34
- await fs.writeFile(stateFile, JSON.stringify({
35
- existingTests: [],
36
- analysisScope: "current_branch_diff",
37
- newEndpoints: [],
38
- repositoryAnalysis: {},
39
- ...overrides,
40
- metadata: {
41
- sessionId: "test-session",
42
- stateType: "analysis",
43
- repositoryPath: dir,
44
- createdAt: "2026-01-01T00:00:00.000Z",
45
- updatedAt: "2026-01-01T00:00:00.000Z",
46
- },
47
- }), "utf-8");
48
- return stateFile;
49
- }
50
- async function readStateFile(stateFile) {
51
- return JSON.parse(await fs.readFile(stateFile, "utf-8"));
52
- }
53
- const AGGREGATE_ANCHOR = "order_count = len(existing_orders)";
54
- function multiRecordCandidate(overrides = {}) {
55
- return {
56
- scenarioName: "orders_multi_record_aggregate",
57
- description: "Create two orders then assert the aggregate count.",
58
- category: "business_rule",
59
- priority: "high",
60
- testType: "integration",
61
- steps: [
62
- {
63
- order: 1,
64
- method: "POST",
65
- path: "/api/v1/orders",
66
- description: "Create first order",
67
- interactionType: "success",
68
- expectedStatusCode: 201,
69
- },
70
- {
71
- order: 2,
72
- method: "GET",
73
- path: "/api/v1/orders/summary",
74
- description: "Assert order_count is 2",
75
- interactionType: "success",
76
- expectedStatusCode: 200,
77
- expectedResponseFields: ["order_count == 2"],
78
- },
79
- ],
80
- ...overrides,
81
- };
82
- }
83
- describe("skyramp_register_test_plan — SKYR-3879 Path B", () => {
84
- let handler;
85
- let tmpDirs = [];
86
- beforeAll(() => {
87
- handler = captureToolHandler();
88
- });
89
- afterEach(async () => {
90
- for (const dir of tmpDirs) {
91
- await fs.rm(dir, { recursive: true, force: true });
92
- }
93
- tmpDirs = [];
94
- });
95
- async function mkTmpDir() {
96
- const dir = await fs.mkdtemp(path.join(os.tmpdir(), "register-plan-test-"));
97
- tmpDirs.push(dir);
98
- return dir;
99
- }
100
- it("errors when stateFile does not exist", async () => {
101
- const dir = await mkTmpDir();
102
- const result = await handler({
103
- stateFile: path.join(dir, "does-not-exist.json"),
104
- candidates: [],
105
- });
106
- expect(result.isError).toBe(true);
107
- expect(result.content[0].text).toContain("State file is empty or invalid");
108
- });
109
- it("rejects a relative stateFile path (schema refine)", async () => {
110
- const result = await handler({ stateFile: "relative/path.json", candidates: [] });
111
- expect(result.isError).toBe(true);
112
- });
113
- it("rejects an empty candidate union instead of persisting an empty plan", async () => {
114
- const dir = await mkTmpDir();
115
- const stateFile = await writeStateFile(dir, { diffText: "" });
116
- const result = await handler({ stateFile, candidates: [] });
117
- expect(result.isError).toBe(true);
118
- expect(result.content[0].text).toContain("No candidates submitted");
119
- const persisted = JSON.parse(await fs.readFile(stateFile, "utf-8"));
120
- expect(persisted.approvedPlan).toBeUndefined();
121
- });
122
- it("promotes a verified discriminator candidate into GENERATE ahead of an unverified one", async () => {
123
- const dir = await mkTmpDir();
124
- const stateFile = await writeStateFile(dir, {
125
- diffText: `--- a/orders.py\n+++ b/orders.py\n+${AGGREGATE_ANCHOR}\n`,
126
- planBudgetContext: {
127
- maxGenerate: 2,
128
- maxTotal: 3,
129
- isUIOnlyPR: false,
130
- hasFrontendChanges: false,
131
- externalCoverageKeys: [],
132
- },
133
- });
134
- const result = await handler({
135
- stateFile,
136
- candidates: [
137
- multiRecordCandidate({
138
- discriminator: { kind: DiscriminatorKind.MULTI_RECORD_AGGREGATE, changedCodeAnchor: AGGREGATE_ANCHOR },
139
- }),
140
- {
141
- scenarioName: "products_crud",
142
- description: "Basic CRUD happy path.",
143
- category: "crud",
144
- priority: "low",
145
- testType: "contract",
146
- steps: [
147
- {
148
- order: 1,
149
- method: "GET",
150
- path: "/api/v1/products",
151
- description: "List products",
152
- interactionType: "success",
153
- expectedStatusCode: 200,
154
- },
155
- ],
156
- },
157
- ],
158
- });
159
- expect(result.isError).toBeFalsy();
160
- expect(result.content[0].text).toContain("orders_multi_record_aggregate");
161
- expect(result.content[0].text).toContain("verified discriminator: multi_record_aggregate");
162
- const persisted = await readStateFile(stateFile);
163
- expect(persisted.approvedPlan.generate[0].scenarioName).toBe("orders_multi_record_aggregate");
164
- expect(persisted.approvedPlan.generate[0].verifiedDiscriminator).toBe(DiscriminatorKind.MULTI_RECORD_AGGREGATE);
165
- expect(persisted.approvedPlan.demotions).toEqual([]);
166
- });
167
- it("demotes a gamed discriminator claim (no structural evidence) but keeps the candidate registered", async () => {
168
- const dir = await mkTmpDir();
169
- const stateFile = await writeStateFile(dir, {
170
- diffText: `--- a/orders.py\n+++ b/orders.py\n+${AGGREGATE_ANCHOR}\n`,
171
- planBudgetContext: {
172
- maxGenerate: 2,
173
- maxTotal: 3,
174
- isUIOnlyPR: false,
175
- hasFrontendChanges: false,
176
- externalCoverageKeys: [],
177
- },
178
- });
179
- const result = await handler({
180
- stateFile,
181
- candidates: [
182
- multiRecordCandidate({
183
- // No aggregate assertion — steps.length === 2 but the second step doesn't
184
- // assert a count. A gamed claim: declares the discriminator anyway.
185
- steps: [
186
- {
187
- order: 1,
188
- method: "POST",
189
- path: "/api/v1/orders",
190
- description: "Create order",
191
- interactionType: "success",
192
- expectedStatusCode: 201,
193
- },
194
- ],
195
- discriminator: { kind: DiscriminatorKind.MULTI_RECORD_AGGREGATE, changedCodeAnchor: AGGREGATE_ANCHOR },
196
- }),
197
- ],
198
- });
199
- expect(result.isError).toBeFalsy();
200
- expect(result.content[0].text).toContain("Demotions (1)");
201
- expect(result.content[0].text).toContain("multi_record_aggregate unverified");
202
- const persisted = await readStateFile(stateFile);
203
- expect(persisted.approvedPlan.demotions).toHaveLength(1);
204
- expect(persisted.approvedPlan.demotions[0].reason).toContain("multi_record_aggregate unverified");
205
- // The candidate itself is NOT dropped — it must still appear somewhere in the plan.
206
- const allScenarioNames = [...persisted.approvedPlan.generate, ...persisted.approvedPlan.additional].map((item) => item.scenarioName);
207
- expect(allScenarioNames).toContain("orders_multi_record_aggregate");
208
- // And it must NOT carry a verifiedDiscriminator.
209
- const item = [...persisted.approvedPlan.generate, ...persisted.approvedPlan.additional].find((i) => i.scenarioName === "orders_multi_record_aggregate");
210
- expect(item.verifiedDiscriminator).toBeUndefined();
211
- });
212
- it("merges agent candidates with pre-seeded server candidates from analyze_changes", async () => {
213
- const dir = await mkTmpDir();
214
- const serverScenario = {
215
- scenarioName: "server_seeded_scenario",
216
- description: "Pre-ranked by the server's own classification.",
217
- category: "business_rule",
218
- priority: "high",
219
- steps: [
220
- {
221
- order: 1,
222
- method: "GET",
223
- path: "/api/v1/reports",
224
- description: "Fetch report",
225
- interactionType: "success",
226
- expectedStatusCode: 200,
227
- },
228
- ],
229
- chainingKeys: [],
230
- requiresAuth: false,
231
- estimatedComplexity: "simple",
232
- testType: "contract",
233
- };
234
- const serverCandidateId = computeCandidateId(serverScenario);
235
- const stateFile = await writeStateFile(dir, {
236
- diffText: "",
237
- repositoryAnalysis: { scenarios: [serverScenario] },
238
- planBudgetContext: {
239
- maxGenerate: 5,
240
- maxTotal: 5,
241
- isUIOnlyPR: false,
242
- hasFrontendChanges: false,
243
- externalCoverageKeys: [],
244
- },
245
- approvedPlan: {
246
- planId: "prior-plan",
247
- createdAt: "2026-01-01T00:00:00.000Z",
248
- generate: [
249
- {
250
- candidateId: serverCandidateId,
251
- scenarioName: "server_seeded_scenario",
252
- testType: "contract",
253
- category: "business_rule",
254
- source: CandidateSource.SERVER,
255
- matchKeys: [`name:server-seeded-scenario`, `ep:contract:GET:/api/v1/reports`],
256
- },
257
- ],
258
- additional: [],
259
- demotions: [],
260
- },
261
- });
262
- const result = await handler({
263
- stateFile,
264
- candidates: [
265
- {
266
- scenarioName: "agent_drafted_scenario",
267
- description: "Agent-drafted candidate this run.",
268
- category: "workflow",
269
- priority: "medium",
270
- testType: "integration",
271
- steps: [
272
- {
273
- order: 1,
274
- method: "POST",
275
- path: "/api/v1/carts",
276
- description: "Create cart",
277
- interactionType: "success",
278
- expectedStatusCode: 201,
279
- },
280
- ],
281
- },
282
- ],
283
- });
284
- expect(result.isError).toBeFalsy();
285
- const persisted = await readStateFile(stateFile);
286
- const names = persisted.approvedPlan.generate.map((item) => item.scenarioName);
287
- expect(names).toContain("server_seeded_scenario");
288
- expect(names).toContain("agent_drafted_scenario");
289
- const serverItem = persisted.approvedPlan.generate.find((item) => item.scenarioName === "server_seeded_scenario");
290
- expect(serverItem.source).toBe("server");
291
- const agentItem = persisted.approvedPlan.generate.find((item) => item.scenarioName === "agent_drafted_scenario");
292
- expect(agentItem.source).toBe("agent");
293
- // New plan replaces the old planId (fresh registration, not an append).
294
- expect(persisted.approvedPlan.planId).not.toBe("prior-plan");
295
- });
296
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,17 +0,0 @@
1
- import path from "path";
2
- import { resolveSaveStoragePath } from "./resolveSaveStoragePath.js";
3
- describe("resolveSaveStoragePath", () => {
4
- const outputDir = "/abs/output";
5
- it("joins a bare filename with outputDir", () => {
6
- expect(resolveSaveStoragePath("auth.json", outputDir)).toBe(path.join(outputDir, "auth.json"));
7
- });
8
- it("returns an absolute path unchanged", () => {
9
- expect(resolveSaveStoragePath("/tmp/storage.json", outputDir)).toBe("/tmp/storage.json");
10
- });
11
- it("returns a relative path with separators unchanged", () => {
12
- expect(resolveSaveStoragePath("subdir/storage.json", outputDir)).toBe("subdir/storage.json");
13
- });
14
- it("returns input unchanged when outputDir is undefined", () => {
15
- expect(resolveSaveStoragePath("auth.json", undefined)).toBe("auth.json");
16
- });
17
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,104 +0,0 @@
1
- import path from "path";
2
- import { SESSION_STORAGE_FILENAME } from "../../types/TestTypes.js";
3
- import { resolveSessionPaths } from "./resolveSessionPaths.js";
4
- describe("resolveSessionPaths", () => {
5
- const outputDir = "/abs/output";
6
- const defaultPath = path.join(outputDir, SESSION_STORAGE_FILENAME);
7
- const baseArgs = {
8
- loadOverride: undefined,
9
- saveOverride: undefined,
10
- outputDir,
11
- sessionExists: false,
12
- };
13
- describe("mode=auto", () => {
14
- it("does NOT save when no session exists — no implicit credential storage (SKYR-3793)", () => {
15
- // Storing the auth session must be opt-in (sessionMode=capture or an
16
- // explicit save override). Plain auto with no existing session must not
17
- // write credentials the user never asked to store.
18
- expect(resolveSessionPaths({ ...baseArgs, mode: "auto" })).toEqual({});
19
- });
20
- it("reuses (loads) the default path when a session exists, without saving", () => {
21
- expect(resolveSessionPaths({ ...baseArgs, mode: "auto", sessionExists: true })).toEqual({ loadPath: defaultPath });
22
- });
23
- it("respects an explicit load override when given (auto + sessionExists)", () => {
24
- expect(resolveSessionPaths({
25
- ...baseArgs,
26
- mode: "auto",
27
- sessionExists: true,
28
- loadOverride: "/elsewhere/session.json",
29
- })).toEqual({ loadPath: "/elsewhere/session.json" });
30
- });
31
- it("respects an explicit save override (auto + no session)", () => {
32
- expect(resolveSessionPaths({
33
- ...baseArgs,
34
- mode: "auto",
35
- saveOverride: "auth.json",
36
- })).toEqual({ savePath: path.join(outputDir, "auth.json") });
37
- });
38
- });
39
- describe("mode=capture", () => {
40
- it("always saves at the default path, never loads", () => {
41
- expect(resolveSessionPaths({ ...baseArgs, mode: "capture" })).toEqual({
42
- savePath: defaultPath,
43
- });
44
- });
45
- it("ignores a load override (capture is a clean slate)", () => {
46
- expect(resolveSessionPaths({
47
- ...baseArgs,
48
- mode: "capture",
49
- loadOverride: "/elsewhere/session.json",
50
- })).toEqual({ savePath: defaultPath });
51
- });
52
- it("honors an explicit save override", () => {
53
- expect(resolveSessionPaths({
54
- ...baseArgs,
55
- mode: "capture",
56
- saveOverride: "/tmp/custom.json",
57
- })).toEqual({ savePath: "/tmp/custom.json" });
58
- });
59
- it("still captures even when a session file already exists (we're recapturing)", () => {
60
- expect(resolveSessionPaths({
61
- ...baseArgs,
62
- mode: "capture",
63
- sessionExists: true,
64
- })).toEqual({ savePath: defaultPath });
65
- });
66
- });
67
- describe("mode=reuse", () => {
68
- it("loads the default path, does not save (preserves the session file)", () => {
69
- expect(resolveSessionPaths({ ...baseArgs, mode: "reuse" })).toEqual({
70
- loadPath: defaultPath,
71
- });
72
- });
73
- it("honors an explicit load override", () => {
74
- expect(resolveSessionPaths({
75
- ...baseArgs,
76
- mode: "reuse",
77
- loadOverride: "/elsewhere/session.json",
78
- })).toEqual({ loadPath: "/elsewhere/session.json" });
79
- });
80
- it("allows an explicit save override even in reuse (caller wants to refresh)", () => {
81
- expect(resolveSessionPaths({
82
- ...baseArgs,
83
- mode: "reuse",
84
- saveOverride: "/tmp/refreshed.json",
85
- })).toEqual({ loadPath: defaultPath, savePath: "/tmp/refreshed.json" });
86
- });
87
- });
88
- describe("mode=ignore", () => {
89
- it("loads and saves nothing by default", () => {
90
- expect(resolveSessionPaths({ ...baseArgs, mode: "ignore" })).toEqual({});
91
- });
92
- it("honors explicit overrides only", () => {
93
- expect(resolveSessionPaths({
94
- ...baseArgs,
95
- mode: "ignore",
96
- loadOverride: "/in/session.json",
97
- saveOverride: "/out/session.json",
98
- })).toEqual({
99
- loadPath: "/in/session.json",
100
- savePath: "/out/session.json",
101
- });
102
- });
103
- });
104
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,17 +0,0 @@
1
- import { consumeSavedSessionPath, setSavedSessionPath, } from "./sessionState.js";
2
- describe("sessionState", () => {
3
- afterEach(() => {
4
- // Ensure module-level state is cleared between tests.
5
- setSavedSessionPath(undefined);
6
- });
7
- it("returns the set value on first consume and undefined on the second", () => {
8
- setSavedSessionPath("/abs/session.json");
9
- expect(consumeSavedSessionPath()).toBe("/abs/session.json");
10
- expect(consumeSavedSessionPath()).toBeUndefined();
11
- });
12
- it("setting undefined clears any prior value", () => {
13
- setSavedSessionPath("/abs/session.json");
14
- setSavedSessionPath(undefined);
15
- expect(consumeSavedSessionPath()).toBeUndefined();
16
- });
17
- });
@@ -1,139 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest } from "@jest/globals";
3
- import { z } from "zod";
4
- import * as fs from "fs/promises";
5
- import * as path from "path";
6
- import * as os from "os";
7
- // WorkspaceConfigManager + serviceSchema now live in the local workspace module
8
- // (SKYR-3794 moved them out of @skyramp/skyramp). serviceSchema must be a real
9
- // zod schema because `z.array(serviceSchema)` runs at module import time.
10
- jest.unstable_mockModule("../../workspace/workspace.js", () => ({
11
- WorkspaceConfigManager: jest.fn(),
12
- serviceSchema: z.any(),
13
- }));
14
- jest.unstable_mockModule("js-yaml", () => ({
15
- default: { dump: jest.fn() },
16
- }));
17
- jest.unstable_mockModule("../../utils/logger.js", () => ({
18
- logger: { info: jest.fn(), error: jest.fn() },
19
- }));
20
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
21
- AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
22
- }));
23
- jest.unstable_mockModule("./initScanWorkspaceTool.js", () => ({
24
- validateAndConsumeScanToken: jest.fn(),
25
- }));
26
- const { registerInitializeWorkspaceTool } = await import("./initializeWorkspaceTool.js");
27
- const { validateAndConsumeScanToken } = await import("./initScanWorkspaceTool.js");
28
- const { WorkspaceConfigManager } = await import("../../workspace/workspace.js");
29
- const yaml = (await import("js-yaml")).default;
30
- describe("initializeWorkspaceTool", () => {
31
- let tmpDir;
32
- let handler;
33
- let mockManager;
34
- beforeEach(async () => {
35
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "init-workspace-test-"));
36
- // Build a mock WorkspaceConfigManager
37
- mockManager = {
38
- exists: jest.fn().mockResolvedValue(false),
39
- getConfigPath: jest
40
- .fn()
41
- .mockReturnValue(path.join(tmpDir, ".skyramp/workspace.yml")),
42
- initialize: jest.fn().mockResolvedValue({ workspace: {}, services: [] }),
43
- updateMetadata: jest.fn().mockImplementation(async (data) => ({
44
- workspace: {},
45
- services: [],
46
- metadata: { ...data, updatedAt: "2024-01-01T00:00:00.000Z" },
47
- })),
48
- };
49
- WorkspaceConfigManager.mockImplementation(() => mockManager);
50
- // Mock validateAndConsumeScanToken to accept any token
51
- validateAndConsumeScanToken.mockReturnValue(true);
52
- // Mock yaml.dump to return a simple string
53
- yaml.dump.mockReturnValue("services: []\n");
54
- // Register the tool and capture the handler
55
- const mockServer = {
56
- registerTool: jest.fn((_name, _opts, fn) => {
57
- handler = fn;
58
- }),
59
- };
60
- registerInitializeWorkspaceTool(mockServer);
61
- });
62
- afterEach(async () => {
63
- await fs.rm(tmpDir, { recursive: true, force: true });
64
- jest.clearAllMocks();
65
- });
66
- it("registers the tool with the expected schema shape", () => {
67
- const mockServer = {
68
- registerTool: jest.fn(),
69
- };
70
- registerInitializeWorkspaceTool(mockServer);
71
- expect(mockServer.registerTool).toHaveBeenCalledWith("skyramp_init_workspace", expect.objectContaining({
72
- description: expect.stringContaining("PREREQUISITE"),
73
- inputSchema: expect.objectContaining({
74
- workspacePath: expect.any(Object),
75
- services: expect.any(Object),
76
- scanToken: expect.any(Object),
77
- force: expect.any(Object),
78
- }),
79
- }), expect.any(Function));
80
- });
81
- it("successfully initializes workspace with valid input in merge mode", async () => {
82
- // Create the parent directory
83
- await fs.mkdir(path.join(tmpDir, ".skyramp"), { recursive: true });
84
- const result = await handler({
85
- workspacePath: tmpDir,
86
- services: [
87
- {
88
- serviceName: "test-service",
89
- language: "python",
90
- testDirectory: "tests",
91
- },
92
- ],
93
- scanToken: "valid-token",
94
- force: false,
95
- });
96
- expect(result.isError).toBeFalsy();
97
- expect(result.content[0].text).toContain("Workspace initialized");
98
- expect(mockManager.initialize).toHaveBeenCalled();
99
- expect(mockManager.updateMetadata).toHaveBeenCalledWith(expect.objectContaining({
100
- executorVersion: expect.any(String),
101
- }));
102
- });
103
- it("returns error when workspace already exists without force flag", async () => {
104
- mockManager.exists.mockResolvedValue(true);
105
- const result = await handler({
106
- workspacePath: tmpDir,
107
- services: [
108
- { serviceName: "svc", language: "python", testDirectory: "tests" },
109
- ],
110
- scanToken: "valid-token",
111
- force: false,
112
- });
113
- expect(result.isError).toBe(false);
114
- expect(result.content[0].text).toContain("already initialized");
115
- });
116
- it("returns error when scanToken is invalid", async () => {
117
- validateAndConsumeScanToken.mockReturnValue(false);
118
- const result = await handler({
119
- workspacePath: tmpDir,
120
- services: [
121
- { serviceName: "svc", language: "python", testDirectory: "tests" },
122
- ],
123
- scanToken: "bad-token",
124
- force: false,
125
- });
126
- expect(result.isError).toBe(true);
127
- expect(result.content[0].text).toContain("Invalid or expired scanToken");
128
- });
129
- it("returns error when no services provided", async () => {
130
- const result = await handler({
131
- workspacePath: tmpDir,
132
- services: [],
133
- scanToken: "valid-token",
134
- force: false,
135
- });
136
- expect(result.isError).toBe(true);
137
- expect(result.content[0].text).toContain("No services provided");
138
- });
139
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,50 +0,0 @@
1
- import { upsertServicesByRepo } from "./serviceUpsert.js";
2
- // upsertServicesByRepo is the merge=true core: it upserts incoming services into
3
- // the existing list keyed on the composite (repository, serviceName), so a
4
- // same-named service from a different repo is a distinct entry rather than an
5
- // overwrite. This is what makes the unified multi-repo workspace.yml correct.
6
- describe("upsertServicesByRepo", () => {
7
- const svc = (serviceName, repository, testDirectory) => ({ serviceName, repository, testDirectory });
8
- it("keeps same-named services from different repos as distinct entries", () => {
9
- const existing = [svc("api", undefined, "tests/primary")];
10
- const result = upsertServicesByRepo(existing, [
11
- svc("api", "letsramp/api-insight", "tests/py"),
12
- ]);
13
- expect(result).toHaveLength(2);
14
- expect(result.find((s) => !s.repository)?.testDirectory).toBe("tests/primary");
15
- expect(result.find((s) => s.repository === "letsramp/api-insight")?.testDirectory).toBe("tests/py");
16
- });
17
- it("updates only the matching (repository, serviceName) entry in place", () => {
18
- const existing = [
19
- svc("api", undefined, "tests/primary"),
20
- svc("api", "letsramp/api-insight", "tests/py"),
21
- ];
22
- // Re-register the related repo's service with a new testDirectory.
23
- const result = upsertServicesByRepo(existing, [
24
- svc("api", "letsramp/api-insight", "tests/py-v2"),
25
- ]);
26
- expect(result).toHaveLength(2);
27
- // Primary untouched.
28
- expect(result.find((s) => !s.repository)?.testDirectory).toBe("tests/primary");
29
- // Related repo's entry updated in place (not duplicated).
30
- const related = result.filter((s) => s.repository === "letsramp/api-insight");
31
- expect(related).toHaveLength(1);
32
- expect(related[0].testDirectory).toBe("tests/py-v2");
33
- });
34
- it("treats an absent repository as the primary (normalized to '')", () => {
35
- const existing = [svc("api", undefined, "tests/primary")];
36
- // A second primary write (no repository) upserts the SAME entry.
37
- const result = upsertServicesByRepo(existing, [svc("api", undefined, "tests/primary-v2")]);
38
- expect(result).toHaveLength(1);
39
- expect(result[0].testDirectory).toBe("tests/primary-v2");
40
- });
41
- it("appends brand-new services from multiple repos", () => {
42
- const result = upsertServicesByRepo([], [
43
- svc("web", "letsramp/frontend", "tests/web"),
44
- svc("api", "letsramp/backend", "tests/api"),
45
- svc("api", undefined, "tests/primary"),
46
- ]);
47
- expect(result).toHaveLength(3);
48
- expect(new Set(result.map((s) => `${s.repository ?? ""}|${s.serviceName}`))).toEqual(new Set(["letsramp/frontend|web", "letsramp/backend|api", "|api"]));
49
- });
50
- });
@@ -1 +0,0 @@
1
- export {};