github-issue-tower-defence-management 2.19.0 → 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 (783) hide show
  1. package/README.md +1 -1
  2. package/bin/adapter/entry-points/cli/index.js +0 -0
  3. package/bin/domain/usecases/OauthTokenSelectUseCase.js +1 -1
  4. package/package.json +5 -1
  5. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +1 -1
  6. package/.env.example +0 -7
  7. package/.gitattributes +0 -2
  8. package/.github/CODEOWNERS +0 -6
  9. package/.github/copilot-instructions.md +0 -6
  10. package/.github/dependabot.yml +0 -19
  11. package/.github/instructions/code-review.instructions.md +0 -50
  12. package/.github/workflows/api-created_issue_pr.yml +0 -97
  13. package/.github/workflows/ci-failure-issue-creator.yml +0 -48
  14. package/.github/workflows/close-manual-prs.yml +0 -80
  15. package/.github/workflows/codeql.yml +0 -107
  16. package/.github/workflows/commit-lint.yml +0 -85
  17. package/.github/workflows/configs/commitlint.config.cjs +0 -32
  18. package/.github/workflows/configs/commitlint.config.js +0 -32
  19. package/.github/workflows/console-ui.yml +0 -68
  20. package/.github/workflows/create-pr.yml +0 -116
  21. package/.github/workflows/empty-format-test-job.yml +0 -34
  22. package/.github/workflows/format.yml +0 -28
  23. package/.github/workflows/publish.yml +0 -111
  24. package/.github/workflows/secret-scan.yml +0 -51
  25. package/.github/workflows/test.yml +0 -58
  26. package/.github/workflows/umino-project.yml +0 -207
  27. package/.gitleaks.toml +0 -22
  28. package/.prettierignore +0 -31
  29. package/.prettierrc +0 -5
  30. package/CHANGELOG.md +0 -4155
  31. package/CONTRIBUTING.md +0 -60
  32. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js +0 -12
  33. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js.map +0 -1
  34. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js +0 -125
  35. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js.map +0 -1
  36. package/bin/adapter/repositories/issue/issueTimelineUtils.js +0 -38
  37. package/bin/adapter/repositories/issue/issueTimelineUtils.js.map +0 -1
  38. package/bin/domain/entities/UnansweredOwnerCall.js +0 -3
  39. package/bin/domain/entities/UnansweredOwnerCall.js.map +0 -1
  40. package/bin/domain/entities/WorkingTime.js +0 -3
  41. package/bin/domain/entities/WorkingTime.js.map +0 -1
  42. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js +0 -37
  43. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js.map +0 -1
  44. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +0 -118
  45. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +0 -1
  46. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js +0 -180
  47. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js.map +0 -1
  48. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js +0 -160
  49. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js.map +0 -1
  50. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js +0 -3
  51. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js.map +0 -1
  52. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js +0 -7
  53. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js.map +0 -1
  54. package/bin/domain/usecases/findLastAgentDeclaringReport.js +0 -12
  55. package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +0 -1
  56. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js +0 -25
  57. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js.map +0 -1
  58. package/bin/domain/usecases/isWaitingForOwnerApproval.js +0 -16
  59. package/bin/domain/usecases/isWaitingForOwnerApproval.js.map +0 -1
  60. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js +0 -7
  61. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js.map +0 -1
  62. package/commitlint.config.js +0 -6
  63. package/eslint.config.mjs +0 -76
  64. package/jest.config.js +0 -84
  65. package/jest.setup.js +0 -1
  66. package/lib/buffer-equal-constant-time-compat/index.js +0 -17
  67. package/lib/buffer-equal-constant-time-compat/index.test.js +0 -54
  68. package/lib/buffer-equal-constant-time-compat/package.json +0 -5
  69. package/lib/typescript-compat/bin/tsc +0 -2
  70. package/lib/typescript-compat/bin/tsserver +0 -2
  71. package/lib/typescript-compat/lib/typescript.js +0 -1
  72. package/lib/typescript-compat/package.json +0 -13
  73. package/renovate.json +0 -46
  74. package/scripts/copyConsoleUiDist.mjs +0 -35
  75. package/scripts/defaultBranchTipVerify.sh +0 -23
  76. package/scripts/testWorkflowRunVerify.sh +0 -47
  77. package/scripts/verifyConsoleUiDist.mjs +0 -96
  78. package/src/adapter/ci/consoleUiWorkflowTimeout.test.ts +0 -61
  79. package/src/adapter/ci/publishTestWorkflowGate.test.ts +0 -653
  80. package/src/adapter/ci/workflowRunCancellation.test.ts +0 -72
  81. package/src/adapter/entry-points/cli/fleetConfig.test.ts +0 -553
  82. package/src/adapter/entry-points/cli/fleetConfig.ts +0 -266
  83. package/src/adapter/entry-points/cli/index.test.ts +0 -2837
  84. package/src/adapter/entry-points/cli/index.ts +0 -1488
  85. package/src/adapter/entry-points/cli/projectConfig.test.ts +0 -342
  86. package/src/adapter/entry-points/cli/projectConfig.ts +0 -500
  87. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.test.ts +0 -658
  88. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.ts +0 -482
  89. package/src/adapter/entry-points/console/consoleDataDelivery.test.ts +0 -222
  90. package/src/adapter/entry-points/console/consoleDataDelivery.ts +0 -134
  91. package/src/adapter/entry-points/console/consoleDoneStore.test.ts +0 -169
  92. package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -139
  93. package/src/adapter/entry-points/console/consoleGithubTokenResolver.test.ts +0 -275
  94. package/src/adapter/entry-points/console/consoleGithubTokenResolver.ts +0 -113
  95. package/src/adapter/entry-points/console/consoleImageProxy.test.ts +0 -140
  96. package/src/adapter/entry-points/console/consoleImageProxy.ts +0 -111
  97. package/src/adapter/entry-points/console/consoleItemUrlLookup.test.ts +0 -63
  98. package/src/adapter/entry-points/console/consoleItemUrlLookup.ts +0 -66
  99. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +0 -2523
  100. package/src/adapter/entry-points/console/consoleOperationApi.ts +0 -936
  101. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +0 -246
  102. package/src/adapter/entry-points/console/consoleProjectResolver.ts +0 -111
  103. package/src/adapter/entry-points/console/consoleReadApi.test.ts +0 -714
  104. package/src/adapter/entry-points/console/consoleReadApi.ts +0 -361
  105. package/src/adapter/entry-points/console/consoleTabNames.test.ts +0 -52
  106. package/src/adapter/entry-points/console/consoleTabNames.ts +0 -37
  107. package/src/adapter/entry-points/console/consoleUiDistSync.test.ts +0 -66
  108. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +0 -595
  109. package/src/adapter/entry-points/console/dashboardComposeService.ts +0 -256
  110. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +0 -180
  111. package/src/adapter/entry-points/console/ensureConsoleRunning.test.ts +0 -115
  112. package/src/adapter/entry-points/console/ensureConsoleRunning.ts +0 -64
  113. package/src/adapter/entry-points/console/ui/.storybook/main.ts +0 -12
  114. package/src/adapter/entry-points/console/ui/.storybook/preview.ts +0 -15
  115. package/src/adapter/entry-points/console/ui/biome.json +0 -53
  116. package/src/adapter/entry-points/console/ui/components.json +0 -20
  117. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +0 -725
  118. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +0 -831
  119. package/src/adapter/entry-points/console/ui/e2e/playwright.config.ts +0 -30
  120. package/src/adapter/entry-points/console/ui/e2e/tsconfig.json +0 -18
  121. package/src/adapter/entry-points/console/ui/index.html +0 -12
  122. package/src/adapter/entry-points/console/ui/jest-globals.cjs +0 -26
  123. package/src/adapter/entry-points/console/ui/jest.setup.ts +0 -1
  124. package/src/adapter/entry-points/console/ui/jest.styleMock.js +0 -1
  125. package/src/adapter/entry-points/console/ui/public/sw.js +0 -60
  126. package/src/adapter/entry-points/console/ui/src/components/ui/badge.stories.tsx +0 -35
  127. package/src/adapter/entry-points/console/ui/src/components/ui/badge.tsx +0 -28
  128. package/src/adapter/entry-points/console/ui/src/components/ui/button.stories.tsx +0 -34
  129. package/src/adapter/entry-points/console/ui/src/components/ui/button.tsx +0 -50
  130. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.stories.tsx +0 -28
  131. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.test.tsx +0 -65
  132. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.tsx +0 -19
  133. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +0 -61
  134. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +0 -199
  135. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +0 -166
  136. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.stories.tsx +0 -22
  137. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.test.tsx +0 -74
  138. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.tsx +0 -84
  139. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.stories.tsx +0 -27
  140. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.test.tsx +0 -85
  141. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.tsx +0 -97
  142. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +0 -76
  143. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +0 -56
  144. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +0 -39
  145. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.stories.tsx +0 -32
  146. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.test.tsx +0 -105
  147. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.tsx +0 -157
  148. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +0 -68
  149. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +0 -612
  150. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +0 -276
  151. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.stories.tsx +0 -33
  152. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.test.tsx +0 -57
  153. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +0 -76
  154. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.stories.tsx +0 -25
  155. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.test.tsx +0 -55
  156. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.tsx +0 -49
  157. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.stories.tsx +0 -24
  158. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.test.tsx +0 -63
  159. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.tsx +0 -19
  160. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.stories.tsx +0 -33
  161. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.test.tsx +0 -62
  162. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.tsx +0 -59
  163. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.stories.tsx +0 -38
  164. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.test.tsx +0 -151
  165. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.tsx +0 -245
  166. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +0 -514
  167. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +0 -727
  168. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +0 -387
  169. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.stories.tsx +0 -82
  170. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.test.tsx +0 -52
  171. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.tsx +0 -32
  172. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.stories.tsx +0 -48
  173. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +0 -235
  174. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +0 -145
  175. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.stories.tsx +0 -29
  176. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.test.tsx +0 -30
  177. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.tsx +0 -50
  178. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +0 -38
  179. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +0 -54
  180. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +0 -60
  181. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.stories.tsx +0 -56
  182. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.test.tsx +0 -111
  183. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.tsx +0 -92
  184. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.stories.tsx +0 -51
  185. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.test.tsx +0 -59
  186. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.tsx +0 -39
  187. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +0 -207
  188. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +0 -332
  189. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +0 -211
  190. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.stories.tsx +0 -49
  191. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.test.tsx +0 -157
  192. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.tsx +0 -118
  193. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.stories.tsx +0 -23
  194. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.test.tsx +0 -40
  195. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.tsx +0 -17
  196. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.stories.tsx +0 -101
  197. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.test.tsx +0 -102
  198. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.tsx +0 -75
  199. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.stories.tsx +0 -40
  200. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.test.tsx +0 -213
  201. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.tsx +0 -115
  202. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.stories.tsx +0 -50
  203. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.test.tsx +0 -98
  204. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.tsx +0 -66
  205. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.stories.tsx +0 -69
  206. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.test.tsx +0 -101
  207. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.tsx +0 -75
  208. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.stories.tsx +0 -137
  209. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.test.tsx +0 -554
  210. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.tsx +0 -400
  211. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.stories.tsx +0 -30
  212. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.test.tsx +0 -111
  213. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.tsx +0 -91
  214. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.stories.tsx +0 -27
  215. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.test.tsx +0 -24
  216. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.tsx +0 -28
  217. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.stories.tsx +0 -14
  218. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.test.tsx +0 -21
  219. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +0 -26
  220. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +0 -14
  221. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +0 -40
  222. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +0 -37
  223. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleErrorToast.stories.tsx +0 -33
  224. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.stories.tsx +0 -20
  225. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +0 -48
  226. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +0 -42
  227. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.stories.tsx +0 -115
  228. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.test.tsx +0 -262
  229. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.tsx +0 -123
  230. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +0 -65
  231. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +0 -159
  232. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +0 -55
  233. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.stories.tsx +0 -20
  234. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.test.tsx +0 -93
  235. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.tsx +0 -45
  236. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.stories.tsx +0 -17
  237. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.test.tsx +0 -49
  238. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.tsx +0 -66
  239. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.stories.tsx +0 -47
  240. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.test.tsx +0 -95
  241. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.tsx +0 -62
  242. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.stories.tsx +0 -32
  243. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.test.tsx +0 -101
  244. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.tsx +0 -80
  245. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.test.ts +0 -304
  246. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.ts +0 -167
  247. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.test.ts +0 -498
  248. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.ts +0 -277
  249. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +0 -99
  250. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +0 -90
  251. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +0 -110
  252. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +0 -83
  253. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.test.ts +0 -51
  254. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.ts +0 -39
  255. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +0 -229
  256. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +0 -194
  257. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +0 -151
  258. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.ts +0 -128
  259. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +0 -629
  260. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +0 -417
  261. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +0 -40
  262. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +0 -71
  263. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.test.ts +0 -24
  264. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.ts +0 -17
  265. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.test.ts +0 -97
  266. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.ts +0 -44
  267. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.test.ts +0 -59
  268. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.ts +0 -26
  269. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +0 -48
  270. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +0 -39
  271. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +0 -41
  272. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +0 -57
  273. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.test.ts +0 -115
  274. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.ts +0 -115
  275. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +0 -546
  276. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +0 -278
  277. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.test.ts +0 -110
  278. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.ts +0 -70
  279. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.test.ts +0 -387
  280. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.ts +0 -407
  281. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +0 -446
  282. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +0 -533
  283. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.test.ts +0 -78
  284. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.ts +0 -34
  285. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +0 -252
  286. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +0 -207
  287. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +0 -27
  288. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +0 -71
  289. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.test.ts +0 -88
  290. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.ts +0 -34
  291. package/src/adapter/entry-points/console/ui/src/features/console/lib/navigation.ts +0 -7
  292. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +0 -104
  293. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +0 -79
  294. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +0 -200
  295. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +0 -122
  296. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.test.ts +0 -90
  297. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.ts +0 -37
  298. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.test.ts +0 -34
  299. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.ts +0 -85
  300. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.test.ts +0 -81
  301. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.ts +0 -61
  302. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.test.ts +0 -35
  303. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.ts +0 -21
  304. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.test.ts +0 -96
  305. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.ts +0 -93
  306. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +0 -229
  307. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.ts +0 -106
  308. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.test.ts +0 -18
  309. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.ts +0 -16
  310. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.test.ts +0 -97
  311. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.ts +0 -95
  312. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.test.ts +0 -53
  313. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.ts +0 -33
  314. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.test.ts +0 -72
  315. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +0 -61
  316. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +0 -325
  317. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +0 -94
  318. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.test.ts +0 -44
  319. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.ts +0 -17
  320. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +0 -103
  321. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +0 -64
  322. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.test.ts +0 -74
  323. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.ts +0 -53
  324. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.test.ts +0 -34
  325. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.ts +0 -28
  326. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +0 -89
  327. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.ts +0 -30
  328. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.test.ts +0 -61
  329. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.ts +0 -77
  330. package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -173
  331. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +0 -629
  332. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +0 -351
  333. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +0 -1469
  334. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +0 -736
  335. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +0 -25
  336. package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +0 -418
  337. package/src/adapter/entry-points/console/ui/src/index.css +0 -1592
  338. package/src/adapter/entry-points/console/ui/src/lib/utils.ts +0 -4
  339. package/src/adapter/entry-points/console/ui/src/main.tsx +0 -21
  340. package/src/adapter/entry-points/console/ui/src/vite-env.d.ts +0 -1
  341. package/src/adapter/entry-points/console/ui/tsconfig.json +0 -26
  342. package/src/adapter/entry-points/console/ui/vite.config.ts +0 -24
  343. package/src/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +0 -88
  344. package/src/adapter/entry-points/console/ui-dist/assets/index-KyQISRuQ.css +0 -2
  345. package/src/adapter/entry-points/console/ui-dist/index.html +0 -13
  346. package/src/adapter/entry-points/console/ui-dist/sw.js +0 -60
  347. package/src/adapter/entry-points/console/webServer.test.ts +0 -2945
  348. package/src/adapter/entry-points/console/webServer.ts +0 -940
  349. package/src/adapter/entry-points/function/getStoryObjectMap.ts +0 -3
  350. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.test.ts +0 -218
  351. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.ts +0 -113
  352. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +0 -110
  353. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +0 -116
  354. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -859
  355. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -984
  356. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +0 -126
  357. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts +0 -80
  358. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +0 -491
  359. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +0 -137
  360. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +0 -508
  361. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +0 -156
  362. package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +0 -332
  363. package/src/adapter/entry-points/handlers/consoleListsWriter.ts +0 -60
  364. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +0 -112
  365. package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +0 -51
  366. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +0 -285
  367. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -109
  368. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +0 -170
  369. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts +0 -40
  370. package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +0 -191
  371. package/src/adapter/entry-points/handlers/machineStatusWriter.ts +0 -110
  372. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +0 -548
  373. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +0 -192
  374. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +0 -111
  375. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.ts +0 -25
  376. package/src/adapter/entry-points/handlers/ownerCallFileStore.test.ts +0 -286
  377. package/src/adapter/entry-points/handlers/ownerCallFileStore.ts +0 -145
  378. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.test.ts +0 -55
  379. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.ts +0 -36
  380. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.test.ts +0 -72
  381. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +0 -74
  382. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +0 -181
  383. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +0 -18
  384. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +0 -489
  385. package/src/adapter/entry-points/handlers/situationFileWriter.ts +0 -176
  386. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +0 -169
  387. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +0 -32
  388. package/src/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.ts +0 -41
  389. package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +0 -81
  390. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +0 -266
  391. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +0 -262
  392. package/src/adapter/proxy/ClaudeMessageResponseParser.test.ts +0 -211
  393. package/src/adapter/proxy/ClaudeMessageResponseParser.ts +0 -181
  394. package/src/adapter/proxy/RateLimitCache.test.ts +0 -1077
  395. package/src/adapter/proxy/RateLimitCache.ts +0 -385
  396. package/src/adapter/proxy/TokenListLoader.test.ts +0 -183
  397. package/src/adapter/proxy/TokenListLoader.ts +0 -75
  398. package/src/adapter/proxy/ensureProxyRunning.test.ts +0 -85
  399. package/src/adapter/proxy/ensureProxyRunning.ts +0 -41
  400. package/src/adapter/proxy/proxyEntry.test.ts +0 -552
  401. package/src/adapter/proxy/proxyEntry.ts +0 -214
  402. package/src/adapter/repositories/BaseGitHubRepository.test.ts +0 -26
  403. package/src/adapter/repositories/BaseGitHubRepository.ts +0 -26
  404. package/src/adapter/repositories/BrowserGitHubProjectRepository.test.ts +0 -108
  405. package/src/adapter/repositories/BrowserGitHubProjectRepository.ts +0 -118
  406. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.test.ts +0 -71
  407. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.ts +0 -21
  408. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +0 -324
  409. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +0 -148
  410. package/src/adapter/repositories/FetchWebhookRepository.ts +0 -8
  411. package/src/adapter/repositories/FileHandoverStateRepository.test.ts +0 -114
  412. package/src/adapter/repositories/FileHandoverStateRepository.ts +0 -73
  413. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +0 -514
  414. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +0 -109
  415. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.test.ts +0 -284
  416. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.ts +0 -128
  417. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.test.ts +0 -138
  418. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts +0 -113
  419. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.test.ts +0 -121
  420. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts +0 -103
  421. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +0 -360
  422. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +0 -77
  423. package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +0 -59
  424. package/src/adapter/repositories/FileSystemSessionRecordReader.ts +0 -31
  425. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +0 -287
  426. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +0 -125
  427. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.test.ts +0 -285
  428. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts +0 -119
  429. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.test.ts +0 -237
  430. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts +0 -57
  431. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.test.ts +0 -56
  432. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.ts +0 -32
  433. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.test.ts +0 -68
  434. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.ts +0 -23
  435. package/src/adapter/repositories/GitHubIssueCheckpointRepository.test.ts +0 -51
  436. package/src/adapter/repositories/GitHubIssueCheckpointRepository.ts +0 -47
  437. package/src/adapter/repositories/GitHubIssueCommentRepository.test.ts +0 -278
  438. package/src/adapter/repositories/GitHubIssueCommentRepository.ts +0 -109
  439. package/src/adapter/repositories/GoogleSpreadsheetRepository.integration.test.ts +0 -265
  440. package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +0 -431
  441. package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +0 -268
  442. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +0 -272
  443. package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -111
  444. package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +0 -254
  445. package/src/adapter/repositories/GraphqlProjectRepository.test.ts +0 -209
  446. package/src/adapter/repositories/GraphqlProjectRepository.ts +0 -707
  447. package/src/adapter/repositories/GraphqlProjectRepositoryProjectLocation.test.ts +0 -297
  448. package/src/adapter/repositories/KyHttpRepository.ts +0 -8
  449. package/src/adapter/repositories/KySlackRepository.test.ts +0 -172
  450. package/src/adapter/repositories/KySlackRepository.ts +0 -298
  451. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.test.ts +0 -193
  452. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.ts +0 -106
  453. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +0 -190
  454. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +0 -102
  455. package/src/adapter/repositories/LocalStorageCacheRepository.test.ts +0 -265
  456. package/src/adapter/repositories/LocalStorageCacheRepository.ts +0 -79
  457. package/src/adapter/repositories/LocalStorageRepository.integration.test.ts +0 -142
  458. package/src/adapter/repositories/LocalStorageRepository.test.ts +0 -173
  459. package/src/adapter/repositories/LocalStorageRepository.ts +0 -30
  460. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +0 -15
  461. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +0 -9
  462. package/src/adapter/repositories/NodeLocalCommandRunner.largeOutput.test.ts +0 -16
  463. package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +0 -118
  464. package/src/adapter/repositories/NodeLocalCommandRunner.ts +0 -66
  465. package/src/adapter/repositories/NodeProcessSignalRepository.test.ts +0 -82
  466. package/src/adapter/repositories/NodeProcessSignalRepository.ts +0 -47
  467. package/src/adapter/repositories/NodeSubAgentProcessLister.test.ts +0 -44
  468. package/src/adapter/repositories/NodeSubAgentProcessLister.ts +0 -40
  469. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +0 -546
  470. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +0 -245
  471. package/src/adapter/repositories/OauthAPIClaudeRepository.ts +0 -279
  472. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.test.ts +0 -166
  473. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.ts +0 -32
  474. package/src/adapter/repositories/OauthProxyClaudeRepository.test.ts +0 -428
  475. package/src/adapter/repositories/OauthProxyClaudeRepository.ts +0 -124
  476. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.test.ts +0 -242
  477. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.ts +0 -261
  478. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.test.ts +0 -184
  479. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts +0 -138
  480. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.test.ts +0 -208
  481. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.ts +0 -130
  482. package/src/adapter/repositories/ProcFsProcessEnvironReader.test.ts +0 -60
  483. package/src/adapter/repositories/ProcFsProcessEnvironReader.ts +0 -38
  484. package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +0 -207
  485. package/src/adapter/repositories/ProcHostMetricsRepository.ts +0 -181
  486. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +0 -231
  487. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +0 -134
  488. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.test.ts +0 -121
  489. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.ts +0 -58
  490. package/src/adapter/repositories/ProjectIssuesCacheRepository.test.ts +0 -218
  491. package/src/adapter/repositories/ProjectIssuesCacheRepository.ts +0 -141
  492. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +0 -1195
  493. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +0 -185
  494. package/src/adapter/repositories/ProxyRateLimitCacheRepository.test.ts +0 -116
  495. package/src/adapter/repositories/ProxyRateLimitCacheRepository.ts +0 -67
  496. package/src/adapter/repositories/RateLimitSnapshotRepository.ts +0 -50
  497. package/src/adapter/repositories/RealSleeper.test.ts +0 -19
  498. package/src/adapter/repositories/RealSleeper.ts +0 -12
  499. package/src/adapter/repositories/RestProjectRepository.test.ts +0 -277
  500. package/src/adapter/repositories/RestProjectRepository.ts +0 -129
  501. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.test.ts +0 -313
  502. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.ts +0 -164
  503. package/src/adapter/repositories/SystemDateRepository.test.ts +0 -60
  504. package/src/adapter/repositories/SystemDateRepository.ts +0 -34
  505. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +0 -253
  506. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +0 -158
  507. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.queuedReply.test.ts +0 -171
  508. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +0 -86
  509. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -996
  510. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -396
  511. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.test.ts +0 -208
  512. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.ts +0 -80
  513. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +0 -1674
  514. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +0 -459
  515. package/src/adapter/repositories/clSessionScopeUnitName.test.ts +0 -76
  516. package/src/adapter/repositories/clSessionScopeUnitName.ts +0 -5
  517. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.test.ts +0 -34
  518. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.ts +0 -8
  519. package/src/adapter/repositories/githubGraphqlClient.test.ts +0 -479
  520. package/src/adapter/repositories/githubGraphqlClient.ts +0 -243
  521. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +0 -5182
  522. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +0 -3303
  523. package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -46
  524. package/src/adapter/repositories/issue/ApiV3IssueRepository.ts +0 -88
  525. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -1776
  526. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +0 -1552
  527. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -393
  528. package/src/adapter/repositories/issue/RestIssueRepository.ts +0 -252
  529. package/src/adapter/repositories/issue/gitHubRawUrl.test.ts +0 -29
  530. package/src/adapter/repositories/issue/gitHubRawUrl.ts +0 -30
  531. package/src/adapter/repositories/issue/githubRateLimitRetry.test.ts +0 -167
  532. package/src/adapter/repositories/issue/githubRateLimitRetry.ts +0 -109
  533. package/src/adapter/repositories/localStorageCacheDirectory.test.ts +0 -81
  534. package/src/adapter/repositories/localStorageCacheDirectory.ts +0 -13
  535. package/src/adapter/repositories/projectFieldDefinition.test.ts +0 -191
  536. package/src/adapter/repositories/projectFieldDefinition.ts +0 -144
  537. package/src/adapter/repositories/utils.test.ts +0 -55
  538. package/src/adapter/repositories/utils.ts +0 -45
  539. package/src/domain/entities/ClaudeHandoverSession.ts +0 -12
  540. package/src/domain/entities/ClaudeMessageResponse.ts +0 -31
  541. package/src/domain/entities/ClaudeTokenUsage.ts +0 -17
  542. package/src/domain/entities/ClaudeWindowUsage.ts +0 -5
  543. package/src/domain/entities/Comment.ts +0 -5
  544. package/src/domain/entities/InteractiveLiveSession.ts +0 -6
  545. package/src/domain/entities/Issue.ts +0 -31
  546. package/src/domain/entities/KevReportWatermark.ts +0 -4
  547. package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -14
  548. package/src/domain/entities/LiveSessionOutputActivity.ts +0 -4
  549. package/src/domain/entities/LiveSessionProcessSnapshot.ts +0 -18
  550. package/src/domain/entities/LiveTmuxSession.ts +0 -4
  551. package/src/domain/entities/Member.ts +0 -3
  552. package/src/domain/entities/OutputDegeneration.ts +0 -10
  553. package/src/domain/entities/Project.ts +0 -88
  554. package/src/domain/entities/ProjectField.ts +0 -3
  555. package/src/domain/entities/ProjectFieldName.ts +0 -2
  556. package/src/domain/entities/ProjectFieldSingleSelect.ts +0 -8
  557. package/src/domain/entities/ProjectFieldSingleSelectOption.ts +0 -8
  558. package/src/domain/entities/RequiredProjectField.ts +0 -68
  559. package/src/domain/entities/SearchedIssue.ts +0 -11
  560. package/src/domain/entities/StoryObjectMap.ts +0 -12
  561. package/src/domain/entities/TokenExhaustionHandoverState.ts +0 -8
  562. package/src/domain/entities/WorkflowStatus.ts +0 -66
  563. package/src/domain/usecases/ActionAnnouncementUseCase.test.ts +0 -91
  564. package/src/domain/usecases/ActionAnnouncementUseCase.ts +0 -78
  565. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.test.ts +0 -222
  566. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.ts +0 -76
  567. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.test.ts +0 -118
  568. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.ts +0 -112
  569. package/src/domain/usecases/AnalyzeStoriesUseCase.test.ts +0 -82
  570. package/src/domain/usecases/AnalyzeStoriesUseCase.ts +0 -296
  571. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.test.ts +0 -578
  572. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts +0 -137
  573. package/src/domain/usecases/CONTRIBUTING_FOR_TEST.md +0 -259
  574. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.test.ts +0 -437
  575. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.ts +0 -80
  576. package/src/domain/usecases/ChangeTargetPullRequestApprover.test.ts +0 -248
  577. package/src/domain/usecases/ChangeTargetPullRequestApprover.ts +0 -69
  578. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +0 -411
  579. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +0 -142
  580. package/src/domain/usecases/ClearDependedIssueURLUseCase.test.ts +0 -1120
  581. package/src/domain/usecases/ClearDependedIssueURLUseCase.ts +0 -161
  582. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.test.ts +0 -345
  583. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.ts +0 -81
  584. package/src/domain/usecases/CliErrorReportUseCase.test.ts +0 -162
  585. package/src/domain/usecases/CliErrorReportUseCase.ts +0 -72
  586. package/src/domain/usecases/ConflictedIssueRevertUseCase.test.ts +0 -886
  587. package/src/domain/usecases/ConflictedIssueRevertUseCase.ts +0 -146
  588. package/src/domain/usecases/CreateEstimationIssueUseCase.test.ts +0 -227
  589. package/src/domain/usecases/CreateEstimationIssueUseCase.ts +0 -205
  590. package/src/domain/usecases/CreateNewStoryByLabelUseCase.test.ts +0 -1084
  591. package/src/domain/usecases/CreateNewStoryByLabelUseCase.ts +0 -146
  592. package/src/domain/usecases/DailySecurityScanUseCase.test.ts +0 -1709
  593. package/src/domain/usecases/DailySecurityScanUseCase.ts +0 -476
  594. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +0 -533
  595. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +0 -122
  596. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +0 -330
  597. package/src/domain/usecases/GetStoryObjectMapUseCase.ts +0 -79
  598. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -1913
  599. package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -846
  600. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +0 -175
  601. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts +0 -63
  602. package/src/domain/usecases/IssueNoStatusUpdateUseCase.test.ts +0 -165
  603. package/src/domain/usecases/IssueNoStatusUpdateUseCase.ts +0 -44
  604. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +0 -1137
  605. package/src/domain/usecases/IssueRejectionEvaluator.ts +0 -340
  606. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +0 -679
  607. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +0 -178
  608. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +0 -5441
  609. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +0 -891
  610. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +0 -2069
  611. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +0 -489
  612. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +0 -651
  613. package/src/domain/usecases/OauthTokenSelectUseCase.ts +0 -274
  614. package/src/domain/usecases/OutputDegenerationDetector.test.ts +0 -167
  615. package/src/domain/usecases/OutputDegenerationDetector.ts +0 -169
  616. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.test.ts +0 -711
  617. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.ts +0 -146
  618. package/src/domain/usecases/QualityCheckAdvanceUseCase.test.ts +0 -421
  619. package/src/domain/usecases/QualityCheckAdvanceUseCase.ts +0 -76
  620. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.test.ts +0 -188
  621. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.ts +0 -57
  622. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +0 -303
  623. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +0 -155
  624. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +0 -1838
  625. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -221
  626. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +0 -2392
  627. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +0 -499
  628. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.test.ts +0 -224
  629. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts +0 -142
  630. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +0 -360
  631. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +0 -70
  632. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.test.ts +0 -400
  633. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.ts +0 -48
  634. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +0 -696
  635. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +0 -201
  636. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +0 -1363
  637. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +0 -215
  638. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.test.ts +0 -170
  639. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.ts +0 -38
  640. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +0 -283
  641. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +0 -98
  642. package/src/domain/usecases/StartPreparationUseCase.test.ts +0 -7822
  643. package/src/domain/usecases/StartPreparationUseCase.ts +0 -876
  644. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.test.ts +0 -200
  645. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.ts +0 -74
  646. package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +0 -838
  647. package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +0 -439
  648. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.test.ts +0 -306
  649. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.ts +0 -64
  650. package/src/domain/usecases/UpdateRateLimitCacheUseCase.test.ts +0 -178
  651. package/src/domain/usecases/UpdateRateLimitCacheUseCase.ts +0 -22
  652. package/src/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.ts +0 -5
  653. package/src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts +0 -9
  654. package/src/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.ts +0 -8
  655. package/src/domain/usecases/adapter-interfaces/ClaudeMessageResponseRepository.ts +0 -5
  656. package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +0 -6
  657. package/src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts +0 -8
  658. package/src/domain/usecases/adapter-interfaces/ConsoleTabsRepository.ts +0 -12
  659. package/src/domain/usecases/adapter-interfaces/CopilotRepository.ts +0 -3
  660. package/src/domain/usecases/adapter-interfaces/DateRepository.ts +0 -7
  661. package/src/domain/usecases/adapter-interfaces/GitHubGraphqlRateLimitRepository.ts +0 -3
  662. package/src/domain/usecases/adapter-interfaces/HttpRepository.ts +0 -3
  663. package/src/domain/usecases/adapter-interfaces/InteractiveLiveSessionTranscriptResolver.ts +0 -7
  664. package/src/domain/usecases/adapter-interfaces/IssueAttachmentRepository.ts +0 -9
  665. package/src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts +0 -3
  666. package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +0 -7
  667. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +0 -229
  668. package/src/domain/usecases/adapter-interfaces/KevReportWatermarkRepository.ts +0 -11
  669. package/src/domain/usecases/adapter-interfaces/LiveSessionProcessSnapshotProvider.ts +0 -5
  670. package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +0 -16
  671. package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +0 -5
  672. package/src/domain/usecases/adapter-interfaces/ProcessEnvironReader.ts +0 -3
  673. package/src/domain/usecases/adapter-interfaces/ProcessSignalRepository.ts +0 -5
  674. package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +0 -29
  675. package/src/domain/usecases/adapter-interfaces/RateLimitCacheRepository.ts +0 -10
  676. package/src/domain/usecases/adapter-interfaces/RefusalTailStatusProvider.ts +0 -5
  677. package/src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts +0 -6
  678. package/src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts +0 -4
  679. package/src/domain/usecases/adapter-interfaces/SessionOutputActivityRepository.ts +0 -7
  680. package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +0 -3
  681. package/src/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.ts +0 -8
  682. package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +0 -10
  683. package/src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts +0 -20
  684. package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +0 -18
  685. package/src/domain/usecases/adapter-interfaces/SilentSessionNotificationRepository.ts +0 -6
  686. package/src/domain/usecases/adapter-interfaces/SlackRepository.ts +0 -20
  687. package/src/domain/usecases/adapter-interfaces/Sleeper.ts +0 -3
  688. package/src/domain/usecases/adapter-interfaces/SpreadsheetRepository.ts +0 -18
  689. package/src/domain/usecases/adapter-interfaces/StatusDefaultRepository.ts +0 -5
  690. package/src/domain/usecases/adapter-interfaces/SubAgentLivenessResolver.ts +0 -6
  691. package/src/domain/usecases/adapter-interfaces/SubAgentProcessLister.ts +0 -8
  692. package/src/domain/usecases/adapter-interfaces/SubAgentSilentSecondsResolver.ts +0 -3
  693. package/src/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.ts +0 -6
  694. package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +0 -9
  695. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +0 -20
  696. package/src/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.ts +0 -6
  697. package/src/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.ts +0 -22
  698. package/src/domain/usecases/adapter-interfaces/WebhookRepository.ts +0 -3
  699. package/src/domain/usecases/agentReportPrefix.ts +0 -1
  700. package/src/domain/usecases/autoStatusCheckComments.ts +0 -1
  701. package/src/domain/usecases/console/.gitkeep +0 -0
  702. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +0 -887
  703. package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -368
  704. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +0 -654
  705. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +0 -318
  706. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +0 -34
  707. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +0 -27
  708. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +0 -160
  709. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -69
  710. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +0 -362
  711. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +0 -234
  712. package/src/domain/usecases/ensureAgentOptionAndGetId.ts +0 -47
  713. package/src/domain/usecases/extractFencedJsonBlocks.test.ts +0 -40
  714. package/src/domain/usecases/extractFencedJsonBlocks.ts +0 -21
  715. package/src/domain/usecases/extractNextStepAgent.test.ts +0 -58
  716. package/src/domain/usecases/extractNextStepAgent.ts +0 -18
  717. package/src/domain/usecases/extractNextStepAgentFromComments.ts +0 -13
  718. package/src/domain/usecases/findLastAgentReport.test.ts +0 -121
  719. package/src/domain/usecases/findLastAgentReport.ts +0 -14
  720. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -339
  721. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +0 -188
  722. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +0 -389
  723. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts +0 -103
  724. package/src/domain/usecases/intmux/OwnerCallFile.test.ts +0 -184
  725. package/src/domain/usecases/intmux/OwnerCallFile.ts +0 -80
  726. package/src/domain/usecases/isAgentReportBody.test.ts +0 -108
  727. package/src/domain/usecases/isAgentReportBody.ts +0 -52
  728. package/src/domain/usecases/isAuthorAuthorizedForAutoStatusCheck.ts +0 -12
  729. package/src/domain/usecases/isHumanComment.test.ts +0 -77
  730. package/src/domain/usecases/isHumanComment.ts +0 -23
  731. package/src/domain/usecases/isRecord.ts +0 -2
  732. package/src/domain/usecases/issueReactivationTriggerIsPending.test.ts +0 -541
  733. package/src/domain/usecases/issueReactivationTriggerIsPending.ts +0 -25
  734. package/src/domain/usecases/nextStepAgentDispatchRepeatedMessage.ts +0 -2
  735. package/src/domain/usecases/normalizeReportBody.test.ts +0 -24
  736. package/src/domain/usecases/normalizeReportBody.ts +0 -2
  737. package/src/domain/usecases/resolveAllowedIssueAuthors.test.ts +0 -51
  738. package/src/domain/usecases/resolveAllowedIssueAuthors.ts +0 -6
  739. package/src/domain/usecases/resolveLabelsAsLlmAgentName.test.ts +0 -42
  740. package/src/domain/usecases/resolveLabelsAsLlmAgentName.ts +0 -6
  741. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.test.ts +0 -33
  742. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.ts +0 -7
  743. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.test.ts +0 -337
  744. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.ts +0 -133
  745. package/src/domain/usecases/silentSessionReminderSentinel.ts +0 -9
  746. package/src/domain/usecases/triagerAgentName.ts +0 -4
  747. package/src/domain/usecases/utils.test.ts +0 -35
  748. package/src/domain/usecases/utils.ts +0 -6
  749. package/src/index.test.ts +0 -8
  750. package/src/index.ts +0 -10
  751. package/src/releaseConfig.test.ts +0 -40
  752. package/tsconfig.build.json +0 -18
  753. package/tsconfig.json +0 -25
  754. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts +0 -13
  755. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts.map +0 -1
  756. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts +0 -16
  757. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts.map +0 -1
  758. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts +0 -12
  759. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts.map +0 -1
  760. package/types/domain/entities/UnansweredOwnerCall.d.ts +0 -5
  761. package/types/domain/entities/UnansweredOwnerCall.d.ts.map +0 -1
  762. package/types/domain/entities/WorkingTime.d.ts +0 -8
  763. package/types/domain/entities/WorkingTime.d.ts.map +0 -1
  764. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts +0 -17
  765. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts.map +0 -1
  766. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +0 -23
  767. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +0 -1
  768. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts +0 -50
  769. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts.map +0 -1
  770. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts +0 -15
  771. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts.map +0 -1
  772. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts +0 -11
  773. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts.map +0 -1
  774. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts +0 -3
  775. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts.map +0 -1
  776. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +0 -5
  777. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +0 -1
  778. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts +0 -5
  779. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts.map +0 -1
  780. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts +0 -2
  781. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts.map +0 -1
  782. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts +0 -3
  783. 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
- });