github-issue-tower-defence-management 2.19.1 → 2.19.3

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