github-issue-tower-defence-management 2.19.1 → 2.19.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 (780) hide show
  1. package/bin/adapter/entry-points/cli/index.js +0 -0
  2. package/package.json +5 -1
  3. package/.env.example +0 -7
  4. package/.gitattributes +0 -2
  5. package/.github/CODEOWNERS +0 -6
  6. package/.github/copilot-instructions.md +0 -6
  7. package/.github/dependabot.yml +0 -19
  8. package/.github/instructions/code-review.instructions.md +0 -50
  9. package/.github/workflows/api-created_issue_pr.yml +0 -97
  10. package/.github/workflows/ci-failure-issue-creator.yml +0 -48
  11. package/.github/workflows/close-manual-prs.yml +0 -80
  12. package/.github/workflows/codeql.yml +0 -107
  13. package/.github/workflows/commit-lint.yml +0 -85
  14. package/.github/workflows/configs/commitlint.config.cjs +0 -32
  15. package/.github/workflows/configs/commitlint.config.js +0 -32
  16. package/.github/workflows/console-ui.yml +0 -68
  17. package/.github/workflows/create-pr.yml +0 -116
  18. package/.github/workflows/empty-format-test-job.yml +0 -34
  19. package/.github/workflows/format.yml +0 -28
  20. package/.github/workflows/publish.yml +0 -111
  21. package/.github/workflows/secret-scan.yml +0 -51
  22. package/.github/workflows/test.yml +0 -58
  23. package/.github/workflows/umino-project.yml +0 -207
  24. package/.gitleaks.toml +0 -22
  25. package/.prettierignore +0 -31
  26. package/.prettierrc +0 -5
  27. package/CHANGELOG.md +0 -4162
  28. package/CONTRIBUTING.md +0 -60
  29. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js +0 -12
  30. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js.map +0 -1
  31. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js +0 -125
  32. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js.map +0 -1
  33. package/bin/adapter/repositories/issue/issueTimelineUtils.js +0 -38
  34. package/bin/adapter/repositories/issue/issueTimelineUtils.js.map +0 -1
  35. package/bin/domain/entities/UnansweredOwnerCall.js +0 -3
  36. package/bin/domain/entities/UnansweredOwnerCall.js.map +0 -1
  37. package/bin/domain/entities/WorkingTime.js +0 -3
  38. package/bin/domain/entities/WorkingTime.js.map +0 -1
  39. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js +0 -37
  40. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js.map +0 -1
  41. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +0 -118
  42. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +0 -1
  43. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js +0 -180
  44. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js.map +0 -1
  45. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js +0 -160
  46. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js.map +0 -1
  47. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js +0 -3
  48. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js.map +0 -1
  49. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js +0 -7
  50. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js.map +0 -1
  51. package/bin/domain/usecases/findLastAgentDeclaringReport.js +0 -12
  52. package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +0 -1
  53. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js +0 -25
  54. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js.map +0 -1
  55. package/bin/domain/usecases/isWaitingForOwnerApproval.js +0 -16
  56. package/bin/domain/usecases/isWaitingForOwnerApproval.js.map +0 -1
  57. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js +0 -7
  58. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js.map +0 -1
  59. package/commitlint.config.js +0 -6
  60. package/eslint.config.mjs +0 -76
  61. package/jest.config.js +0 -84
  62. package/jest.setup.js +0 -1
  63. package/lib/buffer-equal-constant-time-compat/index.js +0 -17
  64. package/lib/buffer-equal-constant-time-compat/index.test.js +0 -54
  65. package/lib/buffer-equal-constant-time-compat/package.json +0 -5
  66. package/lib/typescript-compat/bin/tsc +0 -2
  67. package/lib/typescript-compat/bin/tsserver +0 -2
  68. package/lib/typescript-compat/lib/typescript.js +0 -1
  69. package/lib/typescript-compat/package.json +0 -13
  70. package/renovate.json +0 -46
  71. package/scripts/copyConsoleUiDist.mjs +0 -35
  72. package/scripts/defaultBranchTipVerify.sh +0 -23
  73. package/scripts/testWorkflowRunVerify.sh +0 -47
  74. package/scripts/verifyConsoleUiDist.mjs +0 -96
  75. package/src/adapter/ci/consoleUiWorkflowTimeout.test.ts +0 -61
  76. package/src/adapter/ci/publishTestWorkflowGate.test.ts +0 -653
  77. package/src/adapter/ci/workflowRunCancellation.test.ts +0 -72
  78. package/src/adapter/entry-points/cli/fleetConfig.test.ts +0 -553
  79. package/src/adapter/entry-points/cli/fleetConfig.ts +0 -266
  80. package/src/adapter/entry-points/cli/index.test.ts +0 -2837
  81. package/src/adapter/entry-points/cli/index.ts +0 -1488
  82. package/src/adapter/entry-points/cli/projectConfig.test.ts +0 -342
  83. package/src/adapter/entry-points/cli/projectConfig.ts +0 -500
  84. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.test.ts +0 -658
  85. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.ts +0 -482
  86. package/src/adapter/entry-points/console/consoleDataDelivery.test.ts +0 -222
  87. package/src/adapter/entry-points/console/consoleDataDelivery.ts +0 -134
  88. package/src/adapter/entry-points/console/consoleDoneStore.test.ts +0 -169
  89. package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -139
  90. package/src/adapter/entry-points/console/consoleGithubTokenResolver.test.ts +0 -275
  91. package/src/adapter/entry-points/console/consoleGithubTokenResolver.ts +0 -113
  92. package/src/adapter/entry-points/console/consoleImageProxy.test.ts +0 -140
  93. package/src/adapter/entry-points/console/consoleImageProxy.ts +0 -111
  94. package/src/adapter/entry-points/console/consoleItemUrlLookup.test.ts +0 -63
  95. package/src/adapter/entry-points/console/consoleItemUrlLookup.ts +0 -66
  96. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +0 -2523
  97. package/src/adapter/entry-points/console/consoleOperationApi.ts +0 -936
  98. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +0 -246
  99. package/src/adapter/entry-points/console/consoleProjectResolver.ts +0 -111
  100. package/src/adapter/entry-points/console/consoleReadApi.test.ts +0 -714
  101. package/src/adapter/entry-points/console/consoleReadApi.ts +0 -361
  102. package/src/adapter/entry-points/console/consoleTabNames.test.ts +0 -52
  103. package/src/adapter/entry-points/console/consoleTabNames.ts +0 -37
  104. package/src/adapter/entry-points/console/consoleUiDistSync.test.ts +0 -66
  105. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +0 -595
  106. package/src/adapter/entry-points/console/dashboardComposeService.ts +0 -256
  107. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +0 -180
  108. package/src/adapter/entry-points/console/ensureConsoleRunning.test.ts +0 -115
  109. package/src/adapter/entry-points/console/ensureConsoleRunning.ts +0 -64
  110. package/src/adapter/entry-points/console/ui/.storybook/main.ts +0 -12
  111. package/src/adapter/entry-points/console/ui/.storybook/preview.ts +0 -15
  112. package/src/adapter/entry-points/console/ui/biome.json +0 -53
  113. package/src/adapter/entry-points/console/ui/components.json +0 -20
  114. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +0 -725
  115. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +0 -831
  116. package/src/adapter/entry-points/console/ui/e2e/playwright.config.ts +0 -30
  117. package/src/adapter/entry-points/console/ui/e2e/tsconfig.json +0 -18
  118. package/src/adapter/entry-points/console/ui/index.html +0 -12
  119. package/src/adapter/entry-points/console/ui/jest-globals.cjs +0 -26
  120. package/src/adapter/entry-points/console/ui/jest.setup.ts +0 -1
  121. package/src/adapter/entry-points/console/ui/jest.styleMock.js +0 -1
  122. package/src/adapter/entry-points/console/ui/public/sw.js +0 -60
  123. package/src/adapter/entry-points/console/ui/src/components/ui/badge.stories.tsx +0 -35
  124. package/src/adapter/entry-points/console/ui/src/components/ui/badge.tsx +0 -28
  125. package/src/adapter/entry-points/console/ui/src/components/ui/button.stories.tsx +0 -34
  126. package/src/adapter/entry-points/console/ui/src/components/ui/button.tsx +0 -50
  127. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.stories.tsx +0 -28
  128. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.test.tsx +0 -65
  129. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.tsx +0 -19
  130. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +0 -61
  131. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +0 -199
  132. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +0 -166
  133. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.stories.tsx +0 -22
  134. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.test.tsx +0 -74
  135. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.tsx +0 -84
  136. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.stories.tsx +0 -27
  137. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.test.tsx +0 -85
  138. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.tsx +0 -97
  139. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +0 -76
  140. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +0 -56
  141. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +0 -39
  142. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.stories.tsx +0 -32
  143. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.test.tsx +0 -105
  144. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.tsx +0 -157
  145. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +0 -68
  146. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +0 -612
  147. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +0 -276
  148. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.stories.tsx +0 -33
  149. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.test.tsx +0 -57
  150. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +0 -76
  151. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.stories.tsx +0 -25
  152. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.test.tsx +0 -55
  153. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.tsx +0 -49
  154. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.stories.tsx +0 -24
  155. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.test.tsx +0 -63
  156. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.tsx +0 -19
  157. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.stories.tsx +0 -33
  158. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.test.tsx +0 -62
  159. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.tsx +0 -59
  160. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.stories.tsx +0 -38
  161. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.test.tsx +0 -151
  162. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.tsx +0 -245
  163. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +0 -514
  164. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +0 -727
  165. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +0 -387
  166. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.stories.tsx +0 -82
  167. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.test.tsx +0 -52
  168. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.tsx +0 -32
  169. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.stories.tsx +0 -48
  170. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +0 -235
  171. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +0 -145
  172. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.stories.tsx +0 -29
  173. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.test.tsx +0 -30
  174. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.tsx +0 -50
  175. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +0 -38
  176. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +0 -54
  177. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +0 -60
  178. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.stories.tsx +0 -56
  179. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.test.tsx +0 -111
  180. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.tsx +0 -92
  181. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.stories.tsx +0 -51
  182. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.test.tsx +0 -59
  183. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.tsx +0 -39
  184. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +0 -207
  185. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +0 -332
  186. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +0 -211
  187. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.stories.tsx +0 -49
  188. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.test.tsx +0 -157
  189. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.tsx +0 -118
  190. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.stories.tsx +0 -23
  191. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.test.tsx +0 -40
  192. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.tsx +0 -17
  193. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.stories.tsx +0 -101
  194. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.test.tsx +0 -102
  195. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.tsx +0 -75
  196. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.stories.tsx +0 -40
  197. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.test.tsx +0 -213
  198. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.tsx +0 -115
  199. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.stories.tsx +0 -50
  200. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.test.tsx +0 -98
  201. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.tsx +0 -66
  202. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.stories.tsx +0 -69
  203. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.test.tsx +0 -101
  204. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.tsx +0 -75
  205. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.stories.tsx +0 -137
  206. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.test.tsx +0 -554
  207. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.tsx +0 -400
  208. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.stories.tsx +0 -30
  209. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.test.tsx +0 -111
  210. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.tsx +0 -91
  211. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.stories.tsx +0 -27
  212. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.test.tsx +0 -24
  213. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.tsx +0 -28
  214. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.stories.tsx +0 -14
  215. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.test.tsx +0 -21
  216. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +0 -26
  217. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +0 -14
  218. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +0 -40
  219. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +0 -37
  220. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleErrorToast.stories.tsx +0 -33
  221. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.stories.tsx +0 -20
  222. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +0 -48
  223. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +0 -42
  224. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.stories.tsx +0 -115
  225. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.test.tsx +0 -262
  226. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.tsx +0 -123
  227. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +0 -65
  228. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +0 -159
  229. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +0 -55
  230. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.stories.tsx +0 -20
  231. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.test.tsx +0 -93
  232. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.tsx +0 -45
  233. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.stories.tsx +0 -17
  234. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.test.tsx +0 -49
  235. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.tsx +0 -66
  236. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.stories.tsx +0 -47
  237. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.test.tsx +0 -95
  238. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.tsx +0 -62
  239. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.stories.tsx +0 -32
  240. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.test.tsx +0 -101
  241. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.tsx +0 -80
  242. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.test.ts +0 -304
  243. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.ts +0 -167
  244. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.test.ts +0 -498
  245. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.ts +0 -277
  246. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +0 -99
  247. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +0 -90
  248. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +0 -110
  249. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +0 -83
  250. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.test.ts +0 -51
  251. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.ts +0 -39
  252. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +0 -229
  253. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +0 -194
  254. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +0 -151
  255. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.ts +0 -128
  256. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +0 -629
  257. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +0 -417
  258. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +0 -40
  259. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +0 -71
  260. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.test.ts +0 -24
  261. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.ts +0 -17
  262. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.test.ts +0 -97
  263. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.ts +0 -44
  264. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.test.ts +0 -59
  265. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.ts +0 -26
  266. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +0 -48
  267. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +0 -39
  268. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +0 -41
  269. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +0 -57
  270. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.test.ts +0 -115
  271. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.ts +0 -115
  272. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +0 -546
  273. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +0 -278
  274. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.test.ts +0 -110
  275. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.ts +0 -70
  276. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.test.ts +0 -387
  277. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.ts +0 -407
  278. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +0 -446
  279. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +0 -533
  280. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.test.ts +0 -78
  281. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.ts +0 -34
  282. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +0 -252
  283. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +0 -207
  284. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +0 -27
  285. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +0 -71
  286. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.test.ts +0 -88
  287. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.ts +0 -34
  288. package/src/adapter/entry-points/console/ui/src/features/console/lib/navigation.ts +0 -7
  289. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +0 -104
  290. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +0 -79
  291. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +0 -200
  292. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +0 -122
  293. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.test.ts +0 -90
  294. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.ts +0 -37
  295. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.test.ts +0 -34
  296. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.ts +0 -85
  297. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.test.ts +0 -81
  298. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.ts +0 -61
  299. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.test.ts +0 -35
  300. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.ts +0 -21
  301. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.test.ts +0 -96
  302. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.ts +0 -93
  303. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +0 -229
  304. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.ts +0 -106
  305. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.test.ts +0 -18
  306. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.ts +0 -16
  307. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.test.ts +0 -97
  308. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.ts +0 -95
  309. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.test.ts +0 -53
  310. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.ts +0 -33
  311. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.test.ts +0 -72
  312. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +0 -61
  313. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +0 -325
  314. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +0 -94
  315. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.test.ts +0 -44
  316. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.ts +0 -17
  317. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +0 -103
  318. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +0 -64
  319. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.test.ts +0 -74
  320. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.ts +0 -53
  321. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.test.ts +0 -34
  322. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.ts +0 -28
  323. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +0 -89
  324. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.ts +0 -30
  325. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.test.ts +0 -61
  326. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.ts +0 -77
  327. package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -173
  328. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +0 -629
  329. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +0 -351
  330. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +0 -1469
  331. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +0 -736
  332. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +0 -25
  333. package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +0 -418
  334. package/src/adapter/entry-points/console/ui/src/index.css +0 -1592
  335. package/src/adapter/entry-points/console/ui/src/lib/utils.ts +0 -4
  336. package/src/adapter/entry-points/console/ui/src/main.tsx +0 -21
  337. package/src/adapter/entry-points/console/ui/src/vite-env.d.ts +0 -1
  338. package/src/adapter/entry-points/console/ui/tsconfig.json +0 -26
  339. package/src/adapter/entry-points/console/ui/vite.config.ts +0 -24
  340. package/src/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +0 -88
  341. package/src/adapter/entry-points/console/ui-dist/assets/index-KyQISRuQ.css +0 -2
  342. package/src/adapter/entry-points/console/ui-dist/index.html +0 -13
  343. package/src/adapter/entry-points/console/ui-dist/sw.js +0 -60
  344. package/src/adapter/entry-points/console/webServer.test.ts +0 -2945
  345. package/src/adapter/entry-points/console/webServer.ts +0 -940
  346. package/src/adapter/entry-points/function/getStoryObjectMap.ts +0 -3
  347. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.test.ts +0 -218
  348. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.ts +0 -113
  349. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +0 -110
  350. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +0 -116
  351. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -859
  352. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -984
  353. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +0 -126
  354. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts +0 -80
  355. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +0 -491
  356. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +0 -137
  357. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +0 -508
  358. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +0 -156
  359. package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +0 -332
  360. package/src/adapter/entry-points/handlers/consoleListsWriter.ts +0 -60
  361. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +0 -112
  362. package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +0 -51
  363. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +0 -285
  364. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -109
  365. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +0 -170
  366. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts +0 -40
  367. package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +0 -191
  368. package/src/adapter/entry-points/handlers/machineStatusWriter.ts +0 -110
  369. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +0 -548
  370. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +0 -192
  371. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +0 -111
  372. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.ts +0 -25
  373. package/src/adapter/entry-points/handlers/ownerCallFileStore.test.ts +0 -286
  374. package/src/adapter/entry-points/handlers/ownerCallFileStore.ts +0 -145
  375. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.test.ts +0 -55
  376. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.ts +0 -36
  377. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.test.ts +0 -72
  378. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +0 -74
  379. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +0 -181
  380. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +0 -18
  381. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +0 -489
  382. package/src/adapter/entry-points/handlers/situationFileWriter.ts +0 -176
  383. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +0 -169
  384. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +0 -32
  385. package/src/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.ts +0 -41
  386. package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +0 -81
  387. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +0 -266
  388. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +0 -262
  389. package/src/adapter/proxy/ClaudeMessageResponseParser.test.ts +0 -211
  390. package/src/adapter/proxy/ClaudeMessageResponseParser.ts +0 -181
  391. package/src/adapter/proxy/RateLimitCache.test.ts +0 -1077
  392. package/src/adapter/proxy/RateLimitCache.ts +0 -385
  393. package/src/adapter/proxy/TokenListLoader.test.ts +0 -183
  394. package/src/adapter/proxy/TokenListLoader.ts +0 -75
  395. package/src/adapter/proxy/ensureProxyRunning.test.ts +0 -85
  396. package/src/adapter/proxy/ensureProxyRunning.ts +0 -41
  397. package/src/adapter/proxy/proxyEntry.test.ts +0 -552
  398. package/src/adapter/proxy/proxyEntry.ts +0 -214
  399. package/src/adapter/repositories/BaseGitHubRepository.test.ts +0 -26
  400. package/src/adapter/repositories/BaseGitHubRepository.ts +0 -26
  401. package/src/adapter/repositories/BrowserGitHubProjectRepository.test.ts +0 -108
  402. package/src/adapter/repositories/BrowserGitHubProjectRepository.ts +0 -118
  403. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.test.ts +0 -71
  404. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.ts +0 -21
  405. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +0 -324
  406. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +0 -148
  407. package/src/adapter/repositories/FetchWebhookRepository.ts +0 -8
  408. package/src/adapter/repositories/FileHandoverStateRepository.test.ts +0 -114
  409. package/src/adapter/repositories/FileHandoverStateRepository.ts +0 -73
  410. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +0 -514
  411. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +0 -109
  412. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.test.ts +0 -284
  413. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.ts +0 -128
  414. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.test.ts +0 -138
  415. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts +0 -113
  416. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.test.ts +0 -121
  417. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts +0 -103
  418. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +0 -360
  419. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +0 -77
  420. package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +0 -59
  421. package/src/adapter/repositories/FileSystemSessionRecordReader.ts +0 -31
  422. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +0 -287
  423. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +0 -125
  424. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.test.ts +0 -285
  425. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts +0 -119
  426. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.test.ts +0 -237
  427. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts +0 -57
  428. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.test.ts +0 -56
  429. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.ts +0 -32
  430. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.test.ts +0 -68
  431. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.ts +0 -23
  432. package/src/adapter/repositories/GitHubIssueCheckpointRepository.test.ts +0 -51
  433. package/src/adapter/repositories/GitHubIssueCheckpointRepository.ts +0 -47
  434. package/src/adapter/repositories/GitHubIssueCommentRepository.test.ts +0 -278
  435. package/src/adapter/repositories/GitHubIssueCommentRepository.ts +0 -109
  436. package/src/adapter/repositories/GoogleSpreadsheetRepository.integration.test.ts +0 -265
  437. package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +0 -431
  438. package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +0 -268
  439. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +0 -272
  440. package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -111
  441. package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +0 -254
  442. package/src/adapter/repositories/GraphqlProjectRepository.test.ts +0 -209
  443. package/src/adapter/repositories/GraphqlProjectRepository.ts +0 -707
  444. package/src/adapter/repositories/GraphqlProjectRepositoryProjectLocation.test.ts +0 -297
  445. package/src/adapter/repositories/KyHttpRepository.ts +0 -8
  446. package/src/adapter/repositories/KySlackRepository.test.ts +0 -172
  447. package/src/adapter/repositories/KySlackRepository.ts +0 -298
  448. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.test.ts +0 -193
  449. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.ts +0 -106
  450. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +0 -190
  451. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +0 -102
  452. package/src/adapter/repositories/LocalStorageCacheRepository.test.ts +0 -265
  453. package/src/adapter/repositories/LocalStorageCacheRepository.ts +0 -79
  454. package/src/adapter/repositories/LocalStorageRepository.integration.test.ts +0 -142
  455. package/src/adapter/repositories/LocalStorageRepository.test.ts +0 -173
  456. package/src/adapter/repositories/LocalStorageRepository.ts +0 -30
  457. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +0 -15
  458. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +0 -9
  459. package/src/adapter/repositories/NodeLocalCommandRunner.largeOutput.test.ts +0 -16
  460. package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +0 -118
  461. package/src/adapter/repositories/NodeLocalCommandRunner.ts +0 -66
  462. package/src/adapter/repositories/NodeProcessSignalRepository.test.ts +0 -82
  463. package/src/adapter/repositories/NodeProcessSignalRepository.ts +0 -47
  464. package/src/adapter/repositories/NodeSubAgentProcessLister.test.ts +0 -44
  465. package/src/adapter/repositories/NodeSubAgentProcessLister.ts +0 -40
  466. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +0 -546
  467. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +0 -245
  468. package/src/adapter/repositories/OauthAPIClaudeRepository.ts +0 -279
  469. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.test.ts +0 -166
  470. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.ts +0 -32
  471. package/src/adapter/repositories/OauthProxyClaudeRepository.test.ts +0 -428
  472. package/src/adapter/repositories/OauthProxyClaudeRepository.ts +0 -124
  473. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.test.ts +0 -242
  474. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.ts +0 -261
  475. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.test.ts +0 -184
  476. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts +0 -138
  477. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.test.ts +0 -208
  478. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.ts +0 -130
  479. package/src/adapter/repositories/ProcFsProcessEnvironReader.test.ts +0 -60
  480. package/src/adapter/repositories/ProcFsProcessEnvironReader.ts +0 -38
  481. package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +0 -207
  482. package/src/adapter/repositories/ProcHostMetricsRepository.ts +0 -181
  483. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +0 -231
  484. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +0 -134
  485. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.test.ts +0 -121
  486. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.ts +0 -58
  487. package/src/adapter/repositories/ProjectIssuesCacheRepository.test.ts +0 -218
  488. package/src/adapter/repositories/ProjectIssuesCacheRepository.ts +0 -141
  489. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +0 -1195
  490. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +0 -185
  491. package/src/adapter/repositories/ProxyRateLimitCacheRepository.test.ts +0 -116
  492. package/src/adapter/repositories/ProxyRateLimitCacheRepository.ts +0 -67
  493. package/src/adapter/repositories/RateLimitSnapshotRepository.ts +0 -50
  494. package/src/adapter/repositories/RealSleeper.test.ts +0 -19
  495. package/src/adapter/repositories/RealSleeper.ts +0 -12
  496. package/src/adapter/repositories/RestProjectRepository.test.ts +0 -277
  497. package/src/adapter/repositories/RestProjectRepository.ts +0 -129
  498. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.test.ts +0 -313
  499. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.ts +0 -164
  500. package/src/adapter/repositories/SystemDateRepository.test.ts +0 -60
  501. package/src/adapter/repositories/SystemDateRepository.ts +0 -34
  502. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +0 -253
  503. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +0 -158
  504. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.queuedReply.test.ts +0 -171
  505. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +0 -86
  506. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -996
  507. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -396
  508. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.test.ts +0 -208
  509. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.ts +0 -80
  510. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +0 -1674
  511. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +0 -459
  512. package/src/adapter/repositories/clSessionScopeUnitName.test.ts +0 -76
  513. package/src/adapter/repositories/clSessionScopeUnitName.ts +0 -5
  514. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.test.ts +0 -34
  515. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.ts +0 -8
  516. package/src/adapter/repositories/githubGraphqlClient.test.ts +0 -479
  517. package/src/adapter/repositories/githubGraphqlClient.ts +0 -243
  518. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +0 -5182
  519. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +0 -3303
  520. package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -46
  521. package/src/adapter/repositories/issue/ApiV3IssueRepository.ts +0 -88
  522. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -1776
  523. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +0 -1552
  524. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -393
  525. package/src/adapter/repositories/issue/RestIssueRepository.ts +0 -252
  526. package/src/adapter/repositories/issue/gitHubRawUrl.test.ts +0 -29
  527. package/src/adapter/repositories/issue/gitHubRawUrl.ts +0 -30
  528. package/src/adapter/repositories/issue/githubRateLimitRetry.test.ts +0 -167
  529. package/src/adapter/repositories/issue/githubRateLimitRetry.ts +0 -109
  530. package/src/adapter/repositories/localStorageCacheDirectory.test.ts +0 -81
  531. package/src/adapter/repositories/localStorageCacheDirectory.ts +0 -13
  532. package/src/adapter/repositories/projectFieldDefinition.test.ts +0 -191
  533. package/src/adapter/repositories/projectFieldDefinition.ts +0 -144
  534. package/src/adapter/repositories/utils.test.ts +0 -55
  535. package/src/adapter/repositories/utils.ts +0 -45
  536. package/src/domain/entities/ClaudeHandoverSession.ts +0 -12
  537. package/src/domain/entities/ClaudeMessageResponse.ts +0 -31
  538. package/src/domain/entities/ClaudeTokenUsage.ts +0 -17
  539. package/src/domain/entities/ClaudeWindowUsage.ts +0 -5
  540. package/src/domain/entities/Comment.ts +0 -5
  541. package/src/domain/entities/InteractiveLiveSession.ts +0 -6
  542. package/src/domain/entities/Issue.ts +0 -31
  543. package/src/domain/entities/KevReportWatermark.ts +0 -4
  544. package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -14
  545. package/src/domain/entities/LiveSessionOutputActivity.ts +0 -4
  546. package/src/domain/entities/LiveSessionProcessSnapshot.ts +0 -18
  547. package/src/domain/entities/LiveTmuxSession.ts +0 -4
  548. package/src/domain/entities/Member.ts +0 -3
  549. package/src/domain/entities/OutputDegeneration.ts +0 -10
  550. package/src/domain/entities/Project.ts +0 -88
  551. package/src/domain/entities/ProjectField.ts +0 -3
  552. package/src/domain/entities/ProjectFieldName.ts +0 -2
  553. package/src/domain/entities/ProjectFieldSingleSelect.ts +0 -8
  554. package/src/domain/entities/ProjectFieldSingleSelectOption.ts +0 -8
  555. package/src/domain/entities/RequiredProjectField.ts +0 -68
  556. package/src/domain/entities/SearchedIssue.ts +0 -11
  557. package/src/domain/entities/StoryObjectMap.ts +0 -12
  558. package/src/domain/entities/TokenExhaustionHandoverState.ts +0 -8
  559. package/src/domain/entities/WorkflowStatus.ts +0 -66
  560. package/src/domain/usecases/ActionAnnouncementUseCase.test.ts +0 -91
  561. package/src/domain/usecases/ActionAnnouncementUseCase.ts +0 -78
  562. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.test.ts +0 -222
  563. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.ts +0 -76
  564. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.test.ts +0 -118
  565. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.ts +0 -112
  566. package/src/domain/usecases/AnalyzeStoriesUseCase.test.ts +0 -82
  567. package/src/domain/usecases/AnalyzeStoriesUseCase.ts +0 -296
  568. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.test.ts +0 -578
  569. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts +0 -137
  570. package/src/domain/usecases/CONTRIBUTING_FOR_TEST.md +0 -259
  571. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.test.ts +0 -437
  572. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.ts +0 -80
  573. package/src/domain/usecases/ChangeTargetPullRequestApprover.test.ts +0 -248
  574. package/src/domain/usecases/ChangeTargetPullRequestApprover.ts +0 -69
  575. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +0 -411
  576. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +0 -142
  577. package/src/domain/usecases/ClearDependedIssueURLUseCase.test.ts +0 -1120
  578. package/src/domain/usecases/ClearDependedIssueURLUseCase.ts +0 -161
  579. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.test.ts +0 -345
  580. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.ts +0 -81
  581. package/src/domain/usecases/CliErrorReportUseCase.test.ts +0 -162
  582. package/src/domain/usecases/CliErrorReportUseCase.ts +0 -72
  583. package/src/domain/usecases/ConflictedIssueRevertUseCase.test.ts +0 -886
  584. package/src/domain/usecases/ConflictedIssueRevertUseCase.ts +0 -146
  585. package/src/domain/usecases/CreateEstimationIssueUseCase.test.ts +0 -227
  586. package/src/domain/usecases/CreateEstimationIssueUseCase.ts +0 -205
  587. package/src/domain/usecases/CreateNewStoryByLabelUseCase.test.ts +0 -1084
  588. package/src/domain/usecases/CreateNewStoryByLabelUseCase.ts +0 -146
  589. package/src/domain/usecases/DailySecurityScanUseCase.test.ts +0 -1709
  590. package/src/domain/usecases/DailySecurityScanUseCase.ts +0 -476
  591. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +0 -533
  592. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +0 -122
  593. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +0 -330
  594. package/src/domain/usecases/GetStoryObjectMapUseCase.ts +0 -79
  595. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -1913
  596. package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -846
  597. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +0 -175
  598. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts +0 -63
  599. package/src/domain/usecases/IssueNoStatusUpdateUseCase.test.ts +0 -165
  600. package/src/domain/usecases/IssueNoStatusUpdateUseCase.ts +0 -44
  601. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +0 -1137
  602. package/src/domain/usecases/IssueRejectionEvaluator.ts +0 -340
  603. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +0 -679
  604. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +0 -178
  605. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +0 -5441
  606. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +0 -891
  607. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +0 -2069
  608. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +0 -489
  609. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +0 -662
  610. package/src/domain/usecases/OauthTokenSelectUseCase.ts +0 -274
  611. package/src/domain/usecases/OutputDegenerationDetector.test.ts +0 -167
  612. package/src/domain/usecases/OutputDegenerationDetector.ts +0 -169
  613. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.test.ts +0 -711
  614. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.ts +0 -146
  615. package/src/domain/usecases/QualityCheckAdvanceUseCase.test.ts +0 -421
  616. package/src/domain/usecases/QualityCheckAdvanceUseCase.ts +0 -76
  617. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.test.ts +0 -188
  618. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.ts +0 -57
  619. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +0 -303
  620. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +0 -155
  621. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +0 -1838
  622. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -221
  623. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +0 -2392
  624. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +0 -499
  625. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.test.ts +0 -224
  626. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts +0 -142
  627. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +0 -360
  628. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +0 -70
  629. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.test.ts +0 -400
  630. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.ts +0 -48
  631. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +0 -696
  632. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +0 -201
  633. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +0 -1363
  634. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +0 -215
  635. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.test.ts +0 -170
  636. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.ts +0 -38
  637. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +0 -283
  638. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +0 -98
  639. package/src/domain/usecases/StartPreparationUseCase.test.ts +0 -7822
  640. package/src/domain/usecases/StartPreparationUseCase.ts +0 -876
  641. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.test.ts +0 -200
  642. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.ts +0 -74
  643. package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +0 -838
  644. package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +0 -439
  645. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.test.ts +0 -306
  646. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.ts +0 -64
  647. package/src/domain/usecases/UpdateRateLimitCacheUseCase.test.ts +0 -178
  648. package/src/domain/usecases/UpdateRateLimitCacheUseCase.ts +0 -22
  649. package/src/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.ts +0 -5
  650. package/src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts +0 -9
  651. package/src/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.ts +0 -8
  652. package/src/domain/usecases/adapter-interfaces/ClaudeMessageResponseRepository.ts +0 -5
  653. package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +0 -6
  654. package/src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts +0 -8
  655. package/src/domain/usecases/adapter-interfaces/ConsoleTabsRepository.ts +0 -12
  656. package/src/domain/usecases/adapter-interfaces/CopilotRepository.ts +0 -3
  657. package/src/domain/usecases/adapter-interfaces/DateRepository.ts +0 -7
  658. package/src/domain/usecases/adapter-interfaces/GitHubGraphqlRateLimitRepository.ts +0 -3
  659. package/src/domain/usecases/adapter-interfaces/HttpRepository.ts +0 -3
  660. package/src/domain/usecases/adapter-interfaces/InteractiveLiveSessionTranscriptResolver.ts +0 -7
  661. package/src/domain/usecases/adapter-interfaces/IssueAttachmentRepository.ts +0 -9
  662. package/src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts +0 -3
  663. package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +0 -7
  664. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +0 -229
  665. package/src/domain/usecases/adapter-interfaces/KevReportWatermarkRepository.ts +0 -11
  666. package/src/domain/usecases/adapter-interfaces/LiveSessionProcessSnapshotProvider.ts +0 -5
  667. package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +0 -16
  668. package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +0 -5
  669. package/src/domain/usecases/adapter-interfaces/ProcessEnvironReader.ts +0 -3
  670. package/src/domain/usecases/adapter-interfaces/ProcessSignalRepository.ts +0 -5
  671. package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +0 -29
  672. package/src/domain/usecases/adapter-interfaces/RateLimitCacheRepository.ts +0 -10
  673. package/src/domain/usecases/adapter-interfaces/RefusalTailStatusProvider.ts +0 -5
  674. package/src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts +0 -6
  675. package/src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts +0 -4
  676. package/src/domain/usecases/adapter-interfaces/SessionOutputActivityRepository.ts +0 -7
  677. package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +0 -3
  678. package/src/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.ts +0 -8
  679. package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +0 -10
  680. package/src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts +0 -20
  681. package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +0 -18
  682. package/src/domain/usecases/adapter-interfaces/SilentSessionNotificationRepository.ts +0 -6
  683. package/src/domain/usecases/adapter-interfaces/SlackRepository.ts +0 -20
  684. package/src/domain/usecases/adapter-interfaces/Sleeper.ts +0 -3
  685. package/src/domain/usecases/adapter-interfaces/SpreadsheetRepository.ts +0 -18
  686. package/src/domain/usecases/adapter-interfaces/StatusDefaultRepository.ts +0 -5
  687. package/src/domain/usecases/adapter-interfaces/SubAgentLivenessResolver.ts +0 -6
  688. package/src/domain/usecases/adapter-interfaces/SubAgentProcessLister.ts +0 -8
  689. package/src/domain/usecases/adapter-interfaces/SubAgentSilentSecondsResolver.ts +0 -3
  690. package/src/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.ts +0 -6
  691. package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +0 -9
  692. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +0 -20
  693. package/src/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.ts +0 -6
  694. package/src/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.ts +0 -22
  695. package/src/domain/usecases/adapter-interfaces/WebhookRepository.ts +0 -3
  696. package/src/domain/usecases/agentReportPrefix.ts +0 -1
  697. package/src/domain/usecases/autoStatusCheckComments.ts +0 -1
  698. package/src/domain/usecases/console/.gitkeep +0 -0
  699. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +0 -887
  700. package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -368
  701. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +0 -654
  702. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +0 -318
  703. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +0 -34
  704. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +0 -27
  705. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +0 -160
  706. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -69
  707. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +0 -362
  708. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +0 -234
  709. package/src/domain/usecases/ensureAgentOptionAndGetId.ts +0 -47
  710. package/src/domain/usecases/extractFencedJsonBlocks.test.ts +0 -40
  711. package/src/domain/usecases/extractFencedJsonBlocks.ts +0 -21
  712. package/src/domain/usecases/extractNextStepAgent.test.ts +0 -58
  713. package/src/domain/usecases/extractNextStepAgent.ts +0 -18
  714. package/src/domain/usecases/extractNextStepAgentFromComments.ts +0 -13
  715. package/src/domain/usecases/findLastAgentReport.test.ts +0 -121
  716. package/src/domain/usecases/findLastAgentReport.ts +0 -14
  717. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -339
  718. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +0 -188
  719. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +0 -389
  720. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts +0 -103
  721. package/src/domain/usecases/intmux/OwnerCallFile.test.ts +0 -184
  722. package/src/domain/usecases/intmux/OwnerCallFile.ts +0 -80
  723. package/src/domain/usecases/isAgentReportBody.test.ts +0 -108
  724. package/src/domain/usecases/isAgentReportBody.ts +0 -52
  725. package/src/domain/usecases/isAuthorAuthorizedForAutoStatusCheck.ts +0 -12
  726. package/src/domain/usecases/isHumanComment.test.ts +0 -77
  727. package/src/domain/usecases/isHumanComment.ts +0 -23
  728. package/src/domain/usecases/isRecord.ts +0 -2
  729. package/src/domain/usecases/issueReactivationTriggerIsPending.test.ts +0 -541
  730. package/src/domain/usecases/issueReactivationTriggerIsPending.ts +0 -25
  731. package/src/domain/usecases/nextStepAgentDispatchRepeatedMessage.ts +0 -2
  732. package/src/domain/usecases/normalizeReportBody.test.ts +0 -24
  733. package/src/domain/usecases/normalizeReportBody.ts +0 -2
  734. package/src/domain/usecases/resolveAllowedIssueAuthors.test.ts +0 -51
  735. package/src/domain/usecases/resolveAllowedIssueAuthors.ts +0 -6
  736. package/src/domain/usecases/resolveLabelsAsLlmAgentName.test.ts +0 -42
  737. package/src/domain/usecases/resolveLabelsAsLlmAgentName.ts +0 -6
  738. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.test.ts +0 -33
  739. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.ts +0 -7
  740. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.test.ts +0 -337
  741. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.ts +0 -133
  742. package/src/domain/usecases/silentSessionReminderSentinel.ts +0 -9
  743. package/src/domain/usecases/triagerAgentName.ts +0 -4
  744. package/src/domain/usecases/utils.test.ts +0 -35
  745. package/src/domain/usecases/utils.ts +0 -6
  746. package/src/index.test.ts +0 -8
  747. package/src/index.ts +0 -10
  748. package/src/releaseConfig.test.ts +0 -40
  749. package/tsconfig.build.json +0 -18
  750. package/tsconfig.json +0 -25
  751. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts +0 -13
  752. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts.map +0 -1
  753. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts +0 -16
  754. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts.map +0 -1
  755. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts +0 -12
  756. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts.map +0 -1
  757. package/types/domain/entities/UnansweredOwnerCall.d.ts +0 -5
  758. package/types/domain/entities/UnansweredOwnerCall.d.ts.map +0 -1
  759. package/types/domain/entities/WorkingTime.d.ts +0 -8
  760. package/types/domain/entities/WorkingTime.d.ts.map +0 -1
  761. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts +0 -17
  762. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts.map +0 -1
  763. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +0 -23
  764. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +0 -1
  765. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts +0 -50
  766. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts.map +0 -1
  767. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts +0 -15
  768. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts.map +0 -1
  769. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts +0 -11
  770. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts.map +0 -1
  771. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts +0 -3
  772. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts.map +0 -1
  773. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +0 -5
  774. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +0 -1
  775. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts +0 -5
  776. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts.map +0 -1
  777. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts +0 -2
  778. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts.map +0 -1
  779. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts +0 -3
  780. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts.map +0 -1
