@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,522 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest } from "@jest/globals";
3
- // Mock all heavy dependencies so the module can be loaded in isolation.
4
- // Under native ESM, jest.mock is not hoisted — every mock the subject's import
5
- // graph touches must use unstable_mockModule before the dynamic import below.
6
- jest.unstable_mockModule("simple-git", () => ({ simpleGit: jest.fn() }));
7
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
8
- AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
9
- }));
10
- jest.unstable_mockModule("../../prompts/test-recommendation/test-recommendation-prompt.js", () => ({
11
- buildRecommendationPrompt: jest.fn(),
12
- // SKYR-3879 Path B pre-seed reads this before the state write — default to
13
- // "nothing pre-ranked" so existing tests (which don't exercise the plan
14
- // pre-seed) see byte-identical unifiedState/writeRepoData behavior.
15
- computeScoredCandidates: jest.fn(() => ({
16
- scored: [],
17
- filteredChangedFiles: [],
18
- isUIOnlyPR: false,
19
- hasFrontendChanges: false,
20
- hasApiChanges: false,
21
- maxGen: 0,
22
- seed: "",
23
- })),
24
- }));
25
- jest.unstable_mockModule("../../prompts/test-recommendation/recommendationSections.js", () => ({
26
- MAX_RECOMMENDATIONS: 10,
27
- MAX_TESTS_TO_GENERATE: 3,
28
- }));
29
- jest.unstable_mockModule("../../prompts/test-recommendation/analysisOutputPrompt.js", () => ({
30
- buildAnalysisOutputText: jest.fn(),
31
- }));
32
- jest.unstable_mockModule("../../services/TestDiscoveryService.js", () => ({
33
- TestDiscoveryService: jest.fn(),
34
- }));
35
- jest.unstable_mockModule("../../utils/branchDiff.js", () => ({
36
- computeBranchDiff: jest.fn(),
37
- }));
38
- jest.unstable_mockModule("../../utils/routeParsers.js", () => ({
39
- classifyEndpointsByChangedFiles: jest.fn(),
40
- extractResourceFromPath: jest.fn(),
41
- extractTopLevelResourceFromPath: jest.fn(),
42
- parseFileEndpoints: jest.fn(),
43
- selectRemovalCandidateFiles: jest.fn(() => []),
44
- recoverRemovedEndpointsFromBase: jest.fn(async () => []),
45
- // isRouteLikeFile needed by pathAffinityClassification.js (SKYR-3857), which
46
- // analyzeChangesTool.ts now imports — tests here drive the classify mock to
47
- // unmatchedFiles: [] so the fallback never runs, but the module graph must link.
48
- isRouteLikeFile: jest.fn(() => false),
49
- isVersionLikeSegment: jest.fn(() => false),
50
- SKIP_PATH_SEGMENTS: new Set(["api", "v1", "v2", "v3", "public"]),
51
- }));
52
- jest.unstable_mockModule("../../utils/importerHop.js", () => ({
53
- mapUnmatchedFilesToImporterEndpoints: jest.fn(() => ({ endpoints: [], byFile: {} })),
54
- buildHopResponseShapeInputs: jest.fn(() => ({ endpoints: [], addedFieldsByFile: {} })),
55
- deriveResourceToken: jest.fn(() => "unknown"),
56
- // deriveResourceTokensForFile/endpointResourceTokens/sliceAddedLinesByFile
57
- // needed by pathAffinityClassification.js (SKYR-3857 derivation
58
- // consolidation), which now imports all three from this module — tests here
59
- // drive isRouteLikeFile to false so the fallback never calls them, but the
60
- // module graph must link.
61
- deriveResourceTokensForFile: jest.fn(() => []),
62
- endpointResourceTokens: jest.fn(() => new Set()),
63
- sliceAddedLinesByFile: jest.fn(() => new Map()),
64
- sliceDiffByFile: jest.fn(() => new Map()),
65
- extractChangedSymbols: jest.fn(() => []),
66
- }));
67
- jest.unstable_mockModule("../../utils/repoScanner.js", () => ({
68
- scanAllRepoEndpoints: jest.fn(),
69
- scanRelatedEndpoints: jest.fn(),
70
- grepRouterMountingContext: jest.fn(),
71
- findCandidateRouteFiles: jest.fn(),
72
- }));
73
- jest.unstable_mockModule("../../utils/projectMetadata.js", () => ({
74
- detectProjectMetadata: jest.fn(),
75
- }));
76
- jest.unstable_mockModule("../../utils/scenarioDrafting.js", () => ({
77
- draftScenariosFromEndpoints: jest.fn(),
78
- draftResponseShapeScenarios: jest.fn(() => []),
79
- capScenarios: jest.fn((scenarios) => scenarios),
80
- }));
81
- jest.unstable_mockModule("../../utils/trace-parser.js", () => ({
82
- parseTraceFile: jest.fn(),
83
- discoverTraceFiles: jest.fn(),
84
- discoverPlaywrightZips: jest.fn(),
85
- }));
86
- jest.unstable_mockModule("../../utils/pr-comment-parser.js", () => ({
87
- parsePRComments: jest.fn(),
88
- }));
89
- jest.unstable_mockModule("../../utils/AnalysisStateManager.js", () => {
90
- const makeInstance = () => ({
91
- writeData: jest.fn(),
92
- writeRepoData: jest.fn(),
93
- getStatePath: jest.fn().mockReturnValue("/tmp/skyramp/analysis-state.json"),
94
- });
95
- const StateManager = jest.fn().mockImplementation(makeInstance);
96
- StateManager.fromStatePath = jest.fn().mockImplementation(makeInstance);
97
- return {
98
- StateManager,
99
- registerSession: jest.fn(),
100
- storeSessionData: jest.fn(),
101
- setTestsRepoDir: jest.fn(),
102
- getActiveRunStatePath: jest.fn(),
103
- setActiveRunStatePath: jest.fn(),
104
- clearActiveRunStatePath: jest.fn(),
105
- };
106
- });
107
- // Keep every real workspaceAuth export the subject's import graph needs
108
- // (readWorkspaceConfigRaw, getDefaultAuthHeader, WorkspaceAuthType, …) and
109
- // override only parseWorkspaceAuthType. requireActual can't load ESM
110
- // synchronously, so import the real module first (mock is not hoisted).
111
- const realWorkspaceAuth = await import("../../utils/workspaceAuth.js");
112
- jest.unstable_mockModule("../../utils/workspaceAuth.js", () => ({
113
- ...realWorkspaceAuth,
114
- parseWorkspaceAuthType: jest.fn(),
115
- }));
116
- jest.unstable_mockModule("../../utils/logger.js", () => ({
117
- logger: { info: jest.fn(), debug: jest.fn(), error: jest.fn(), warn: jest.fn(), warning: jest.fn() },
118
- }));
119
- jest.unstable_mockModule("@modelcontextprotocol/sdk/server/mcp.js", () => ({
120
- McpServer: jest.fn(),
121
- }));
122
- jest.unstable_mockModule("@modelcontextprotocol/sdk/types.js", () => ({}));
123
- jest.unstable_mockModule("@modelcontextprotocol/sdk/shared/protocol.js", () => ({}));
124
- // All subject + mocked-module imports must be dynamic and live AFTER the
125
- // unstable_mockModule calls above — static imports hoist above the mocks under
126
- // native ESM, loading the real (heavy) modules unmocked.
127
- const { analyzeChangesInputSchema, isSecurityRelevantDiff, filterEndpointsBySpec, filterUnsupportedGraphQLEndpoints, isGraphQLFile, registerAnalyzeChangesTool, buildTraceFileEntry, buildCaptureInstructions, buildUnintegratedInstructions, NO_UI_INSTRUCTIONS, NO_RESOLVABLE_URLS_INSTRUCTIONS, } = await import("./analyzeChangesTool.js");
128
- const { computeBranchDiff } = await import("../../utils/branchDiff.js");
129
- const { classifyEndpointsByChangedFiles } = await import("../../utils/routeParsers.js");
130
- const { scanRelatedEndpoints, scanAllRepoEndpoints } = await import("../../utils/repoScanner.js");
131
- const { StateManager } = await import("../../utils/AnalysisStateManager.js");
132
- const os = await import("os");
133
- const path = await import("path");
134
- const fsSync = await import("fs");
135
- const fsModule = fsSync;
136
- describe("isSecurityRelevantDiff", () => {
137
- it("matches auth-specific middleware and admin-key signals", () => {
138
- expect(isSecurityRelevantDiff("const authMiddleware = requireAuth();")).toBe(true);
139
- expect(isSecurityRelevantDiff("require_admin_key(request)")).toBe(true);
140
- expect(isSecurityRelevantDiff("headers['x-admin-key']")).toBe(true);
141
- expect(isSecurityRelevantDiff("require-role for destructive delete")).toBe(true);
142
- });
143
- it("does not match generic middleware-only refactors", () => {
144
- expect(isSecurityRelevantDiff("refactor logging middleware order")).toBe(false);
145
- expect(isSecurityRelevantDiff("move compression middleware to server setup")).toBe(false);
146
- });
147
- });
148
- describe("automatic old files cleanup", () => {
149
- let cleanupOldFilesSpy;
150
- beforeEach(() => {
151
- jest.clearAllMocks();
152
- cleanupOldFilesSpy = jest.fn().mockResolvedValue(0);
153
- StateManager.cleanupOldFiles = cleanupOldFilesSpy;
154
- });
155
- it("calls cleanupOldFiles with default temp dir", async () => {
156
- expect(StateManager.cleanupOldFiles).toBeDefined();
157
- await StateManager.cleanupOldFiles(24, undefined);
158
- expect(cleanupOldFilesSpy).toHaveBeenCalledWith(24, undefined);
159
- });
160
- it("calls cleanupOldFiles with custom dir", async () => {
161
- const customPath = "/custom/dir";
162
- await StateManager.cleanupOldFiles(24, customPath);
163
- expect(cleanupOldFilesSpy).toHaveBeenCalledWith(24, customPath);
164
- });
165
- it("calls cleanupOldFiles with empty stateTypes", async () => {
166
- await StateManager.cleanupOldFiles(24, undefined, []);
167
- expect(cleanupOldFilesSpy).toHaveBeenCalledWith(24, undefined, []);
168
- });
169
- it("continues execution if cleanup fails", async () => {
170
- cleanupOldFilesSpy.mockRejectedValue(new Error("permission denied"));
171
- await expect(StateManager.cleanupOldFiles(24).catch(() => Promise.resolve())).resolves.toBeUndefined();
172
- });
173
- });
174
- // ─────────────────────────────────────────────────────────────────────────────
175
- // filterEndpointsBySpec — spec filtering + merge logic (Step 4c / 4c-merge)
176
- // ─────────────────────────────────────────────────────────────────────────────
177
- describe("filterEndpointsBySpec", () => {
178
- const makeEp = (path, methods = ["GET"]) => ({ path, methods, sourceFile: "" });
179
- it("removes scanned endpoints that are not in the spec and not diff-changed", () => {
180
- const scanned = [makeEp("/api/v1/users"), makeEp("/api/v1/phantom")];
181
- const specPaths = new Set(["/api/v1/users"]);
182
- const specItems = { "/api/v1/users": { get: {} } };
183
- const diffChanged = new Set();
184
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
185
- expect(result.map(e => e.path)).toContain("/api/v1/users");
186
- expect(result.map(e => e.path)).not.toContain("/api/v1/phantom");
187
- });
188
- it("returns the endpoint list unchanged when specPaths is empty (early-return guard)", () => {
189
- const scanned = [makeEp("/api/v1/new-feature")];
190
- const specPaths = new Set(); // spec lags — new endpoint not there yet
191
- const specItems = {};
192
- const diffChanged = new Set(["/api/v1/new-feature"]);
193
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
194
- // specPaths is empty → early-return guard triggers → input returned as-is without filtering
195
- expect(result.map(e => e.path)).toContain("/api/v1/new-feature");
196
- });
197
- it("preserves a diff-changed endpoint that is absent from a non-empty spec (spec-lag path)", () => {
198
- // This is the real spec-lag scenario: spec exists (non-empty) but the new endpoint
199
- // added in this PR hasn't been documented yet. filterEndpointsBySpec must keep it.
200
- const scanned = [makeEp("/api/v1/new-feature")];
201
- const specPaths = new Set(["/api/v1/users"]); // spec has other paths but not new-feature
202
- const specItems = { "/api/v1/users": { get: {} } };
203
- const diffChanged = new Set(["/api/v1/new-feature"]);
204
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
205
- expect(result.map(e => e.path)).toContain("/api/v1/new-feature");
206
- });
207
- it("preserves spec-lagging diff-changed path when spec has other paths", () => {
208
- const scanned = [makeEp("/api/v1/users"), makeEp("/api/v1/new-feature")];
209
- const specPaths = new Set(["/api/v1/users"]); // spec has users but not new-feature
210
- const specItems = { "/api/v1/users": { get: {} } };
211
- const diffChanged = new Set(["/api/v1/new-feature"]);
212
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
213
- expect(result.map(e => e.path)).toContain("/api/v1/users");
214
- expect(result.map(e => e.path)).toContain("/api/v1/new-feature");
215
- });
216
- it("normalises Express :param paths to {param} when matching OpenAPI spec paths", () => {
217
- const scanned = [makeEp("/api/v1/users/:id")];
218
- const specPaths = new Set(["/api/v1/users/{id}"]);
219
- const specItems = { "/api/v1/users/{id}": { get: {} } };
220
- const diffChanged = new Set();
221
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
222
- const paths = result.map(e => e.path);
223
- // Express-style path is kept because it normalises to the spec path
224
- expect(paths).toContain("/api/v1/users/:id");
225
- // The spec form must NOT be added as a duplicate — would produce two entries for the same route
226
- expect(paths).not.toContain("/api/v1/users/{id}");
227
- expect(paths).toHaveLength(1);
228
- });
229
- it("merges spec-only paths that the static scan missed", () => {
230
- const scanned = [makeEp("/api/v1/users")];
231
- const specPaths = new Set(["/api/v1/users", "/api/v1/products"]);
232
- const specItems = {
233
- "/api/v1/users": { get: {} },
234
- "/api/v1/products": { get: {}, post: {} },
235
- };
236
- const diffChanged = new Set();
237
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
238
- const paths = result.map(e => e.path);
239
- expect(paths).toContain("/api/v1/users");
240
- expect(paths).toContain("/api/v1/products");
241
- const products = result.find(e => e.path === "/api/v1/products");
242
- expect(products?.methods).toEqual(expect.arrayContaining(["GET", "POST"]));
243
- });
244
- it("does not add spec-only paths that have no HTTP verb operations", () => {
245
- const scanned = [];
246
- const specPaths = new Set(["/api/v1/empty-path"]);
247
- const specItems = { "/api/v1/empty-path": { "x-custom-field": "value" } }; // no HTTP verbs
248
- const diffChanged = new Set();
249
- const result = filterEndpointsBySpec(scanned, specPaths, specItems, diffChanged);
250
- expect(result.map(e => e.path)).not.toContain("/api/v1/empty-path");
251
- });
252
- it("returns the original array unchanged when specPaths is empty", () => {
253
- const scanned = [makeEp("/api/v1/anything")];
254
- const result = filterEndpointsBySpec(scanned, new Set(), {}, new Set());
255
- expect(result).toEqual(scanned);
256
- });
257
- });
258
- // ─────────────────────────────────────────────────────────────────────────────
259
- // isGraphQLFile — unit tests
260
- // ─────────────────────────────────────────────────────────────────────────────
261
- describe("isGraphQLFile", () => {
262
- const tmpDir = os.tmpdir();
263
- it("returns true for .graphql extension without reading content", async () => {
264
- const result = await isGraphQLFile("schema.graphql", "/any/repo");
265
- expect(result).toBe(true);
266
- });
267
- it("returns true for .gql extension without reading content", async () => {
268
- const result = await isGraphQLFile("types.gql", "/any/repo");
269
- expect(result).toBe(true);
270
- });
271
- it("returns true for a top-level graphql directory non-code artifact", async () => {
272
- const result = await isGraphQLFile("graphql/schema", "/any/repo");
273
- expect(result).toBe(true);
274
- });
275
- it("returns true for a file containing a GraphQL type Query block", async () => {
276
- const file = path.join(tmpDir, "graphql-test-query.ts");
277
- fsSync.writeFileSync(file, 'type Query {\n hello: String\n}\n');
278
- const result = await isGraphQLFile(path.basename(file), tmpDir);
279
- expect(result).toBe(true);
280
- });
281
- it("returns true for a file containing a GraphQL schema block", async () => {
282
- const file = path.join(tmpDir, "graphql-test-schema.ts");
283
- fsSync.writeFileSync(file, 'schema {\n query: Query\n}\n');
284
- const result = await isGraphQLFile(path.basename(file), tmpDir);
285
- expect(result).toBe(true);
286
- });
287
- it("returns true for a GraphQL resolver implementation file", async () => {
288
- const file = path.join(tmpDir, "users.resolver.ts");
289
- fsSync.writeFileSync(file, 'import { Resolver, Query } from "@nestjs/graphql";\n@Resolver()\nclass UsersResolver { @Query() users() { return []; } }\n');
290
- const result = await isGraphQLFile(path.basename(file), tmpDir);
291
- expect(result).toBe(true);
292
- });
293
- it("returns false for a Nest REST controller using @Query params", async () => {
294
- const file = path.join(tmpDir, "users.controller.ts");
295
- fsSync.writeFileSync(file, 'import { Controller, Get, Query } from "@nestjs/common";\n@Controller("users")\nclass UsersController { @Get() list(@Query("page") page: string) { return page; } }\n');
296
- const result = await isGraphQLFile(path.basename(file), tmpDir);
297
- expect(result).toBe(false);
298
- });
299
- it("returns false for a regular TypeScript route file", async () => {
300
- const file = path.join(tmpDir, "graphql-test-route.ts");
301
- fsSync.writeFileSync(file, 'import express from "express";\nrouter.get("/users", handler);\n');
302
- const result = await isGraphQLFile(path.basename(file), tmpDir);
303
- expect(result).toBe(false);
304
- });
305
- it("returns false for frontend Apollo gql query templates", async () => {
306
- const file = path.join(tmpDir, "usersQuery.tsx");
307
- fsSync.writeFileSync(file, 'import { gql } from "@apollo/client";\nexport const USERS_QUERY = gql`query Users { users { id } }`;\n');
308
- const result = await isGraphQLFile(path.basename(file), tmpDir);
309
- expect(result).toBe(false);
310
- });
311
- it("returns false (does not throw) when the file does not exist", async () => {
312
- const result = await isGraphQLFile("nonexistent-file.ts", tmpDir);
313
- expect(result).toBe(false);
314
- });
315
- });
316
- describe("filterUnsupportedGraphQLEndpoints", () => {
317
- it("memoizes GraphQL source-file checks within one filter pass", async () => {
318
- const endpoints = [
319
- { path: "/users", methods: ["GET"], sourceFile: "src/routes/users.ts" },
320
- { path: "/users/{id}", methods: ["GET"], sourceFile: "src/routes/users.ts" },
321
- { path: "/orders", methods: ["GET"], sourceFile: "src/routes/orders.ts" },
322
- { path: "/graphql", methods: ["POST"], sourceFile: "src/routes/graphql.ts" },
323
- { path: "/openapi-only", methods: ["GET"], sourceFile: "" },
324
- ];
325
- const readSpy = jest.spyOn(fsModule.promises, "readFile").mockResolvedValue('router.get("/users", handler)');
326
- const result = await filterUnsupportedGraphQLEndpoints(endpoints, "/repo");
327
- expect(result.map((endpoint) => endpoint.path)).toEqual(["/users", "/users/{id}", "/orders", "/openapi-only"]);
328
- expect(readSpy).toHaveBeenCalledTimes(2);
329
- expect(readSpy.mock.calls.map(([file]) => String(file))).toEqual([
330
- path.join("/repo", "src/routes/users.ts"),
331
- path.join("/repo", "src/routes/orders.ts"),
332
- ]);
333
- readSpy.mockRestore();
334
- });
335
- });
336
- /** Register the tool against a minimal mock server and return the captured handler.
337
- * The handler takes (params, extra) — extra is pre-filled with a no-op sendNotification. */
338
- function captureAnalyzeHandler() {
339
- let capturedHandler;
340
- const mockServer = {
341
- registerTool: jest.fn((_name, _meta, handler) => {
342
- capturedHandler = handler;
343
- }),
344
- };
345
- registerAnalyzeChangesTool(mockServer);
346
- const mockExtra = { sendNotification: jest.fn().mockResolvedValue(undefined), _meta: {} };
347
- return (params) => capturedHandler(params, mockExtra);
348
- }
349
- describe("analyzeChangesTool handler — GraphQL-only early return", () => {
350
- const baseParams = {
351
- repositoryPath: "/fake/repo",
352
- scope: "branch_diff",
353
- };
354
- beforeEach(() => {
355
- jest.clearAllMocks();
356
- // Minimal diffData: two .graphql files changed, no endpoints classified
357
- computeBranchDiff.mockResolvedValue({
358
- currentBranch: "feature",
359
- baseBranch: "main",
360
- changedFiles: ["schema.graphql", "mutations.graphql"],
361
- deletedFiles: [],
362
- diffContent: "",
363
- });
364
- scanRelatedEndpoints.mockReturnValue([]);
365
- classifyEndpointsByChangedFiles.mockReturnValue({
366
- changedEndpoints: [],
367
- newEndpoints: [],
368
- removedEndpoints: [],
369
- unmatchedFiles: [],
370
- affectedServices: [],
371
- });
372
- });
373
- it("returns the GraphQL-only early-return message when all changed files are .graphql", async () => {
374
- const handler = captureAnalyzeHandler();
375
- const result = await handler(baseParams);
376
- expect(result.isError).toBeFalsy();
377
- expect(result.content[0].text).toContain("GraphQL-only diff detected");
378
- expect(result.content[0].text).toContain("schema.graphql");
379
- expect(result.content[0].text).toContain("REST API testing");
380
- });
381
- it("returns the GraphQL-only message when only GraphQL resolver endpoints are scanned", async () => {
382
- computeBranchDiff.mockResolvedValue({
383
- currentBranch: "feature",
384
- baseBranch: "main",
385
- changedFiles: ["src/graphql/users.resolver.ts"],
386
- deletedFiles: [],
387
- diffContent: "",
388
- });
389
- scanRelatedEndpoints.mockReturnValue([
390
- { path: "/users", methods: ["GET"], sourceFile: "src/graphql/users.resolver.ts" },
391
- ]);
392
- classifyEndpointsByChangedFiles.mockReturnValue({
393
- changedEndpoints: [],
394
- newEndpoints: [],
395
- removedEndpoints: [],
396
- unmatchedFiles: [],
397
- affectedServices: [],
398
- });
399
- const readSpy = jest.spyOn(fsModule.promises, "readFile").mockResolvedValue('import { Resolver, Query } from "@nestjs/graphql";\n@Resolver()\nclass UsersResolver { @Query() users() { return []; } }');
400
- const handler = captureAnalyzeHandler();
401
- const result = await handler(baseParams);
402
- expect(classifyEndpointsByChangedFiles).toHaveBeenCalledWith(expect.anything(), [], []);
403
- expect(result.content[0].text).toContain("GraphQL-only diff detected");
404
- expect(result.content[0].text).toContain("endpoint implementation");
405
- readSpy.mockRestore();
406
- });
407
- it("does NOT early-return when .graphql files are mixed with REST route files", async () => {
408
- computeBranchDiff.mockResolvedValue({
409
- currentBranch: "feature",
410
- baseBranch: "main",
411
- changedFiles: ["schema.graphql", "src/routes/users.ts"],
412
- deletedFiles: [],
413
- diffContent: "",
414
- });
415
- // users.ts is not a GraphQL file — isGraphQLFile will read it and return false
416
- const readSpy = jest.spyOn(fsModule.promises, "readFile").mockResolvedValue('router.get("/users", handler)');
417
- const handler = captureAnalyzeHandler();
418
- const result = await handler(baseParams);
419
- // Should not early-return with GraphQL message
420
- expect(result.content[0].text).not.toContain("GraphQL-only diff detected");
421
- readSpy.mockRestore();
422
- });
423
- it("does NOT early-return when scope is full_repo (only fires for PR diffs)", async () => {
424
- scanAllRepoEndpoints.mockReturnValue([]);
425
- const handler = captureAnalyzeHandler();
426
- const result = await handler({ ...baseParams, scope: "full_repo" });
427
- expect(result.content[0].text).not.toContain("GraphQL-only diff detected");
428
- });
429
- });
430
- // ─────────────────────────────────────────────────────────────────────────────
431
- // buildTraceFileEntry — traceFiles shape mapping
432
- // ─────────────────────────────────────────────────────────────────────────────
433
- describe("buildTraceFileEntry", () => {
434
- it("maps TraceParseResult to a TraceFile", () => {
435
- const parsed = {
436
- format: "har",
437
- entries: [],
438
- userFlows: [
439
- { flowId: "login-flow", entries: [], durationMs: 0 },
440
- { flowId: "checkout-flow", entries: [], durationMs: 0 },
441
- ],
442
- };
443
- const entry = buildTraceFileEntry("/repo/trace.json", parsed);
444
- expect(entry).toEqual({
445
- path: "/repo/trace.json",
446
- format: "har",
447
- analyzed: true,
448
- userFlows: ["login-flow", "checkout-flow"],
449
- });
450
- });
451
- it("returns an empty userFlows array when the trace has no flows", () => {
452
- const parsed = {
453
- format: "har",
454
- entries: [],
455
- userFlows: [],
456
- };
457
- const entry = buildTraceFileEntry("/empty.json", parsed);
458
- expect(entry.userFlows).toEqual([]);
459
- });
460
- });
461
- describe("UI blueprint-capture instructions (merged from ui_analyze_changes)", () => {
462
- it("NO_UI_INSTRUCTIONS signals no capture is needed and references no follow-up tool", () => {
463
- expect(NO_UI_INSTRUCTIONS).toMatch(/no blueprint capture needed/i);
464
- // The former pre-flight tool told the agent to "proceed to
465
- // skyramp_analyze_changes" — that's redundant now we're inside it.
466
- expect(NO_UI_INSTRUCTIONS).not.toMatch(/skyramp_analyze_changes/);
467
- });
468
- it("NO_RESOLVABLE_URLS_INSTRUCTIONS falls back to source-grounded", () => {
469
- expect(NO_RESOLVABLE_URLS_INSTRUCTIONS).toMatch(/source-grounded/i);
470
- });
471
- it("buildCaptureInstructions numbers each candidate URL with its provenance", () => {
472
- const pages = [
473
- { url: "http://localhost:3000/notifications", sourcedFrom: ["app/routes.tsx"], strategy: "framework-route-grep" },
474
- { url: "http://localhost:3000/settings", sourcedFrom: [], strategy: "root-fallback" },
475
- ];
476
- const text = buildCaptureInstructions(pages);
477
- expect(text).toContain("1. http://localhost:3000/notifications");
478
- expect(text).toContain("2. http://localhost:3000/settings");
479
- expect(text).toContain("sourcedFrom: app/routes.tsx");
480
- expect(text).toContain("sourcedFrom: (none)");
481
- expect(text).toContain("browser_navigate");
482
- expect(text).toContain("browser_blueprint");
483
- });
484
- it("buildCaptureInstructions does NOT emit a login step (uiCredentials dropped)", () => {
485
- const pages = [
486
- { url: "http://localhost:3000/x", sourcedFrom: [], strategy: "root-fallback" },
487
- ];
488
- const text = buildCaptureInstructions(pages);
489
- expect(text).not.toMatch(/log in first/i);
490
- expect(text).not.toMatch(/ui-credentials/i);
491
- });
492
- });
493
- // ─────────────────────────────────────────────────────────────────────────────
494
- // buildUnintegratedInstructions — SKYR-3855 server-side integration check.
495
- // Per .claude/CLAUDE.md, only assert structural branching (which files are
496
- // listed), not prose wording.
497
- // ─────────────────────────────────────────────────────────────────────────────
498
- describe("buildUnintegratedInstructions", () => {
499
- it("lists a single unintegrated file when only one is passed (all-unintegrated case)", () => {
500
- const text = buildUnintegratedInstructions([
501
- { file: "src/components/EntityExplorerSidebar.tsx", integrated: false, importers: [], reason: "no-importers" },
502
- ]);
503
- expect(text).toContain("src/components/EntityExplorerSidebar.tsx");
504
- expect(text).not.toContain("src/components/OtherPanel.tsx");
505
- });
506
- it("lists every file passed in — mixed-PR call sites scope this to just the unintegrated subset", () => {
507
- const text = buildUnintegratedInstructions([
508
- { file: "src/components/A.tsx", integrated: false, importers: [], reason: "no-importers" },
509
- { file: "src/components/B.tsx", integrated: false, importers: [], reason: "no-importers" },
510
- ]);
511
- expect(text).toContain("src/components/A.tsx");
512
- expect(text).toContain("src/components/B.tsx");
513
- });
514
- it("filters out integrated files even if accidentally included by a caller", () => {
515
- const text = buildUnintegratedInstructions([
516
- { file: "src/components/Unintegrated.tsx", integrated: false, importers: [], reason: "no-importers" },
517
- { file: "src/pages/Dashboard.tsx", integrated: true, importers: ["src/App.tsx"], reason: "imported" },
518
- ]);
519
- expect(text).toContain("src/components/Unintegrated.tsx");
520
- expect(text).not.toContain("src/pages/Dashboard.tsx");
521
- });
522
- });