@@ -1,2523 +0,0 @@
1
- import * as fs from 'fs';
2
- import { mock } from 'jest-mock-extended';
3
- import * as os from 'os';
4
- import * as path from 'path';
5
- import type { Issue } from '../../../domain/entities/Issue';
6
- import type { Project } from '../../../domain/entities/Project';
7
- import type { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
8
- import {
9
- CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES,
10
- CONSOLE_DONE_STORY_SELECTED_TAB_NAMES,
11
- CONSOLE_DONE_TAB_NAMES,
12
- readDoneProjectItemIds,
13
- } from './consoleDoneStore';
14
- import {
15
- CONFLICT_RETURNED_MESSAGE,
16
- type ConsoleOperationContext,
17
- type ConsoleProjectBinding,
18
- handleAttachmentUpload,
19
- handleComment,
20
- handleCreateIssue,
21
- handleDeleteAllComments,
22
- handleIntmux,
23
- handleReorderStory,
24
- handleReview,
25
- handleReviewComment,
26
- handleStoryAdd,
27
- handleStoryColor,
28
- handleTriage,
29
- } from './consoleOperationApi';
30
-
31
- describe('consoleOperationApi', () => {
32
- let baseDir: string;
33
- let issueRepository: ReturnType<typeof mock<IssueRepository>>;
34
- let project: Project;
35
- let context: ConsoleOperationContext;
36
-
37
- const issue: Issue = {
38
- ...mock<Issue>(),
39
- url: 'https://github.com/o/r/issues/1',
40
- itemId: 'PVTI_loaded',
41
- };
42
-
43
- beforeEach(() => {
44
- baseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-op-'));
45
- issueRepository = mock<IssueRepository>();
46
- issueRepository.get.mockResolvedValue(issue);
47
- issueRepository.getOpenPullRequest.mockResolvedValue({
48
- url: 'https://github.com/o/r/pull/1',
49
- branchName: null,
50
- createdAt: new Date(0),
51
- isDraft: false,
52
- isConflicted: false,
53
- mergeable: 'MERGEABLE',
54
- isPassedAllCiJob: true,
55
- isCiStateSuccess: true,
56
- isResolvedAllReviewComments: true,
57
- isBranchOutOfDate: false,
58
- missingRequiredCheckNames: [],
59
- });
60
- issueRepository.getPullRequestDetail.mockResolvedValue({
61
- title: 'Test PR',
62
- state: 'open',
63
- merged: false,
64
- isDraft: false,
65
- additions: 0,
66
- deletions: 0,
67
- changedFiles: 0,
68
- headRefName: 'feature',
69
- baseRefName: 'main',
70
- author: 'other-user',
71
- files: [],
72
- });
73
- issueRepository.getAuthenticatedUserLogin.mockResolvedValue(
74
- 'authenticated-user',
75
- );
76
- project = {
77
- ...mock<Project>(),
78
- id: 'PVT_1',
79
- status: {
80
- name: 'Status',
81
- fieldId: 'statusField',
82
- statuses: [
83
- {
84
- id: 'status_aw',
85
- name: 'Awaiting workspace',
86
- color: 'GRAY',
87
- description: '',
88
- },
89
- {
90
- id: 'status_intmux',
91
- name: 'In Tmux by human',
92
- color: 'YELLOW',
93
- description: '',
94
- },
95
- {
96
- id: 'status_todo',
97
- name: 'Todo',
98
- color: 'BLUE',
99
- description: '',
100
- },
101
- ],
102
- },
103
- story: {
104
- name: 'Story',
105
- fieldId: 'storyField',
106
- databaseId: 1,
107
- stories: [],
108
- workflowManagementStory: { id: 'wms', name: 'workflow' },
109
- },
110
- };
111
- context = {
112
- resolveIssueRepository: () => issueRepository,
113
- resolveProject: async (pjcode: string) =>
114
- pjcode === 'acme' ? { pjcode, project } : null,
115
- isPjcodeConfigured: (pjcode: string) => pjcode === 'acme',
116
- consoleDataOutputDir: baseDir,
117
- issueAttachmentRepository: null,
118
- resolveProjectRepository: null,
119
- invalidateProject: null,
120
- updateProjectCacheEntry: null,
121
- };
122
- });
123
-
124
- const contextForProject = (
125
- nextProject: Project,
126
- ): ConsoleOperationContext => ({
127
- resolveIssueRepository: () => issueRepository,
128
- resolveProject: async (pjcode: string) =>
129
- pjcode === 'acme' ? { pjcode, project: nextProject } : null,
130
- isPjcodeConfigured: (pjcode: string) => pjcode === 'acme',
131
- consoleDataOutputDir: baseDir,
132
- issueAttachmentRepository: null,
133
- resolveProjectRepository: null,
134
- invalidateProject: null,
135
- updateProjectCacheEntry: null,
136
- });
137
-
138
- afterEach(() => {
139
- fs.rmSync(baseDir, { recursive: true, force: true });
140
- });
141
-
142
- const expectRecordedAcrossTabs = (projectItemId: string): void => {
143
- for (const tab of CONSOLE_DONE_TAB_NAMES) {
144
- expect(readDoneProjectItemIds(baseDir, 'acme', tab)).toContain(
145
- projectItemId,
146
- );
147
- }
148
- };
149
-
150
- const expectRecordedOnlyIn = (
151
- projectItemId: string,
152
- recordedTabs: string[],
153
- ): void => {
154
- for (const tab of CONSOLE_DONE_TAB_NAMES) {
155
- if (recordedTabs.includes(tab)) {
156
- expect(readDoneProjectItemIds(baseDir, 'acme', tab)).toContain(
157
- projectItemId,
158
- );
159
- continue;
160
- }
161
- expect(readDoneProjectItemIds(baseDir, 'acme', tab)).not.toContain(
162
- projectItemId,
163
- );
164
- }
165
- };
166
-
167
- describe('handleReview', () => {
168
- it('approves and merges, sets Awaiting workspace then records done', async () => {
169
- const response = await handleReview(context, {
170
- pjcode: 'acme',
171
- action: 'approve_and_merge',
172
- prUrl: 'https://github.com/o/r/pull/1',
173
- projectItemId: 'PVTI_a',
174
- });
175
- expect(response.statusCode).toBe(200);
176
- expect(issueRepository.approvePullRequest).toHaveBeenCalledWith(
177
- 'https://github.com/o/r/pull/1',
178
- );
179
- expect(issueRepository.mergePullRequest).toHaveBeenCalledWith(
180
- 'https://github.com/o/r/pull/1',
181
- );
182
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
183
- project,
184
- expect.objectContaining({
185
- itemId: 'PVTI_a',
186
- url: 'https://github.com/o/r/pull/1',
187
- }),
188
- 'status_aw',
189
- );
190
- expect(issueRepository.get).not.toHaveBeenCalled();
191
- expectRecordedOnlyIn('PVTI_a', CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES);
192
- });
193
-
194
- it('resolves the issue repository from the url of the operated pull request', async () => {
195
- const resolvedUrls: string[] = [];
196
- const contextRecordingResolvedUrls: ConsoleOperationContext = {
197
- ...context,
198
- resolveIssueRepository: (issueOrPullRequestUrl: string) => {
199
- resolvedUrls.push(issueOrPullRequestUrl);
200
- return issueRepository;
201
- },
202
- };
203
-
204
- await handleReview(contextRecordingResolvedUrls, {
205
- pjcode: 'acme',
206
- action: 'approve_and_merge',
207
- prUrl: 'https://github.com/acme-labs/acme-portal-mock/pull/178',
208
- projectItemId: 'PVTI_resolver',
209
- });
210
-
211
- expect(resolvedUrls).toContain(
212
- 'https://github.com/acme-labs/acme-portal-mock/pull/178',
213
- );
214
- });
215
-
216
- it('requests changes with the inline comment anchored to a line and side', async () => {
217
- const response = await handleReview(context, {
218
- pjcode: 'acme',
219
- action: 'request_changes',
220
- prUrl: 'https://github.com/o/r/pull/1',
221
- projectItemId: 'PVTI_b',
222
- commentBody: 'please fix',
223
- changedFilePath: 'src/a.ts',
224
- line: 17,
225
- side: 'RIGHT',
226
- });
227
- expect(response.statusCode).toBe(200);
228
- expect(
229
- issueRepository.requestChangesWithInlineComment,
230
- ).toHaveBeenCalledWith(
231
- 'https://github.com/o/r/pull/1',
232
- 'src/a.ts',
233
- 'please fix',
234
- {
235
- line: 17,
236
- side: 'RIGHT',
237
- },
238
- );
239
- expectRecordedOnlyIn('PVTI_b', CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES);
240
- });
241
-
242
- it('requests changes without a line anchor when line and side are missing', async () => {
243
- const response = await handleReview(context, {
244
- pjcode: 'acme',
245
- action: 'request_changes',
246
- prUrl: 'https://github.com/o/r/pull/1',
247
- projectItemId: 'PVTI_b',
248
- commentBody: 'please fix',
249
- changedFilePath: 'src/a.ts',
250
- });
251
- expect(response.statusCode).toBe(200);
252
- expect(
253
- issueRepository.requestChangesWithInlineComment,
254
- ).toHaveBeenCalledWith(
255
- 'https://github.com/o/r/pull/1',
256
- 'src/a.ts',
257
- 'please fix',
258
- null,
259
- );
260
- expectRecordedOnlyIn('PVTI_b', CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES);
261
- });
262
-
263
- it('rejects request_changes without a comment body', async () => {
264
- const response = await handleReview(context, {
265
- pjcode: 'acme',
266
- action: 'request_changes',
267
- prUrl: 'https://github.com/o/r/pull/1',
268
- projectItemId: 'PVTI_b',
269
- });
270
- expect(response.statusCode).toBe(400);
271
- expect(
272
- issueRepository.requestChangesWithInlineComment,
273
- ).not.toHaveBeenCalled();
274
- });
275
-
276
- it('closes a pull request and posts a comment', async () => {
277
- const response = await handleReview(context, {
278
- pjcode: 'acme',
279
- action: 'close',
280
- prUrl: 'https://github.com/o/r/pull/1',
281
- projectItemId: 'PVTI_c',
282
- commentBody: 'closing',
283
- });
284
- expect(response.statusCode).toBe(200);
285
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
286
- 'https://github.com/o/r/pull/1',
287
- );
288
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
289
- 'https://github.com/o/r/pull/1',
290
- 'closing',
291
- );
292
- expectRecordedAcrossTabs('PVTI_c');
293
- });
294
-
295
- it('closes a pull request and moves linked issue to Awaiting workspace when issueUrl is provided', async () => {
296
- const response = await handleReview(context, {
297
- pjcode: 'acme',
298
- action: 'close',
299
- prUrl: 'https://github.com/o/r/pull/1',
300
- issueUrl: 'https://github.com/o/r/issues/7',
301
- projectItemId: 'PVTI_c_totally_wrong',
302
- commentBody: 'totally wrong',
303
- });
304
- expect(response.statusCode).toBe(200);
305
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
306
- 'https://github.com/o/r/pull/1',
307
- );
308
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
309
- 'https://github.com/o/r/pull/1',
310
- 'totally wrong',
311
- );
312
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
313
- project,
314
- expect.objectContaining({
315
- itemId: 'PVTI_c_totally_wrong',
316
- url: 'https://github.com/o/r/issues/7',
317
- }),
318
- 'status_aw',
319
- );
320
- expectRecordedAcrossTabs('PVTI_c_totally_wrong');
321
- });
322
-
323
- it('marks a pull request unnecessary by closing it, labelling the item chore and moving it to Awaiting workspace', async () => {
324
- issueRepository.getIssueByUrl.mockResolvedValue({
325
- ...issue,
326
- url: 'https://github.com/o/r/issues/7',
327
- org: 'o',
328
- repo: 'r',
329
- number: 7,
330
- labels: ['bug'],
331
- });
332
- const response = await handleReview(context, {
333
- pjcode: 'acme',
334
- action: 'unnecessary',
335
- prUrl: 'https://github.com/o/r/pull/1',
336
- issueUrl: 'https://github.com/o/r/issues/7',
337
- projectItemId: 'PVTI_unnecessary',
338
- commentBody: 'This pull request is unnecessary.',
339
- });
340
- expect(response.statusCode).toBe(200);
341
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
342
- 'https://github.com/o/r/pull/1',
343
- );
344
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
345
- 'https://github.com/o/r/pull/1',
346
- 'This pull request is unnecessary.',
347
- );
348
- expect(issueRepository.updateLabels).toHaveBeenCalledWith(
349
- expect.objectContaining({ url: 'https://github.com/o/r/issues/7' }),
350
- ['bug', 'chore'],
351
- );
352
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
353
- project,
354
- expect.objectContaining({ itemId: 'PVTI_unnecessary' }),
355
- 'status_aw',
356
- );
357
- expectRecordedAcrossTabs('PVTI_unnecessary');
358
- });
359
-
360
- it('tells the linked issue that the unnecessary pull request must not be created again', async () => {
361
- issueRepository.getIssueByUrl.mockResolvedValue({
362
- ...issue,
363
- url: 'https://github.com/o/r/issues/9',
364
- org: 'o',
365
- repo: 'r',
366
- number: 9,
367
- labels: ['bug'],
368
- });
369
- const issueCommentBody =
370
- 'The pull request for this issue was unnecessary and has been closed: https://github.com/o/r/pull/3\n\nDo not create it again.';
371
- const response = await handleReview(context, {
372
- pjcode: 'acme',
373
- action: 'unnecessary',
374
- prUrl: 'https://github.com/o/r/pull/3',
375
- issueUrl: 'https://github.com/o/r/issues/9',
376
- projectItemId: 'PVTI_unnecessary_issue_comment',
377
- commentBody: 'This pull request is unnecessary.',
378
- issueCommentBody,
379
- });
380
- expect(response.statusCode).toBe(200);
381
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
382
- 'https://github.com/o/r/issues/9',
383
- issueCommentBody,
384
- );
385
- });
386
-
387
- it('keeps the chore label once when the unnecessary item already carries it', async () => {
388
- issueRepository.getIssueByUrl.mockResolvedValue({
389
- ...issue,
390
- url: 'https://github.com/o/r/issues/8',
391
- org: 'o',
392
- repo: 'r',
393
- number: 8,
394
- labels: ['chore'],
395
- });
396
- const response = await handleReview(context, {
397
- pjcode: 'acme',
398
- action: 'unnecessary',
399
- prUrl: 'https://github.com/o/r/pull/2',
400
- issueUrl: 'https://github.com/o/r/issues/8',
401
- projectItemId: 'PVTI_unnecessary_dup',
402
- });
403
- expect(response.statusCode).toBe(200);
404
- expect(issueRepository.updateLabels).not.toHaveBeenCalled();
405
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
406
- project,
407
- expect.objectContaining({ itemId: 'PVTI_unnecessary_dup' }),
408
- 'status_aw',
409
- );
410
- });
411
-
412
- it('rejects an unnecessary action without an issueUrl', async () => {
413
- const response = await handleReview(context, {
414
- pjcode: 'acme',
415
- action: 'unnecessary',
416
- prUrl: 'https://github.com/o/r/pull/1',
417
- projectItemId: 'PVTI_unnecessary_missing',
418
- });
419
- expect(response.statusCode).toBe(400);
420
- expect(issueRepository.closePullRequest).not.toHaveBeenCalled();
421
- });
422
-
423
- it('leaves labels and status untouched for the plain close action', async () => {
424
- const response = await handleReview(context, {
425
- pjcode: 'acme',
426
- action: 'close',
427
- prUrl: 'https://github.com/o/r/pull/1',
428
- projectItemId: 'PVTI_plain_close',
429
- commentBody: 'closing',
430
- });
431
- expect(response.statusCode).toBe(200);
432
- expect(issueRepository.updateLabels).not.toHaveBeenCalled();
433
- expect(issueRepository.updateStatus).not.toHaveBeenCalled();
434
- });
435
-
436
- it('rejects an unknown review action', async () => {
437
- const response = await handleReview(context, {
438
- pjcode: 'acme',
439
- action: 'frobnicate',
440
- prUrl: 'https://github.com/o/r/pull/1',
441
- projectItemId: 'PVTI_c',
442
- });
443
- expect(response.statusCode).toBe(400);
444
- });
445
-
446
- it('rejects a missing prUrl', async () => {
447
- const response = await handleReview(context, {
448
- pjcode: 'acme',
449
- action: 'approve_and_merge',
450
- projectItemId: 'PVTI_c',
451
- });
452
- expect(response.statusCode).toBe(400);
453
- });
454
-
455
- it('rejects a missing action', async () => {
456
- const response = await handleReview(context, {
457
- pjcode: 'acme',
458
- prUrl: 'https://github.com/o/r/pull/1',
459
- projectItemId: 'PVTI_c',
460
- });
461
- expect(response.statusCode).toBe(400);
462
- });
463
-
464
- it('rejects a missing projectItemId', async () => {
465
- const response = await handleReview(context, {
466
- pjcode: 'acme',
467
- action: 'approve_and_merge',
468
- prUrl: 'https://github.com/o/r/pull/1',
469
- });
470
- expect(response.statusCode).toBe(400);
471
- });
472
-
473
- it('merges without approving using the request project item id without a GraphQL item fetch', async () => {
474
- issueRepository.get.mockResolvedValue(null);
475
- const response = await handleReview(context, {
476
- pjcode: 'acme',
477
- action: 'approve_and_merge',
478
- prUrl: 'https://github.com/o/r/pull/1',
479
- projectItemId: 'PVTI_c',
480
- });
481
- expect(response.statusCode).toBe(200);
482
- expect(issueRepository.get).not.toHaveBeenCalled();
483
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
484
- project,
485
- expect.objectContaining({ itemId: 'PVTI_c' }),
486
- 'status_aw',
487
- );
488
- });
489
-
490
- it('returns 400 when the Awaiting workspace status is absent', async () => {
491
- const contextWithoutStatus = contextForProject({
492
- ...project,
493
- status: { name: 'Status', fieldId: 'f', statuses: [] },
494
- });
495
- const response = await handleReview(contextWithoutStatus, {
496
- pjcode: 'acme',
497
- action: 'approve_and_merge',
498
- prUrl: 'https://github.com/o/r/pull/1',
499
- projectItemId: 'PVTI_c',
500
- });
501
- expect(response.statusCode).toBe(400);
502
- });
503
-
504
- it('skips approval and merges directly when the pull request author matches the authenticated account', async () => {
505
- issueRepository.getPullRequestDetail.mockResolvedValue({
506
- title: 'Self PR',
507
- state: 'open',
508
- merged: false,
509
- isDraft: false,
510
- additions: 0,
511
- deletions: 0,
512
- changedFiles: 0,
513
- headRefName: 'feature',
514
- baseRefName: 'main',
515
- author: 'authenticated-user',
516
- files: [],
517
- });
518
- const response = await handleReview(context, {
519
- pjcode: 'acme',
520
- action: 'approve_and_merge',
521
- prUrl: 'https://github.com/o/r/pull/1',
522
- projectItemId: 'PVTI_self',
523
- });
524
- expect(response.statusCode).toBe(200);
525
- expect(issueRepository.approvePullRequest).not.toHaveBeenCalled();
526
- expect(issueRepository.mergePullRequest).toHaveBeenCalledWith(
527
- 'https://github.com/o/r/pull/1',
528
- );
529
- });
530
-
531
- it('merges the pull request when approvePullRequest returns HTTP 422', async () => {
532
- issueRepository.getPullRequestDetail.mockResolvedValue(null);
533
- issueRepository.approvePullRequest.mockRejectedValue(
534
- new Error(
535
- 'Failed to approve PR https://github.com/o/r/pull/1: HTTP 422 Review Can not approve your own pull request',
536
- ),
537
- );
538
- const response = await handleReview(context, {
539
- pjcode: 'acme',
540
- action: 'approve_and_merge',
541
- prUrl: 'https://github.com/o/r/pull/1',
542
- projectItemId: 'PVTI_422',
543
- });
544
- expect(response.statusCode).toBe(200);
545
- expect(issueRepository.mergePullRequest).toHaveBeenCalledWith(
546
- 'https://github.com/o/r/pull/1',
547
- );
548
- expect(issueRepository.updateStatus).toHaveBeenCalled();
549
- });
550
-
551
- it('re-throws errors other than HTTP 422 from approvePullRequest', async () => {
552
- issueRepository.getPullRequestDetail.mockResolvedValue(null);
553
- issueRepository.approvePullRequest.mockRejectedValue(
554
- new Error(
555
- 'Failed to approve PR https://github.com/o/r/pull/1: HTTP 500 Internal Server Error',
556
- ),
557
- );
558
- await expect(
559
- handleReview(context, {
560
- pjcode: 'acme',
561
- action: 'approve_and_merge',
562
- prUrl: 'https://github.com/o/r/pull/1',
563
- projectItemId: 'PVTI_500',
564
- }),
565
- ).rejects.toThrow('HTTP 500');
566
- expect(issueRepository.mergePullRequest).not.toHaveBeenCalled();
567
- });
568
-
569
- it('posts a conflict comment, moves to Awaiting workspace, and returns 200 when the pull request has a merge conflict', async () => {
570
- issueRepository.getOpenPullRequest.mockResolvedValue({
571
- url: 'https://github.com/o/r/pull/1',
572
- branchName: null,
573
- createdAt: new Date(0),
574
- isDraft: false,
575
- isConflicted: true,
576
- mergeable: 'CONFLICTING',
577
- isPassedAllCiJob: true,
578
- isCiStateSuccess: true,
579
- isResolvedAllReviewComments: true,
580
- isBranchOutOfDate: false,
581
- missingRequiredCheckNames: [],
582
- });
583
- const response = await handleReview(context, {
584
- pjcode: 'acme',
585
- action: 'approve_and_merge',
586
- prUrl: 'https://github.com/o/r/pull/1',
587
- projectItemId: 'PVTI_conflict',
588
- });
589
- expect(response.statusCode).toBe(200);
590
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
591
- 'https://github.com/o/r/pull/1',
592
- CONFLICT_RETURNED_MESSAGE,
593
- );
594
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
595
- project,
596
- expect.objectContaining({
597
- itemId: 'PVTI_conflict',
598
- url: 'https://github.com/o/r/pull/1',
599
- }),
600
- 'status_aw',
601
- );
602
- expect(issueRepository.mergePullRequest).not.toHaveBeenCalled();
603
- expectRecordedOnlyIn(
604
- 'PVTI_conflict',
605
- CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES,
606
- );
607
- });
608
-
609
- it('returns 400 with check names when required checks are not green', async () => {
610
- issueRepository.getOpenPullRequest.mockResolvedValue({
611
- url: 'https://github.com/o/r/pull/1',
612
- branchName: null,
613
- createdAt: new Date(0),
614
- isDraft: false,
615
- isConflicted: false,
616
- mergeable: 'MERGEABLE',
617
- isPassedAllCiJob: false,
618
- isCiStateSuccess: false,
619
- isResolvedAllReviewComments: true,
620
- isBranchOutOfDate: false,
621
- missingRequiredCheckNames: ['test', 'lint'],
622
- });
623
- const response = await handleReview(context, {
624
- pjcode: 'acme',
625
- action: 'approve_and_merge',
626
- prUrl: 'https://github.com/o/r/pull/1',
627
- projectItemId: 'PVTI_ci',
628
- });
629
- expect(response.statusCode).toBe(400);
630
- expect(response.body).toMatchObject({
631
- error: 'Cannot merge: required checks are not green: test, lint',
632
- });
633
- expect(issueRepository.mergePullRequest).not.toHaveBeenCalled();
634
- });
635
-
636
- it('returns 400 when the pull request is not found or already closed', async () => {
637
- issueRepository.getOpenPullRequest.mockResolvedValue(null);
638
- const response = await handleReview(context, {
639
- pjcode: 'acme',
640
- action: 'approve_and_merge',
641
- prUrl: 'https://github.com/o/r/pull/1',
642
- projectItemId: 'PVTI_gone',
643
- });
644
- expect(response.statusCode).toBe(400);
645
- expect(response.body).toMatchObject({
646
- error: 'Cannot merge: pull request not found or already closed',
647
- });
648
- expect(issueRepository.mergePullRequest).not.toHaveBeenCalled();
649
- });
650
- });
651
-
652
- describe('handleTriage', () => {
653
- it('sets the status by name', async () => {
654
- const response = await handleTriage(context, {
655
- pjcode: 'acme',
656
- action: 'set_status',
657
- issueUrl: 'https://github.com/o/r/issues/1',
658
- projectItemId: 'PVTI_d',
659
- statusName: 'Todo',
660
- });
661
- expect(response.statusCode).toBe(200);
662
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
663
- project,
664
- expect.objectContaining({
665
- itemId: 'PVTI_d',
666
- url: 'https://github.com/o/r/issues/1',
667
- }),
668
- 'status_todo',
669
- );
670
- expect(issueRepository.get).not.toHaveBeenCalled();
671
- expectRecordedOnlyIn('PVTI_d', CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES);
672
- });
673
-
674
- it('keeps a status change out of the story-selected tabs', async () => {
675
- const response = await handleTriage(context, {
676
- pjcode: 'acme',
677
- action: 'set_status',
678
- issueUrl: 'https://github.com/o/r/issues/1',
679
- projectItemId: 'PVTI_status_only',
680
- statusName: 'Todo',
681
- });
682
- expect(response.statusCode).toBe(200);
683
- expectRecordedOnlyIn(
684
- 'PVTI_status_only',
685
- CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES,
686
- );
687
- });
688
-
689
- it('keeps a story change out of the status-selected tabs', async () => {
690
- const response = await handleTriage(context, {
691
- pjcode: 'acme',
692
- action: 'set_story',
693
- issueUrl: 'https://github.com/o/r/issues/1',
694
- projectItemId: 'PVTI_story_only',
695
- storyOptionId: 'story_opt_1',
696
- });
697
- expect(response.statusCode).toBe(200);
698
- expectRecordedOnlyIn(
699
- 'PVTI_story_only',
700
- CONSOLE_DONE_STORY_SELECTED_TAB_NAMES,
701
- );
702
- });
703
-
704
- it('rejects an unknown status name', async () => {
705
- const response = await handleTriage(context, {
706
- pjcode: 'acme',
707
- action: 'set_status',
708
- issueUrl: 'https://github.com/o/r/issues/1',
709
- projectItemId: 'PVTI_d',
710
- statusName: 'Nonexistent',
711
- });
712
- expect(response.statusCode).toBe(400);
713
- expect(issueRepository.updateStatus).not.toHaveBeenCalled();
714
- });
715
-
716
- it('sets the story option', async () => {
717
- const response = await handleTriage(context, {
718
- pjcode: 'acme',
719
- action: 'set_story',
720
- issueUrl: 'https://github.com/o/r/issues/1',
721
- projectItemId: 'PVTI_e',
722
- storyOptionId: 'story_opt_1',
723
- });
724
- expect(response.statusCode).toBe(200);
725
- expect(issueRepository.updateStory).toHaveBeenCalledWith(
726
- expect.objectContaining({ id: 'PVT_1' }),
727
- expect.objectContaining({ itemId: 'PVTI_e' }),
728
- 'story_opt_1',
729
- );
730
- expect(issueRepository.get).not.toHaveBeenCalled();
731
- expectRecordedOnlyIn('PVTI_e', CONSOLE_DONE_STORY_SELECTED_TAB_NAMES);
732
- });
733
-
734
- it('snoozes for one day via updateNextActionDate', async () => {
735
- const response = await handleTriage(context, {
736
- pjcode: 'acme',
737
- action: 'snooze_1day',
738
- issueUrl: 'https://github.com/o/r/issues/1',
739
- projectItemId: 'PVTI_f',
740
- });
741
- expect(response.statusCode).toBe(200);
742
- expect(issueRepository.updateNextActionDate).toHaveBeenCalledTimes(1);
743
- const call = issueRepository.updateNextActionDate.mock.calls[0];
744
- expect(call[0]).toBe('https://github.com/o/r/issues/1');
745
- expect(call[1]).toBe(project);
746
- expect(call[2]).toBeInstanceOf(Date);
747
- expectRecordedAcrossTabs('PVTI_f');
748
- });
749
-
750
- it('snoozes for one week via updateNextActionDate', async () => {
751
- const response = await handleTriage(context, {
752
- pjcode: 'acme',
753
- action: 'snooze_1week',
754
- issueUrl: 'https://github.com/o/r/issues/1',
755
- projectItemId: 'PVTI_g',
756
- });
757
- expect(response.statusCode).toBe(200);
758
- expect(issueRepository.updateNextActionDate).toHaveBeenCalledTimes(1);
759
- expectRecordedAcrossTabs('PVTI_g');
760
- });
761
-
762
- it('snoozes for one hour via updateNextActionHour', async () => {
763
- const projectWithHour: Project = {
764
- ...project,
765
- nextActionHour: {
766
- name: 'Next Action Hour',
767
- fieldId: 'nahField',
768
- options: [],
769
- },
770
- };
771
- const beforeHour = new Date().getUTCHours();
772
- const response = await handleTriage(contextForProject(projectWithHour), {
773
- pjcode: 'acme',
774
- action: 'snooze_1hour',
775
- issueUrl: 'https://github.com/o/r/issues/1',
776
- projectItemId: 'PVTI_1h',
777
- });
778
- expect(response.statusCode).toBe(200);
779
- expect(issueRepository.updateNextActionHour).toHaveBeenCalledTimes(1);
780
- const call = issueRepository.updateNextActionHour.mock.calls[0];
781
- expect(call[0]).toMatchObject({
782
- nextActionHour: { fieldId: 'nahField' },
783
- });
784
- expect(call[2]).toBeGreaterThanOrEqual(beforeHour + 1);
785
- expect(call[2]).toBeLessThanOrEqual(beforeHour + 2);
786
- expectRecordedAcrossTabs('PVTI_1h');
787
- });
788
-
789
- it('snoozes for three hours via updateNextActionHour', async () => {
790
- const projectWithHour: Project = {
791
- ...project,
792
- nextActionHour: {
793
- name: 'Next Action Hour',
794
- fieldId: 'nahField',
795
- options: [],
796
- },
797
- };
798
- const beforeHour = new Date().getUTCHours();
799
- const response = await handleTriage(contextForProject(projectWithHour), {
800
- pjcode: 'acme',
801
- action: 'snooze_3hours',
802
- issueUrl: 'https://github.com/o/r/issues/1',
803
- projectItemId: 'PVTI_3h',
804
- });
805
- expect(response.statusCode).toBe(200);
806
- expect(issueRepository.updateNextActionHour).toHaveBeenCalledTimes(1);
807
- const call = issueRepository.updateNextActionHour.mock.calls[0];
808
- expect(call[0]).toMatchObject({
809
- nextActionHour: { fieldId: 'nahField' },
810
- });
811
- expect(call[2]).toBeGreaterThanOrEqual(beforeHour + 3);
812
- expect(call[2]).toBeLessThanOrEqual(beforeHour + 4);
813
- expectRecordedAcrossTabs('PVTI_3h');
814
- });
815
-
816
- it('returns 400 when snooze_1hour is used on a project without nextActionHour', async () => {
817
- const projectWithoutHour: Project = { ...project, nextActionHour: null };
818
- const response = await handleTriage(
819
- contextForProject(projectWithoutHour),
820
- {
821
- pjcode: 'acme',
822
- action: 'snooze_1hour',
823
- issueUrl: 'https://github.com/o/r/issues/1',
824
- projectItemId: 'PVTI_nah',
825
- },
826
- );
827
- expect(response.statusCode).toBe(400);
828
- expect(issueRepository.updateNextActionHour).not.toHaveBeenCalled();
829
- });
830
-
831
- it('returns 400 when snooze_3hours is used on a project without nextActionHour', async () => {
832
- const projectWithoutHour: Project = { ...project, nextActionHour: null };
833
- const response = await handleTriage(
834
- contextForProject(projectWithoutHour),
835
- {
836
- pjcode: 'acme',
837
- action: 'snooze_3hours',
838
- issueUrl: 'https://github.com/o/r/issues/1',
839
- projectItemId: 'PVTI_nah3',
840
- },
841
- );
842
- expect(response.statusCode).toBe(400);
843
- expect(issueRepository.updateNextActionHour).not.toHaveBeenCalled();
844
- });
845
-
846
- it('closes an issue as completed via the triage close action', async () => {
847
- const response = await handleTriage(context, {
848
- pjcode: 'acme',
849
- action: 'close',
850
- issueUrl: 'https://github.com/o/r/issues/1',
851
- projectItemId: 'PVTI_h',
852
- commentBody: 'duplicate',
853
- });
854
- expect(response.statusCode).toBe(200);
855
- expect(issueRepository.closeIssueByUrl).toHaveBeenCalledWith(
856
- 'https://github.com/o/r/issues/1',
857
- 'completed',
858
- );
859
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
860
- 'https://github.com/o/r/issues/1',
861
- 'duplicate',
862
- );
863
- expectRecordedAcrossTabs('PVTI_h');
864
- });
865
-
866
- it('closes an issue as not planned via the triage close_not_planned action', async () => {
867
- const response = await handleTriage(context, {
868
- pjcode: 'acme',
869
- action: 'close_not_planned',
870
- issueUrl: 'https://github.com/o/r/issues/2',
871
- projectItemId: 'PVTI_np',
872
- });
873
- expect(response.statusCode).toBe(200);
874
- expect(issueRepository.closeIssueByUrl).toHaveBeenCalledWith(
875
- 'https://github.com/o/r/issues/2',
876
- 'not_planned',
877
- );
878
- expectRecordedAcrossTabs('PVTI_np');
879
- });
880
-
881
- it('closes a pull request via the triage close action without an invalid state_reason', async () => {
882
- const response = await handleTriage(context, {
883
- pjcode: 'acme',
884
- action: 'close',
885
- issueUrl: 'https://github.com/o/r/pull/7',
886
- projectItemId: 'PVTI_prclose',
887
- });
888
- expect(response.statusCode).toBe(200);
889
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
890
- 'https://github.com/o/r/pull/7',
891
- );
892
- expect(issueRepository.closeIssueByUrl).not.toHaveBeenCalled();
893
- expectRecordedAcrossTabs('PVTI_prclose');
894
- });
895
-
896
- it('closes a pull request via the triage close_not_planned action through the pull-request close path', async () => {
897
- const response = await handleTriage(context, {
898
- pjcode: 'acme',
899
- action: 'close_not_planned',
900
- issueUrl: 'https://github.com/o/r/pull/8',
901
- projectItemId: 'PVTI_prnp',
902
- });
903
- expect(response.statusCode).toBe(200);
904
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
905
- 'https://github.com/o/r/pull/8',
906
- );
907
- expect(issueRepository.closeIssueByUrl).not.toHaveBeenCalled();
908
- expectRecordedAcrossTabs('PVTI_prnp');
909
- });
910
-
911
- it('rejects an unknown triage action', async () => {
912
- const response = await handleTriage(context, {
913
- pjcode: 'acme',
914
- action: 'unknown',
915
- issueUrl: 'https://github.com/o/r/issues/1',
916
- projectItemId: 'PVTI_h',
917
- });
918
- expect(response.statusCode).toBe(400);
919
- });
920
-
921
- it('rejects set_status without a status name', async () => {
922
- const response = await handleTriage(context, {
923
- pjcode: 'acme',
924
- action: 'set_status',
925
- issueUrl: 'https://github.com/o/r/issues/1',
926
- projectItemId: 'PVTI_h',
927
- });
928
- expect(response.statusCode).toBe(400);
929
- });
930
-
931
- it('rejects set_story without a story option id', async () => {
932
- const response = await handleTriage(context, {
933
- pjcode: 'acme',
934
- action: 'set_story',
935
- issueUrl: 'https://github.com/o/r/issues/1',
936
- projectItemId: 'PVTI_h',
937
- });
938
- expect(response.statusCode).toBe(400);
939
- });
940
-
941
- it('rejects set_story when the project has no story field', async () => {
942
- const contextWithoutStory = contextForProject({
943
- ...project,
944
- story: null,
945
- });
946
- const response = await handleTriage(contextWithoutStory, {
947
- pjcode: 'acme',
948
- action: 'set_story',
949
- issueUrl: 'https://github.com/o/r/issues/1',
950
- projectItemId: 'PVTI_h',
951
- storyOptionId: 'story_opt_1',
952
- });
953
- expect(response.statusCode).toBe(400);
954
- expect(issueRepository.updateStory).not.toHaveBeenCalled();
955
- });
956
-
957
- it('rejects a missing issueUrl', async () => {
958
- const response = await handleTriage(context, {
959
- pjcode: 'acme',
960
- action: 'set_status',
961
- projectItemId: 'PVTI_h',
962
- statusName: 'Todo',
963
- });
964
- expect(response.statusCode).toBe(400);
965
- });
966
-
967
- it('sets the story using the request project item id without a GraphQL item fetch', async () => {
968
- issueRepository.get.mockResolvedValue(null);
969
- const response = await handleTriage(context, {
970
- pjcode: 'acme',
971
- action: 'set_story',
972
- issueUrl: 'https://github.com/o/r/issues/1',
973
- projectItemId: 'PVTI_h',
974
- storyOptionId: 'story_opt_1',
975
- });
976
- expect(response.statusCode).toBe(200);
977
- expect(issueRepository.get).not.toHaveBeenCalled();
978
- expect(issueRepository.updateStory).toHaveBeenCalledWith(
979
- expect.objectContaining({ id: 'PVT_1' }),
980
- expect.objectContaining({ itemId: 'PVTI_h' }),
981
- 'story_opt_1',
982
- );
983
- });
984
- });
985
-
986
- describe('close operations avoid loading the project via GraphQL', () => {
987
- let resolveProjectSpy: jest.Mock<
988
- Promise<ConsoleProjectBinding | null>,
989
- [string]
990
- >;
991
- let spiedContext: ConsoleOperationContext;
992
-
993
- beforeEach(() => {
994
- resolveProjectSpy = jest.fn(async (pjcode: string) =>
995
- pjcode === 'acme' ? { pjcode, project } : null,
996
- );
997
- spiedContext = {
998
- resolveIssueRepository: () => issueRepository,
999
- resolveProject: resolveProjectSpy,
1000
- isPjcodeConfigured: (pjcode: string) => pjcode === 'acme',
1001
- consoleDataOutputDir: baseDir,
1002
- issueAttachmentRepository: null,
1003
- resolveProjectRepository: null,
1004
- invalidateProject: null,
1005
- updateProjectCacheEntry: null,
1006
- };
1007
- });
1008
-
1009
- it('closes an issue via triage without resolving the project', async () => {
1010
- const response = await handleTriage(spiedContext, {
1011
- pjcode: 'acme',
1012
- action: 'close',
1013
- issueUrl: 'https://github.com/o/r/issues/1',
1014
- projectItemId: 'PVTI_noproj_close',
1015
- commentBody: 'duplicate',
1016
- });
1017
- expect(response.statusCode).toBe(200);
1018
- expect(issueRepository.closeIssueByUrl).toHaveBeenCalledWith(
1019
- 'https://github.com/o/r/issues/1',
1020
- 'completed',
1021
- );
1022
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
1023
- 'https://github.com/o/r/issues/1',
1024
- 'duplicate',
1025
- );
1026
- expect(resolveProjectSpy).not.toHaveBeenCalled();
1027
- expect(issueRepository.get).not.toHaveBeenCalled();
1028
- });
1029
-
1030
- it('closes not planned via triage without resolving the project', async () => {
1031
- const response = await handleTriage(spiedContext, {
1032
- pjcode: 'acme',
1033
- action: 'close_not_planned',
1034
- issueUrl: 'https://github.com/o/r/issues/1',
1035
- projectItemId: 'PVTI_noproj_np',
1036
- });
1037
- expect(response.statusCode).toBe(200);
1038
- expect(issueRepository.closeIssueByUrl).toHaveBeenCalledWith(
1039
- 'https://github.com/o/r/issues/1',
1040
- 'not_planned',
1041
- );
1042
- expect(resolveProjectSpy).not.toHaveBeenCalled();
1043
- });
1044
-
1045
- it('closes a pull request via review without resolving the project', async () => {
1046
- const response = await handleReview(spiedContext, {
1047
- pjcode: 'acme',
1048
- action: 'close',
1049
- prUrl: 'https://github.com/o/r/pull/1',
1050
- projectItemId: 'PVTI_noproj_reviewclose',
1051
- });
1052
- expect(response.statusCode).toBe(200);
1053
- expect(issueRepository.closePullRequest).toHaveBeenCalledWith(
1054
- 'https://github.com/o/r/pull/1',
1055
- );
1056
- expect(resolveProjectSpy).not.toHaveBeenCalled();
1057
- });
1058
-
1059
- it('rejects a close whose pjcode is not configured without resolving the project', async () => {
1060
- const response = await handleTriage(spiedContext, {
1061
- pjcode: 'unconfigured',
1062
- action: 'close',
1063
- issueUrl: 'https://github.com/o/r/issues/1',
1064
- projectItemId: 'PVTI_noproj_badpj',
1065
- });
1066
- expect(response.statusCode).toBe(400);
1067
- expect(issueRepository.closeIssueByUrl).not.toHaveBeenCalled();
1068
- expect(resolveProjectSpy).not.toHaveBeenCalled();
1069
- });
1070
-
1071
- it('sets intmux without a GraphQL item fetch while resolving the project', async () => {
1072
- const response = await handleIntmux(spiedContext, {
1073
- pjcode: 'acme',
1074
- action: 'set_intmux',
1075
- issueUrl: 'https://github.com/o/r/issues/1',
1076
- projectItemId: 'PVTI_needproj_intmux',
1077
- });
1078
- expect(response.statusCode).toBe(200);
1079
- expect(resolveProjectSpy).toHaveBeenCalledTimes(1);
1080
- expect(issueRepository.get).not.toHaveBeenCalled();
1081
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
1082
- project,
1083
- expect.objectContaining({ itemId: 'PVTI_needproj_intmux' }),
1084
- 'status_intmux',
1085
- );
1086
- });
1087
-
1088
- it('resolves the project for set_status but performs no GraphQL item fetch', async () => {
1089
- const response = await handleTriage(spiedContext, {
1090
- pjcode: 'acme',
1091
- action: 'set_status',
1092
- issueUrl: 'https://github.com/o/r/issues/1',
1093
- projectItemId: 'PVTI_needproj_status',
1094
- statusName: 'Todo',
1095
- });
1096
- expect(response.statusCode).toBe(200);
1097
- expect(resolveProjectSpy).toHaveBeenCalledTimes(1);
1098
- expect(issueRepository.get).not.toHaveBeenCalled();
1099
- expect(issueRepository.updateStatus).toHaveBeenCalledTimes(1);
1100
- });
1101
-
1102
- it('resolves the project for set_story but performs no GraphQL item fetch', async () => {
1103
- const response = await handleTriage(spiedContext, {
1104
- pjcode: 'acme',
1105
- action: 'set_story',
1106
- issueUrl: 'https://github.com/o/r/issues/1',
1107
- projectItemId: 'PVTI_needproj_story',
1108
- storyOptionId: 'story_opt_1',
1109
- });
1110
- expect(response.statusCode).toBe(200);
1111
- expect(resolveProjectSpy).toHaveBeenCalledTimes(1);
1112
- expect(issueRepository.get).not.toHaveBeenCalled();
1113
- expect(issueRepository.updateStory).toHaveBeenCalledTimes(1);
1114
- });
1115
-
1116
- it('resolves the project for snooze and passes the item id without a GraphQL item fetch', async () => {
1117
- const response = await handleTriage(spiedContext, {
1118
- pjcode: 'acme',
1119
- action: 'snooze_1day',
1120
- issueUrl: 'https://github.com/o/r/issues/1',
1121
- projectItemId: 'PVTI_needproj_snooze',
1122
- });
1123
- expect(response.statusCode).toBe(200);
1124
- expect(resolveProjectSpy).toHaveBeenCalledTimes(1);
1125
- expect(issueRepository.get).not.toHaveBeenCalled();
1126
- const call = issueRepository.updateNextActionDate.mock.calls[0];
1127
- expect(call[0]).toBe('https://github.com/o/r/issues/1');
1128
- expect(call[1]).toBe(project);
1129
- expect(call[2]).toBeInstanceOf(Date);
1130
- expect(call[3]).toBe('PVTI_needproj_snooze');
1131
- });
1132
- });
1133
-
1134
- describe('handleIntmux', () => {
1135
- it('sets the In Tmux by human status and records done', async () => {
1136
- const response = await handleIntmux(context, {
1137
- pjcode: 'acme',
1138
- action: 'set_intmux',
1139
- issueUrl: 'https://github.com/o/r/issues/1',
1140
- projectItemId: 'PVTI_i',
1141
- });
1142
- expect(response.statusCode).toBe(200);
1143
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
1144
- project,
1145
- expect.objectContaining({
1146
- itemId: 'PVTI_i',
1147
- url: 'https://github.com/o/r/issues/1',
1148
- }),
1149
- 'status_intmux',
1150
- );
1151
- expect(issueRepository.get).not.toHaveBeenCalled();
1152
- expectRecordedOnlyIn('PVTI_i', CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES);
1153
- });
1154
-
1155
- it('rejects an unknown intmux action', async () => {
1156
- const response = await handleIntmux(context, {
1157
- pjcode: 'acme',
1158
- action: 'unset_intmux',
1159
- issueUrl: 'https://github.com/o/r/issues/1',
1160
- projectItemId: 'PVTI_i',
1161
- });
1162
- expect(response.statusCode).toBe(400);
1163
- });
1164
-
1165
- it('sets intmux using the request project item id without a GraphQL item fetch', async () => {
1166
- issueRepository.get.mockResolvedValue(null);
1167
- const response = await handleIntmux(context, {
1168
- pjcode: 'acme',
1169
- action: 'set_intmux',
1170
- issueUrl: 'https://github.com/o/r/issues/1',
1171
- projectItemId: 'PVTI_i',
1172
- });
1173
- expect(response.statusCode).toBe(200);
1174
- expect(issueRepository.get).not.toHaveBeenCalled();
1175
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
1176
- project,
1177
- expect.objectContaining({ itemId: 'PVTI_i' }),
1178
- 'status_intmux',
1179
- );
1180
- });
1181
-
1182
- it('rejects a missing issueUrl', async () => {
1183
- const response = await handleIntmux(context, {
1184
- pjcode: 'acme',
1185
- action: 'set_intmux',
1186
- projectItemId: 'PVTI_i',
1187
- });
1188
- expect(response.statusCode).toBe(400);
1189
- });
1190
-
1191
- it('rejects a missing projectItemId', async () => {
1192
- const response = await handleIntmux(context, {
1193
- pjcode: 'acme',
1194
- action: 'set_intmux',
1195
- issueUrl: 'https://github.com/o/r/issues/1',
1196
- });
1197
- expect(response.statusCode).toBe(400);
1198
- });
1199
-
1200
- it('rejects a missing action', async () => {
1201
- const response = await handleIntmux(context, {
1202
- pjcode: 'acme',
1203
- issueUrl: 'https://github.com/o/r/issues/1',
1204
- projectItemId: 'PVTI_i',
1205
- });
1206
- expect(response.statusCode).toBe(400);
1207
- });
1208
- });
1209
-
1210
- describe('per-project resolution', () => {
1211
- it('rejects an operation whose body has no pjcode', async () => {
1212
- const response = await handleTriage(context, {
1213
- action: 'set_status',
1214
- issueUrl: 'https://github.com/o/r/issues/1',
1215
- projectItemId: 'PVTI_k',
1216
- statusName: 'Todo',
1217
- });
1218
- expect(response.statusCode).toBe(400);
1219
- expect(issueRepository.updateStatus).not.toHaveBeenCalled();
1220
- });
1221
-
1222
- it('rejects an operation whose pjcode has no configured project', async () => {
1223
- const response = await handleTriage(context, {
1224
- pjcode: 'unknown-project',
1225
- action: 'set_status',
1226
- issueUrl: 'https://github.com/o/r/issues/1',
1227
- projectItemId: 'PVTI_k',
1228
- statusName: 'Todo',
1229
- });
1230
- expect(response.statusCode).toBe(400);
1231
- expect(issueRepository.updateStatus).not.toHaveBeenCalled();
1232
- });
1233
-
1234
- it('records the .done exclusion under the resolved pjcode', async () => {
1235
- const otherProject: Project = { ...project, id: 'PVT_other' };
1236
- const multiContext: ConsoleOperationContext = {
1237
- resolveIssueRepository: () => issueRepository,
1238
- resolveProject: async (pjcode: string) => {
1239
- if (pjcode === 'acme') {
1240
- return { pjcode, project };
1241
- }
1242
- if (pjcode === 'globex') {
1243
- return { pjcode, project: otherProject };
1244
- }
1245
- return null;
1246
- },
1247
- isPjcodeConfigured: (pjcode: string) =>
1248
- pjcode === 'acme' || pjcode === 'globex',
1249
- consoleDataOutputDir: baseDir,
1250
- issueAttachmentRepository: null,
1251
- resolveProjectRepository: null,
1252
- invalidateProject: null,
1253
- updateProjectCacheEntry: null,
1254
- };
1255
- const response = await handleTriage(multiContext, {
1256
- pjcode: 'globex',
1257
- action: 'set_status',
1258
- issueUrl: 'https://github.com/o/r/issues/1',
1259
- projectItemId: 'PVTI_x',
1260
- statusName: 'Todo',
1261
- });
1262
- expect(response.statusCode).toBe(200);
1263
- expect(issueRepository.updateStatus).toHaveBeenCalledWith(
1264
- otherProject,
1265
- expect.objectContaining({ itemId: 'PVTI_x' }),
1266
- 'status_todo',
1267
- );
1268
- for (const tab of CONSOLE_DONE_STATUS_SELECTED_TAB_NAMES) {
1269
- expect(readDoneProjectItemIds(baseDir, 'globex', tab)).toContain(
1270
- 'PVTI_x',
1271
- );
1272
- }
1273
- for (const tab of CONSOLE_DONE_TAB_NAMES) {
1274
- expect(readDoneProjectItemIds(baseDir, 'acme', tab)).not.toContain(
1275
- 'PVTI_x',
1276
- );
1277
- }
1278
- });
1279
- });
1280
-
1281
- describe('done recording skips when storage is not configured', () => {
1282
- it('does not throw when consoleDataOutputDir is null', async () => {
1283
- const noStorageContext: ConsoleOperationContext = {
1284
- ...context,
1285
- consoleDataOutputDir: null,
1286
- };
1287
- const response = await handleIntmux(noStorageContext, {
1288
- pjcode: 'acme',
1289
- action: 'set_intmux',
1290
- issueUrl: 'https://github.com/o/r/issues/1',
1291
- projectItemId: 'PVTI_j',
1292
- });
1293
- expect(response.statusCode).toBe(200);
1294
- });
1295
- });
1296
-
1297
- describe('handleComment', () => {
1298
- it('posts a comment and returns the created comment', async () => {
1299
- issueRepository.getIssueOrPullRequestComments.mockResolvedValue([
1300
- {
1301
- author: 'github-actions',
1302
- body: 'All required checks have passed.',
1303
- createdAt: new Date('2026-06-17T07:48:11.000Z'),
1304
- },
1305
- {
1306
- author: 'HiromiShikata',
1307
- body: 'Please rebase onto the latest main branch.',
1308
- createdAt: new Date('2026-06-17T09:03:27.000Z'),
1309
- },
1310
- ]);
1311
- const response = await handleComment(context, {
1312
- pjcode: 'acme',
1313
- url: 'https://github.com/o/r/issues/1',
1314
- body: 'Please rebase onto the latest main branch.',
1315
- });
1316
- expect(response.statusCode).toBe(200);
1317
- expect(issueRepository.createCommentByUrl).toHaveBeenCalledWith(
1318
- 'https://github.com/o/r/issues/1',
1319
- 'Please rebase onto the latest main branch.',
1320
- );
1321
- expect(response.body).toEqual({
1322
- ok: true,
1323
- comment: {
1324
- author: 'HiromiShikata',
1325
- body: 'Please rebase onto the latest main branch.',
1326
- createdAt: '2026-06-17T09:03:27.000Z',
1327
- },
1328
- });
1329
- });
1330
-
1331
- it('falls back to the posted body when no comment is returned', async () => {
1332
- issueRepository.getIssueOrPullRequestComments.mockResolvedValue([]);
1333
- const response = await handleComment(context, {
1334
- pjcode: 'acme',
1335
- url: 'https://github.com/o/r/issues/1',
1336
- body: 'A first comment on this issue.',
1337
- });
1338
- expect(response.statusCode).toBe(200);
1339
- expect(response.body).toEqual({
1340
- ok: true,
1341
- comment: {
1342
- author: '',
1343
- body: 'A first comment on this issue.',
1344
- createdAt: '',
1345
- },
1346
- });
1347
- });
1348
-
1349
- it('rejects when url is missing', async () => {
1350
- const response = await handleComment(context, {
1351
- pjcode: 'acme',
1352
- body: 'A comment without a target url.',
1353
- });
1354
- expect(response.statusCode).toBe(400);
1355
- expect(issueRepository.createCommentByUrl).not.toHaveBeenCalled();
1356
- });
1357
-
1358
- it('rejects when body is missing', async () => {
1359
- const response = await handleComment(context, {
1360
- pjcode: 'acme',
1361
- url: 'https://github.com/o/r/issues/1',
1362
- });
1363
- expect(response.statusCode).toBe(400);
1364
- expect(issueRepository.createCommentByUrl).not.toHaveBeenCalled();
1365
- });
1366
- });
1367
-
1368
- describe('handleReviewComment', () => {
1369
- const validRequest = {
1370
- pjcode: 'acme',
1371
- url: 'https://github.com/o/r/pull/1',
1372
- path: 'src/index.ts',
1373
- line: 42,
1374
- side: 'RIGHT',
1375
- body: 'Consider extracting this into a helper.',
1376
- };
1377
-
1378
- it('creates a line-anchored review comment on the pull request', async () => {
1379
- const response = await handleReviewComment(context, validRequest);
1380
- expect(response.statusCode).toBe(200);
1381
- expect(response.body).toEqual({ ok: true });
1382
- expect(
1383
- issueRepository.createPullRequestReviewComment,
1384
- ).toHaveBeenCalledWith(
1385
- 'https://github.com/o/r/pull/1',
1386
- 'src/index.ts',
1387
- 42,
1388
- 'RIGHT',
1389
- 'Consider extracting this into a helper.',
1390
- );
1391
- });
1392
-
1393
- it('rejects when url is missing', async () => {
1394
- const response = await handleReviewComment(context, {
1395
- ...validRequest,
1396
- url: '',
1397
- });
1398
- expect(response.statusCode).toBe(400);
1399
- expect(
1400
- issueRepository.createPullRequestReviewComment,
1401
- ).not.toHaveBeenCalled();
1402
- });
1403
-
1404
- it('rejects when path is missing', async () => {
1405
- const response = await handleReviewComment(context, {
1406
- ...validRequest,
1407
- path: '',
1408
- });
1409
- expect(response.statusCode).toBe(400);
1410
- expect(
1411
- issueRepository.createPullRequestReviewComment,
1412
- ).not.toHaveBeenCalled();
1413
- });
1414
-
1415
- it('rejects when line is not a positive integer', async () => {
1416
- const response = await handleReviewComment(context, {
1417
- ...validRequest,
1418
- line: 0,
1419
- });
1420
- expect(response.statusCode).toBe(400);
1421
- expect(
1422
- issueRepository.createPullRequestReviewComment,
1423
- ).not.toHaveBeenCalled();
1424
- });
1425
-
1426
- it('rejects when side is not LEFT or RIGHT', async () => {
1427
- const response = await handleReviewComment(context, {
1428
- ...validRequest,
1429
- side: 'CENTER',
1430
- });
1431
- expect(response.statusCode).toBe(400);
1432
- expect(
1433
- issueRepository.createPullRequestReviewComment,
1434
- ).not.toHaveBeenCalled();
1435
- });
1436
-
1437
- it('rejects when body is missing', async () => {
1438
- const response = await handleReviewComment(context, {
1439
- ...validRequest,
1440
- body: '',
1441
- });
1442
- expect(response.statusCode).toBe(400);
1443
- expect(
1444
- issueRepository.createPullRequestReviewComment,
1445
- ).not.toHaveBeenCalled();
1446
- });
1447
-
1448
- it('surfaces the GitHub error message when the comment cannot be created', async () => {
1449
- issueRepository.createPullRequestReviewComment.mockRejectedValue(
1450
- new Error(
1451
- 'Failed to create review comment on PR https://github.com/o/r/pull/1: Validation Failed: line must be part of the diff',
1452
- ),
1453
- );
1454
- const response = await handleReviewComment(context, validRequest);
1455
- expect(response.statusCode).toBe(502);
1456
- expect(response.body).toEqual({
1457
- error:
1458
- 'Failed to create review comment on PR https://github.com/o/r/pull/1: Validation Failed: line must be part of the diff',
1459
- });
1460
- });
1461
- });
1462
-
1463
- describe('handleAttachmentUpload', () => {
1464
- const listItemUrl = 'https://github.com/o/r/issues/1';
1465
-
1466
- const writeListWithItem = (url: string): void => {
1467
- const dir = path.join(baseDir, 'acme', 'prs');
1468
- fs.mkdirSync(dir, { recursive: true });
1469
- fs.writeFileSync(
1470
- path.join(dir, 'list.json'),
1471
- JSON.stringify({ items: [{ url }] }),
1472
- );
1473
- };
1474
-
1475
- beforeEach(() => {
1476
- writeListWithItem(listItemUrl);
1477
- });
1478
-
1479
- const uploadContext = (
1480
- uploadAttachment: (request: {
1481
- issueOrPullRequestUrl: string;
1482
- fileName: string;
1483
- content: Uint8Array;
1484
- }) => Promise<string>,
1485
- ): ConsoleOperationContext => ({
1486
- ...context,
1487
- issueAttachmentRepository: { uploadAttachment },
1488
- });
1489
-
1490
- it('uploads the decoded file and returns the markdown', async () => {
1491
- const received: {
1492
- issueOrPullRequestUrl: string;
1493
- fileName: string;
1494
- content: Uint8Array;
1495
- }[] = [];
1496
- const response = await handleAttachmentUpload(
1497
- uploadContext(async (request) => {
1498
- received.push(request);
1499
- return '![shot](https://github.com/user-attachments/assets/abc)';
1500
- }),
1501
- {
1502
- pjcode: 'acme',
1503
- url: 'https://github.com/o/r/issues/1',
1504
- fileName: 'shot.png',
1505
- contentBase64: Buffer.from([1, 2, 3]).toString('base64'),
1506
- },
1507
- );
1508
- expect(response).toEqual({
1509
- statusCode: 200,
1510
- body: {
1511
- ok: true,
1512
- markdown: '![shot](https://github.com/user-attachments/assets/abc)',
1513
- },
1514
- });
1515
- expect(received).toHaveLength(1);
1516
- expect(received[0].issueOrPullRequestUrl).toBe(
1517
- 'https://github.com/o/r/issues/1',
1518
- );
1519
- expect(received[0].fileName).toBe('shot.png');
1520
- expect(Array.from(received[0].content)).toEqual([1, 2, 3]);
1521
- });
1522
-
1523
- it('rejects a request without a url', async () => {
1524
- const response = await handleAttachmentUpload(
1525
- uploadContext(async () => 'unused'),
1526
- { pjcode: 'acme', fileName: 'shot.png', contentBase64: 'AAEC' },
1527
- );
1528
- expect(response).toEqual({
1529
- statusCode: 400,
1530
- body: { error: 'url is required' },
1531
- });
1532
- });
1533
-
1534
- it('rejects a url that is not a github issue or pull request url', async () => {
1535
- const response = await handleAttachmentUpload(
1536
- uploadContext(async () => 'unused'),
1537
- {
1538
- pjcode: 'acme',
1539
- url: 'https://example.com/o/r/issues/1; rm -rf /',
1540
- fileName: 'shot.png',
1541
- contentBase64: 'AAEC',
1542
- },
1543
- );
1544
- expect(response).toEqual({
1545
- statusCode: 400,
1546
- body: { error: 'url must be a github issue or pull request url' },
1547
- });
1548
- });
1549
-
1550
- it('rejects a request without a fileName', async () => {
1551
- const response = await handleAttachmentUpload(
1552
- uploadContext(async () => 'unused'),
1553
- {
1554
- pjcode: 'acme',
1555
- url: 'https://github.com/o/r/issues/1',
1556
- contentBase64: 'AAEC',
1557
- },
1558
- );
1559
- expect(response).toEqual({
1560
- statusCode: 400,
1561
- body: { error: 'fileName is required' },
1562
- });
1563
- });
1564
-
1565
- it('rejects a request without contentBase64', async () => {
1566
- const response = await handleAttachmentUpload(
1567
- uploadContext(async () => 'unused'),
1568
- {
1569
- pjcode: 'acme',
1570
- url: 'https://github.com/o/r/issues/1',
1571
- fileName: 'shot.png',
1572
- },
1573
- );
1574
- expect(response).toEqual({
1575
- statusCode: 400,
1576
- body: { error: 'contentBase64 is required' },
1577
- });
1578
- });
1579
-
1580
- it('reports that upload is not configured when no repository is wired', async () => {
1581
- const response = await handleAttachmentUpload(
1582
- { ...context, issueAttachmentRepository: null },
1583
- {
1584
- pjcode: 'acme',
1585
- url: 'https://github.com/o/r/issues/1',
1586
- fileName: 'shot.png',
1587
- contentBase64: 'AAEC',
1588
- },
1589
- );
1590
- expect(response).toEqual({
1591
- statusCode: 502,
1592
- body: { error: 'attachment upload is not configured' },
1593
- });
1594
- });
1595
-
1596
- it('rejects a url that is not listed as a console item of the project', async () => {
1597
- const response = await handleAttachmentUpload(
1598
- uploadContext(async () => 'unused'),
1599
- {
1600
- pjcode: 'acme',
1601
- url: 'https://github.com/o/r/issues/9999',
1602
- fileName: 'shot.png',
1603
- contentBase64: 'AAEC',
1604
- },
1605
- );
1606
- expect(response).toEqual({
1607
- statusCode: 400,
1608
- body: { error: 'url is not a console item of this project' },
1609
- });
1610
- });
1611
- });
1612
-
1613
- describe('handleCreateIssue', () => {
1614
- const projectWithStory = (): Project => ({
1615
- ...project,
1616
- story: {
1617
- name: 'Story',
1618
- fieldId: 'storyField',
1619
- databaseId: 1,
1620
- stories: [
1621
- {
1622
- id: 'opt_blue',
1623
- name: 'Portal redesign',
1624
- color: 'BLUE',
1625
- description: '',
1626
- },
1627
- {
1628
- id: 'opt_green',
1629
- name: 'Move to Okinawa',
1630
- color: 'GREEN',
1631
- description: '',
1632
- },
1633
- ],
1634
- workflowManagementStory: { id: 'wms', name: 'workflow' },
1635
- },
1636
- });
1637
-
1638
- beforeEach(() => {
1639
- issueRepository.createNewIssue.mockResolvedValue(42);
1640
- issueRepository.addIssueToProject.mockResolvedValue(undefined);
1641
- });
1642
-
1643
- it('creates the issue, adds it to the project, and sets the story', async () => {
1644
- const storyProject = projectWithStory();
1645
- const createdIssue: Issue = {
1646
- ...mock<Issue>(),
1647
- url: 'https://github.com/acme-labs/portal/issues/42',
1648
- itemId: 'PVTI_new',
1649
- };
1650
- issueRepository.get.mockResolvedValue(createdIssue);
1651
-
1652
- const response = await handleCreateIssue(
1653
- contextForProject(storyProject),
1654
- {
1655
- pjcode: 'acme',
1656
- title: 'New task title',
1657
- storyOptionId: 'opt_blue',
1658
- nameWithOwner: 'acme-labs/portal',
1659
- },
1660
- );
1661
-
1662
- expect(response.statusCode).toBe(200);
1663
- expect(response.body).toEqual({
1664
- ok: true,
1665
- issueUrl: 'https://github.com/acme-labs/portal/issues/42',
1666
- });
1667
- expect(issueRepository.createNewIssue).toHaveBeenCalledWith(
1668
- 'acme-labs',
1669
- 'portal',
1670
- 'New task title',
1671
- '',
1672
- [],
1673
- [],
1674
- );
1675
- expect(issueRepository.addIssueToProject).toHaveBeenCalledWith(
1676
- storyProject,
1677
- 'https://github.com/acme-labs/portal/issues/42',
1678
- );
1679
- expect(issueRepository.updateStory).toHaveBeenCalledWith(
1680
- expect.objectContaining({ story: storyProject.story }),
1681
- createdIssue,
1682
- 'opt_blue',
1683
- );
1684
- });
1685
-
1686
- it('resolves the issue repository from a proxy url of the target repo', async () => {
1687
- const resolvedUrls: string[] = [];
1688
- const recordingContext: ConsoleOperationContext = {
1689
- ...contextForProject(projectWithStory()),
1690
- resolveIssueRepository: (url: string) => {
1691
- resolvedUrls.push(url);
1692
- return issueRepository;
1693
- },
1694
- };
1695
- issueRepository.get.mockResolvedValue(null);
1696
-
1697
- await handleCreateIssue(recordingContext, {
1698
- pjcode: 'acme',
1699
- title: 'Task',
1700
- storyOptionId: 'opt_blue',
1701
- nameWithOwner: 'acme-labs/portal',
1702
- });
1703
-
1704
- expect(resolvedUrls).toContain(
1705
- 'https://github.com/acme-labs/portal/issues/0',
1706
- );
1707
- });
1708
-
1709
- it('skips updateStory when get returns null after addIssueToProject', async () => {
1710
- issueRepository.get.mockResolvedValue(null);
1711
-
1712
- const response = await handleCreateIssue(
1713
- contextForProject(projectWithStory()),
1714
- {
1715
- pjcode: 'acme',
1716
- title: 'Task without project item',
1717
- storyOptionId: 'opt_green',
1718
- nameWithOwner: 'acme-labs/portal',
1719
- },
1720
- );
1721
-
1722
- expect(response.statusCode).toBe(200);
1723
- expect(issueRepository.updateStory).not.toHaveBeenCalled();
1724
- });
1725
-
1726
- it('rejects when title is missing', async () => {
1727
- const response = await handleCreateIssue(context, {
1728
- pjcode: 'acme',
1729
- storyOptionId: 'opt_blue',
1730
- nameWithOwner: 'acme-labs/portal',
1731
- });
1732
- expect(response).toEqual({
1733
- statusCode: 400,
1734
- body: { error: 'title is required' },
1735
- });
1736
- });
1737
-
1738
- it('rejects when storyOptionId is missing', async () => {
1739
- const response = await handleCreateIssue(context, {
1740
- pjcode: 'acme',
1741
- title: 'Task',
1742
- nameWithOwner: 'acme-labs/portal',
1743
- });
1744
- expect(response).toEqual({
1745
- statusCode: 400,
1746
- body: { error: 'storyOptionId is required' },
1747
- });
1748
- });
1749
-
1750
- it('rejects when nameWithOwner is missing', async () => {
1751
- const response = await handleCreateIssue(context, {
1752
- pjcode: 'acme',
1753
- title: 'Task',
1754
- storyOptionId: 'opt_blue',
1755
- });
1756
- expect(response).toEqual({
1757
- statusCode: 400,
1758
- body: { error: 'nameWithOwner is required' },
1759
- });
1760
- });
1761
-
1762
- it('rejects when nameWithOwner has no slash', async () => {
1763
- const response = await handleCreateIssue(context, {
1764
- pjcode: 'acme',
1765
- title: 'Task',
1766
- storyOptionId: 'opt_blue',
1767
- nameWithOwner: 'noslash',
1768
- });
1769
- expect(response).toEqual({
1770
- statusCode: 400,
1771
- body: { error: 'nameWithOwner must be in owner/repo format' },
1772
- });
1773
- });
1774
-
1775
- it('rejects when pjcode is not configured', async () => {
1776
- const response = await handleCreateIssue(context, {
1777
- pjcode: 'unknown',
1778
- title: 'Task',
1779
- storyOptionId: 'opt_blue',
1780
- nameWithOwner: 'acme-labs/portal',
1781
- });
1782
- expect(response).toEqual({
1783
- statusCode: 400,
1784
- body: { error: 'no project configured for pjcode "unknown"' },
1785
- });
1786
- });
1787
-
1788
- it('rejects when the project has no story field', async () => {
1789
- const projectWithoutStory: Project = { ...project, story: null };
1790
-
1791
- const response = await handleCreateIssue(
1792
- contextForProject(projectWithoutStory),
1793
- {
1794
- pjcode: 'acme',
1795
- title: 'Task',
1796
- storyOptionId: 'opt_blue',
1797
- nameWithOwner: 'acme-labs/portal',
1798
- },
1799
- );
1800
- expect(response).toEqual({
1801
- statusCode: 400,
1802
- body: { error: 'project does not have a story field' },
1803
- });
1804
- });
1805
-
1806
- it('rejects when the storyOptionId is not found in the project', async () => {
1807
- const response = await handleCreateIssue(
1808
- contextForProject(projectWithStory()),
1809
- {
1810
- pjcode: 'acme',
1811
- title: 'Task',
1812
- storyOptionId: 'nonexistent',
1813
- nameWithOwner: 'acme-labs/portal',
1814
- },
1815
- );
1816
- expect(response).toEqual({
1817
- statusCode: 400,
1818
- body: { error: 'story option "nonexistent" not found in project' },
1819
- });
1820
- });
1821
- });
1822
-
1823
- describe('handleReorderStory', () => {
1824
- const projectWithOrderedStories = (): Project => ({
1825
- ...project,
1826
- story: {
1827
- name: 'Story',
1828
- fieldId: 'storyField',
1829
- databaseId: 1,
1830
- stories: [
1831
- { id: 'opt_a', name: 'Alpha', color: 'BLUE', description: '' },
1832
- { id: 'opt_b', name: 'Beta', color: 'GREEN', description: '' },
1833
- { id: 'opt_c', name: 'Gamma', color: 'RED', description: '' },
1834
- ],
1835
- workflowManagementStory: { id: 'wms', name: 'workflow' },
1836
- },
1837
- });
1838
-
1839
- const contextWithProjectRepository = (
1840
- resolveProjectRepository: ConsoleOperationContext['resolveProjectRepository'],
1841
- baseProject: Project = projectWithOrderedStories(),
1842
- ): ConsoleOperationContext => ({
1843
- ...contextForProject(baseProject),
1844
- resolveProjectRepository,
1845
- });
1846
-
1847
- it('returns 400 when pjcode is missing', async () => {
1848
- const response = await handleReorderStory(context, {
1849
- storyOptionId: 'opt_b',
1850
- direction: 'up',
1851
- });
1852
- expect(response).toEqual({
1853
- statusCode: 400,
1854
- body: { error: 'pjcode is required' },
1855
- });
1856
- });
1857
-
1858
- it('returns 400 when storyOptionId is missing', async () => {
1859
- const response = await handleReorderStory(context, {
1860
- pjcode: 'acme',
1861
- direction: 'up',
1862
- });
1863
- expect(response).toEqual({
1864
- statusCode: 400,
1865
- body: { error: 'storyOptionId is required' },
1866
- });
1867
- });
1868
-
1869
- it('returns 400 when direction is not "up" or "down"', async () => {
1870
- const response = await handleReorderStory(context, {
1871
- pjcode: 'acme',
1872
- storyOptionId: 'opt_b',
1873
- direction: 'left',
1874
- });
1875
- expect(response).toEqual({
1876
- statusCode: 400,
1877
- body: { error: 'direction must be "up" or "down"' },
1878
- });
1879
- });
1880
-
1881
- it('returns 502 when resolveProjectRepository is null', async () => {
1882
- const response = await handleReorderStory(
1883
- contextWithProjectRepository(null),
1884
- {
1885
- pjcode: 'acme',
1886
- storyOptionId: 'opt_b',
1887
- direction: 'up',
1888
- },
1889
- );
1890
- expect(response).toEqual({
1891
- statusCode: 502,
1892
- body: { error: 'project repository is not configured' },
1893
- });
1894
- });
1895
-
1896
- it('returns 400 when project is not found', async () => {
1897
- const updateStoryList = jest.fn().mockResolvedValue([]);
1898
- const response = await handleReorderStory(
1899
- contextWithProjectRepository(() => ({ updateStoryList })),
1900
- {
1901
- pjcode: 'unknown',
1902
- storyOptionId: 'opt_b',
1903
- direction: 'up',
1904
- },
1905
- );
1906
- expect(response).toEqual({
1907
- statusCode: 400,
1908
- body: { error: 'no project configured for pjcode "unknown"' },
1909
- });
1910
- });
1911
-
1912
- it('returns 400 when story option is not found', async () => {
1913
- const updateStoryList = jest.fn().mockResolvedValue([]);
1914
- const response = await handleReorderStory(
1915
- contextWithProjectRepository(() => ({ updateStoryList })),
1916
- {
1917
- pjcode: 'acme',
1918
- storyOptionId: 'opt_nonexistent',
1919
- direction: 'up',
1920
- },
1921
- );
1922
- expect(response).toEqual({
1923
- statusCode: 400,
1924
- body: { error: 'story option not found' },
1925
- });
1926
- });
1927
-
1928
- it('returns 400 when direction is "up" and the option is already first', async () => {
1929
- const updateStoryList = jest.fn().mockResolvedValue([]);
1930
- const response = await handleReorderStory(
1931
- contextWithProjectRepository(() => ({ updateStoryList })),
1932
- {
1933
- pjcode: 'acme',
1934
- storyOptionId: 'opt_a',
1935
- direction: 'up',
1936
- },
1937
- );
1938
- expect(response).toEqual({
1939
- statusCode: 400,
1940
- body: { error: 'cannot move in that direction' },
1941
- });
1942
- });
1943
-
1944
- it('returns 400 when direction is "down" and the option is already last', async () => {
1945
- const updateStoryList = jest.fn().mockResolvedValue([]);
1946
- const response = await handleReorderStory(
1947
- contextWithProjectRepository(() => ({ updateStoryList })),
1948
- {
1949
- pjcode: 'acme',
1950
- storyOptionId: 'opt_c',
1951
- direction: 'down',
1952
- },
1953
- );
1954
- expect(response).toEqual({
1955
- statusCode: 400,
1956
- body: { error: 'cannot move in that direction' },
1957
- });
1958
- });
1959
-
1960
- it('swaps the target option with its neighbor, calls resolveProjectRepository updateStoryList with reordered list, and returns 200', async () => {
1961
- const updateStoryList = jest.fn().mockResolvedValue([]);
1962
- const resolveProjectRepository = jest.fn(() => ({ updateStoryList }));
1963
- const response = await handleReorderStory(
1964
- contextWithProjectRepository(resolveProjectRepository),
1965
- {
1966
- pjcode: 'acme',
1967
- storyOptionId: 'opt_b',
1968
- direction: 'up',
1969
- },
1970
- );
1971
- expect(response).toEqual({ statusCode: 200, body: { ok: true } });
1972
- expect(resolveProjectRepository).toHaveBeenCalledWith(
1973
- projectWithOrderedStories().url,
1974
- );
1975
- expect(updateStoryList).toHaveBeenCalledTimes(1);
1976
- expect(updateStoryList).toHaveBeenCalledWith(
1977
- expect.objectContaining({ id: projectWithOrderedStories().id }),
1978
- [
1979
- expect.objectContaining({ id: 'opt_b' }),
1980
- expect.objectContaining({ id: 'opt_a' }),
1981
- expect.objectContaining({ id: 'opt_c' }),
1982
- ],
1983
- );
1984
- });
1985
-
1986
- it('calls invalidateProject with pjcode after successful reorder', async () => {
1987
- const updateStoryList = jest.fn().mockResolvedValue([]);
1988
- const invalidateProject = jest.fn();
1989
- const response = await handleReorderStory(
1990
- {
1991
- ...contextWithProjectRepository(() => ({ updateStoryList })),
1992
- invalidateProject,
1993
- },
1994
- {
1995
- pjcode: 'acme',
1996
- storyOptionId: 'opt_b',
1997
- direction: 'up',
1998
- },
1999
- );
2000
- expect(response).toEqual({ statusCode: 200, body: { ok: true } });
2001
- expect(invalidateProject).toHaveBeenCalledWith('acme');
2002
- });
2003
-
2004
- it('returns 400 when project does not have a story field', async () => {
2005
- const projectWithoutStory = {
2006
- ...projectWithOrderedStories(),
2007
- story: null,
2008
- };
2009
- const updateStoryList = jest.fn().mockResolvedValue([]);
2010
- const response = await handleReorderStory(
2011
- contextWithProjectRepository(
2012
- () => ({ updateStoryList }),
2013
- projectWithoutStory,
2014
- ),
2015
- {
2016
- pjcode: 'acme',
2017
- storyOptionId: 'opt_b',
2018
- direction: 'up',
2019
- },
2020
- );
2021
- expect(response).toEqual({
2022
- statusCode: 400,
2023
- body: { error: 'project does not have a story field' },
2024
- });
2025
- });
2026
-
2027
- it('passes the full story list including GRAY options to resolveProjectRepository updateStoryList', async () => {
2028
- const projectWithGray = {
2029
- ...projectWithOrderedStories(),
2030
- story: {
2031
- name: 'Story',
2032
- fieldId: 'storyField',
2033
- databaseId: 1,
2034
- stories: [
2035
- {
2036
- id: 'opt_a',
2037
- name: 'Alpha',
2038
- color: 'BLUE' as const,
2039
- description: '',
2040
- },
2041
- {
2042
- id: 'opt_gray',
2043
- name: 'Archived',
2044
- color: 'GRAY' as const,
2045
- description: '',
2046
- },
2047
- {
2048
- id: 'opt_b',
2049
- name: 'Beta',
2050
- color: 'GREEN' as const,
2051
- description: '',
2052
- },
2053
- ],
2054
- workflowManagementStory: { id: 'wms', name: 'workflow' },
2055
- },
2056
- };
2057
- const updateStoryList = jest.fn().mockResolvedValue([]);
2058
- const response = await handleReorderStory(
2059
- contextWithProjectRepository(
2060
- () => ({ updateStoryList }),
2061
- projectWithGray,
2062
- ),
2063
- {
2064
- pjcode: 'acme',
2065
- storyOptionId: 'opt_b',
2066
- direction: 'up',
2067
- },
2068
- );
2069
- expect(response).toEqual({ statusCode: 200, body: { ok: true } });
2070
- expect(updateStoryList).toHaveBeenCalledWith(
2071
- expect.objectContaining({ id: projectWithGray.id }),
2072
- [
2073
- expect.objectContaining({ id: 'opt_a' }),
2074
- expect.objectContaining({ id: 'opt_b' }),
2075
- expect.objectContaining({ id: 'opt_gray' }),
2076
- ],
2077
- );
2078
- });
2079
- });
2080
-
2081
- describe('handleStoryAdd', () => {
2082
- const buildProjectWithStories = (): Project => ({
2083
- ...project,
2084
- url: 'https://github.com/orgs/acme-labs/projects/1',
2085
- story: {
2086
- name: 'Story',
2087
- fieldId: 'storyField',
2088
- databaseId: 1,
2089
- stories: [
2090
- {
2091
- id: 'opt_first',
2092
- name: 'First story',
2093
- color: 'BLUE',
2094
- description: '',
2095
- },
2096
- {
2097
- id: 'opt_second',
2098
- name: 'Second story',
2099
- color: 'GREEN',
2100
- description: '',
2101
- },
2102
- ],
2103
- workflowManagementStory: { id: 'wms', name: 'workflow' },
2104
- },
2105
- });
2106
-
2107
- const updateStoryList = jest.fn();
2108
- const projectRepositoryResolver = jest.fn(() => ({ updateStoryList }));
2109
-
2110
- const addStoryContext = (p: Project): ConsoleOperationContext => ({
2111
- ...contextForProject(p),
2112
- resolveProjectRepository: projectRepositoryResolver,
2113
- });
2114
-
2115
- beforeEach(() => {
2116
- updateStoryList.mockResolvedValue([]);
2117
- projectRepositoryResolver.mockReturnValue({ updateStoryList });
2118
- });
2119
-
2120
- it('inserts the new story at index 1 and calls updateStoryList', async () => {
2121
- const p = buildProjectWithStories();
2122
- const response = await handleStoryAdd(addStoryContext(p), {
2123
- pjcode: 'acme',
2124
- storyName: 'Brand new story',
2125
- });
2126
- expect(response.statusCode).toBe(200);
2127
- expect(response.body).toEqual({ ok: true });
2128
- expect(updateStoryList).toHaveBeenCalledWith(p, [
2129
- {
2130
- id: 'opt_first',
2131
- name: 'First story',
2132
- color: 'BLUE',
2133
- description: '',
2134
- },
2135
- { id: null, name: 'Brand new story', color: 'RED', description: '' },
2136
- {
2137
- id: 'opt_second',
2138
- name: 'Second story',
2139
- color: 'GREEN',
2140
- description: '',
2141
- },
2142
- ]);
2143
- });
2144
-
2145
- it('inserts the new story as the only entry when the story list is empty', async () => {
2146
- const emptyStoryProject: Project = {
2147
- ...buildProjectWithStories(),
2148
- story: {
2149
- name: 'Story',
2150
- fieldId: 'storyField',
2151
- databaseId: 1,
2152
- stories: [],
2153
- workflowManagementStory: { id: 'wms', name: 'workflow' },
2154
- },
2155
- };
2156
- const response = await handleStoryAdd(
2157
- addStoryContext(emptyStoryProject),
2158
- {
2159
- pjcode: 'acme',
2160
- storyName: 'First ever story',
2161
- },
2162
- );
2163
- expect(response.statusCode).toBe(200);
2164
- expect(updateStoryList).toHaveBeenCalledWith(emptyStoryProject, [
2165
- { id: null, name: 'First ever story', color: 'RED', description: '' },
2166
- ]);
2167
- });
2168
-
2169
- it('resolves the project repository using the project url', async () => {
2170
- const p = buildProjectWithStories();
2171
- await handleStoryAdd(addStoryContext(p), {
2172
- pjcode: 'acme',
2173
- storyName: 'Any story',
2174
- });
2175
- expect(projectRepositoryResolver).toHaveBeenCalledWith(p.url);
2176
- });
2177
-
2178
- it('returns 502 when resolveProjectRepository is null', async () => {
2179
- const response = await handleStoryAdd(
2180
- {
2181
- ...contextForProject(buildProjectWithStories()),
2182
- resolveProjectRepository: null,
2183
- },
2184
- { pjcode: 'acme', storyName: 'Any story' },
2185
- );
2186
- expect(response).toEqual({
2187
- statusCode: 502,
2188
- body: { error: 'project repository is not configured' },
2189
- });
2190
- });
2191
-
2192
- it('rejects when storyName is missing', async () => {
2193
- const response = await handleStoryAdd(
2194
- addStoryContext(buildProjectWithStories()),
2195
- {
2196
- pjcode: 'acme',
2197
- },
2198
- );
2199
- expect(response).toEqual({
2200
- statusCode: 400,
2201
- body: { error: 'storyName is required' },
2202
- });
2203
- });
2204
-
2205
- it('rejects when pjcode is not configured', async () => {
2206
- const response = await handleStoryAdd(
2207
- addStoryContext(buildProjectWithStories()),
2208
- {
2209
- pjcode: 'unknown',
2210
- storyName: 'Any story',
2211
- },
2212
- );
2213
- expect(response).toEqual({
2214
- statusCode: 400,
2215
- body: { error: 'no project configured for pjcode "unknown"' },
2216
- });
2217
- });
2218
-
2219
- it('rejects when the project has no story field', async () => {
2220
- const projectWithoutStory: Project = {
2221
- ...buildProjectWithStories(),
2222
- story: null,
2223
- };
2224
- const response = await handleStoryAdd(
2225
- addStoryContext(projectWithoutStory),
2226
- {
2227
- pjcode: 'acme',
2228
- storyName: 'Any story',
2229
- },
2230
- );
2231
- expect(response).toEqual({
2232
- statusCode: 400,
2233
- body: { error: 'project does not have a story field' },
2234
- });
2235
- });
2236
-
2237
- it('invalidates the project cache after each successful add so consecutive adds both survive', async () => {
2238
- const p = buildProjectWithStories();
2239
- const pStory = p.story;
2240
- if (pStory === null) {
2241
- throw new Error('buildProjectWithStories must return a non-null story');
2242
- }
2243
-
2244
- const projectAfterFirstAdd: Project = {
2245
- ...p,
2246
- story: {
2247
- ...pStory,
2248
- stories: [
2249
- pStory.stories[0],
2250
- { id: 'opt_x', name: 'Story X', color: 'RED', description: '' },
2251
- ...pStory.stories.slice(1),
2252
- ],
2253
- },
2254
- };
2255
-
2256
- let invalidateCalled = false;
2257
- const invalidateProject = jest.fn((_pjcode: string) => {
2258
- invalidateCalled = true;
2259
- });
2260
-
2261
- const resolveProject = jest.fn(async (pjcode: string) => {
2262
- if (pjcode !== 'acme') return null;
2263
- return { pjcode, project: invalidateCalled ? projectAfterFirstAdd : p };
2264
- });
2265
-
2266
- const consecutiveUpdateStoryList = jest.fn().mockResolvedValue([]);
2267
- const consecutiveContext: ConsoleOperationContext = {
2268
- resolveIssueRepository: () => issueRepository,
2269
- resolveProject,
2270
- isPjcodeConfigured: (pjcode: string) => pjcode === 'acme',
2271
- consoleDataOutputDir: baseDir,
2272
- issueAttachmentRepository: null,
2273
- resolveProjectRepository: jest.fn(() => ({
2274
- updateStoryList: consecutiveUpdateStoryList,
2275
- })),
2276
- invalidateProject,
2277
- updateProjectCacheEntry: null,
2278
- };
2279
-
2280
- await handleStoryAdd(consecutiveContext, {
2281
- pjcode: 'acme',
2282
- storyName: 'Story X',
2283
- });
2284
- await handleStoryAdd(consecutiveContext, {
2285
- pjcode: 'acme',
2286
- storyName: 'Story Y',
2287
- });
2288
-
2289
- expect(invalidateProject).toHaveBeenCalledTimes(2);
2290
- expect(invalidateProject).toHaveBeenCalledWith('acme');
2291
- expect(consecutiveUpdateStoryList).toHaveBeenNthCalledWith(
2292
- 2,
2293
- expect.anything(),
2294
- expect.arrayContaining([
2295
- expect.objectContaining({ name: 'Story X' }),
2296
- expect.objectContaining({ name: 'Story Y' }),
2297
- ]),
2298
- );
2299
- });
2300
- });
2301
-
2302
- describe('handleStoryColor', () => {
2303
- const projectWithStory = (): Project => ({
2304
- ...project,
2305
- story: {
2306
- name: 'Story',
2307
- fieldId: 'storyField',
2308
- databaseId: 1,
2309
- stories: [
2310
- {
2311
- id: 'opt_blue',
2312
- name: 'Portal redesign',
2313
- color: 'BLUE',
2314
- description: '',
2315
- },
2316
- {
2317
- id: 'opt_green',
2318
- name: 'Move to Okinawa',
2319
- color: 'GREEN',
2320
- description: '',
2321
- },
2322
- ],
2323
- workflowManagementStory: { id: 'wms', name: 'workflow' },
2324
- },
2325
- });
2326
-
2327
- beforeEach(() => {
2328
- issueRepository.updateStoryOptionColor.mockResolvedValue(undefined);
2329
- });
2330
-
2331
- it('calls updateStoryOptionColor with the correct project, storyOptionId, and newColor', async () => {
2332
- const storyProject = projectWithStory();
2333
- const response = await handleStoryColor(contextForProject(storyProject), {
2334
- pjcode: 'acme',
2335
- storyOptionId: 'opt_blue',
2336
- newColor: 'RED',
2337
- nameWithOwner: 'acme-labs/portal',
2338
- });
2339
-
2340
- expect(response.statusCode).toBe(200);
2341
- expect(issueRepository.updateStoryOptionColor).toHaveBeenCalledWith(
2342
- expect.objectContaining({ story: storyProject.story }),
2343
- 'opt_blue',
2344
- 'RED',
2345
- );
2346
- });
2347
-
2348
- it('resolves the issue repository from a proxy url of the target repo', async () => {
2349
- const resolvedUrls: string[] = [];
2350
- const recordingContext: ConsoleOperationContext = {
2351
- ...contextForProject(projectWithStory()),
2352
- resolveIssueRepository: (url: string) => {
2353
- resolvedUrls.push(url);
2354
- return issueRepository;
2355
- },
2356
- };
2357
-
2358
- await handleStoryColor(recordingContext, {
2359
- pjcode: 'acme',
2360
- storyOptionId: 'opt_blue',
2361
- newColor: 'GREEN',
2362
- nameWithOwner: 'acme-labs/portal',
2363
- });
2364
-
2365
- expect(resolvedUrls).toContain(
2366
- 'https://github.com/acme-labs/portal/issues/0',
2367
- );
2368
- });
2369
-
2370
- it('rejects when storyOptionId is missing', async () => {
2371
- const response = await handleStoryColor(
2372
- contextForProject(projectWithStory()),
2373
- {
2374
- pjcode: 'acme',
2375
- newColor: 'RED',
2376
- nameWithOwner: 'acme-labs/portal',
2377
- },
2378
- );
2379
- expect(response).toEqual({
2380
- statusCode: 400,
2381
- body: { error: 'storyOptionId is required' },
2382
- });
2383
- });
2384
-
2385
- it('rejects when newColor is not a valid ConsoleColor', async () => {
2386
- const response = await handleStoryColor(
2387
- contextForProject(projectWithStory()),
2388
- {
2389
- pjcode: 'acme',
2390
- storyOptionId: 'opt_blue',
2391
- newColor: 'MAGENTA',
2392
- nameWithOwner: 'acme-labs/portal',
2393
- },
2394
- );
2395
- expect(response).toEqual({
2396
- statusCode: 400,
2397
- body: {
2398
- error:
2399
- 'newColor must be one of GRAY, BLUE, GREEN, YELLOW, ORANGE, RED, PINK, PURPLE',
2400
- },
2401
- });
2402
- });
2403
-
2404
- it('rejects when nameWithOwner is missing', async () => {
2405
- const response = await handleStoryColor(
2406
- contextForProject(projectWithStory()),
2407
- {
2408
- pjcode: 'acme',
2409
- storyOptionId: 'opt_blue',
2410
- newColor: 'RED',
2411
- },
2412
- );
2413
- expect(response).toEqual({
2414
- statusCode: 400,
2415
- body: { error: 'nameWithOwner is required' },
2416
- });
2417
- });
2418
-
2419
- it('rejects when the project has no story field', async () => {
2420
- const projectWithoutStory: Project = { ...project, story: null };
2421
-
2422
- const response = await handleStoryColor(
2423
- contextForProject(projectWithoutStory),
2424
- {
2425
- pjcode: 'acme',
2426
- storyOptionId: 'opt_blue',
2427
- newColor: 'RED',
2428
- nameWithOwner: 'acme-labs/portal',
2429
- },
2430
- );
2431
- expect(response).toEqual({
2432
- statusCode: 400,
2433
- body: { error: 'project does not have a story field' },
2434
- });
2435
- });
2436
-
2437
- it('rejects when storyOptionId is not found in project.story.stories', async () => {
2438
- const response = await handleStoryColor(
2439
- contextForProject(projectWithStory()),
2440
- {
2441
- pjcode: 'acme',
2442
- storyOptionId: 'opt_nonexistent',
2443
- newColor: 'RED',
2444
- nameWithOwner: 'acme-labs/portal',
2445
- },
2446
- );
2447
- expect(response).toEqual({
2448
- statusCode: 400,
2449
- body: { error: 'story option "opt_nonexistent" not found in project' },
2450
- });
2451
- expect(issueRepository.updateStoryOptionColor).not.toHaveBeenCalled();
2452
- });
2453
-
2454
- it('calls updateProjectCacheEntry with the updated project after a successful color change', async () => {
2455
- const storyProject = projectWithStory();
2456
- const updatedEntries: { pjcode: string; project: Project }[] = [];
2457
- const contextWithCache: ConsoleOperationContext = {
2458
- ...contextForProject(storyProject),
2459
- updateProjectCacheEntry: (pjcode, updatedProject) => {
2460
- updatedEntries.push({ pjcode, project: updatedProject });
2461
- },
2462
- };
2463
-
2464
- await handleStoryColor(contextWithCache, {
2465
- pjcode: 'acme',
2466
- storyOptionId: 'opt_blue',
2467
- newColor: 'RED',
2468
- nameWithOwner: 'acme-labs/portal',
2469
- });
2470
-
2471
- expect(updatedEntries).toHaveLength(1);
2472
- expect(updatedEntries[0].pjcode).toBe('acme');
2473
- expect(updatedEntries[0].project.story?.stories).toEqual(
2474
- expect.arrayContaining([
2475
- expect.objectContaining({ id: 'opt_blue', color: 'RED' }),
2476
- expect.objectContaining({ id: 'opt_green', color: 'GREEN' }),
2477
- ]),
2478
- );
2479
- });
2480
-
2481
- it('does not call updateProjectCacheEntry when updateProjectCacheEntry is null', async () => {
2482
- const response = await handleStoryColor(
2483
- contextForProject(projectWithStory()),
2484
- {
2485
- pjcode: 'acme',
2486
- storyOptionId: 'opt_blue',
2487
- newColor: 'RED',
2488
- nameWithOwner: 'acme-labs/portal',
2489
- },
2490
- );
2491
- expect(response.statusCode).toBe(200);
2492
- });
2493
- });
2494
-
2495
- describe('handleDeleteAllComments', () => {
2496
- it('deletes all comments for the given issue url and returns 200', async () => {
2497
- issueRepository.deleteAllCommentsByUrl.mockResolvedValue(undefined);
2498
- const response = await handleDeleteAllComments(context, {
2499
- issueUrl: 'https://github.com/o/r/issues/1',
2500
- });
2501
- expect(issueRepository.deleteAllCommentsByUrl).toHaveBeenCalledWith(
2502
- 'https://github.com/o/r/issues/1',
2503
- );
2504
- expect(response.statusCode).toBe(200);
2505
- });
2506
-
2507
- it('returns 400 when issueUrl is missing', async () => {
2508
- const response = await handleDeleteAllComments(context, {});
2509
- expect(response.statusCode).toBe(400);
2510
- });
2511
-
2512
- it('propagates an error from deleteAllCommentsByUrl (resulting in 502 from webServer)', async () => {
2513
- issueRepository.deleteAllCommentsByUrl.mockRejectedValue(
2514
- new Error('API failure'),
2515
- );
2516
- await expect(
2517
- handleDeleteAllComments(context, {
2518
- issueUrl: 'https://github.com/o/r/issues/1',
2519
- }),
2520
- ).rejects.toThrow('API failure');
2521
- });
2522
- });
2523
- });