@jiggai/kitchen 0.3.1 → 0.3.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 (307) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +2 -2
  3. package/.next/server/app/_global-error.html +2 -2
  4. package/.next/server/app/_global-error.rsc +1 -1
  5. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  6. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/server/app/_not-found.html +1 -1
  11. package/.next/server/app/_not-found.rsc +1 -1
  12. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/channels.html +2 -2
  19. package/.next/server/app/channels.rsc +1 -1
  20. package/.next/server/app/channels.segments/_full.segment.rsc +1 -1
  21. package/.next/server/app/channels.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/channels.segments/_index.segment.rsc +1 -1
  23. package/.next/server/app/channels.segments/_tree.segment.rsc +1 -1
  24. package/.next/server/app/channels.segments/channels/__PAGE__.segment.rsc +1 -1
  25. package/.next/server/app/channels.segments/channels.segment.rsc +1 -1
  26. package/.next/server/app/cron-jobs.html +1 -1
  27. package/.next/server/app/cron-jobs.rsc +1 -1
  28. package/.next/server/app/cron-jobs.segments/_full.segment.rsc +1 -1
  29. package/.next/server/app/cron-jobs.segments/_head.segment.rsc +1 -1
  30. package/.next/server/app/cron-jobs.segments/_index.segment.rsc +1 -1
  31. package/.next/server/app/cron-jobs.segments/_tree.segment.rsc +1 -1
  32. package/.next/server/app/cron-jobs.segments/cron-jobs/__PAGE__.segment.rsc +1 -1
  33. package/.next/server/app/cron-jobs.segments/cron-jobs.segment.rsc +1 -1
  34. package/.next/server/app/goals/new.html +2 -2
  35. package/.next/server/app/goals/new.rsc +1 -1
  36. package/.next/server/app/goals/new.segments/_full.segment.rsc +1 -1
  37. package/.next/server/app/goals/new.segments/_head.segment.rsc +1 -1
  38. package/.next/server/app/goals/new.segments/_index.segment.rsc +1 -1
  39. package/.next/server/app/goals/new.segments/_tree.segment.rsc +1 -1
  40. package/.next/server/app/goals/new.segments/goals/new/__PAGE__.segment.rsc +1 -1
  41. package/.next/server/app/goals/new.segments/goals/new.segment.rsc +1 -1
  42. package/.next/server/app/goals/new.segments/goals.segment.rsc +1 -1
  43. package/.next/server/app/goals.html +1 -1
  44. package/.next/server/app/goals.rsc +1 -1
  45. package/.next/server/app/goals.segments/_full.segment.rsc +1 -1
  46. package/.next/server/app/goals.segments/_head.segment.rsc +1 -1
  47. package/.next/server/app/goals.segments/_index.segment.rsc +1 -1
  48. package/.next/server/app/goals.segments/_tree.segment.rsc +1 -1
  49. package/.next/server/app/goals.segments/goals/__PAGE__.segment.rsc +1 -1
  50. package/.next/server/app/goals.segments/goals.segment.rsc +1 -1
  51. package/.next/server/app/settings.html +1 -1
  52. package/.next/server/app/settings.rsc +1 -1
  53. package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  54. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  55. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  56. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  57. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
  58. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  59. package/.next/server/pages/404.html +1 -1
  60. package/.next/server/pages/500.html +2 -2
  61. package/package.json +1 -2
  62. package/src/app/HomeClient.tsx +0 -207
  63. package/src/app/agents/[agentId]/agent-editor-tabs.tsx +0 -298
  64. package/src/app/agents/[agentId]/agent-editor.tsx +0 -468
  65. package/src/app/agents/[agentId]/page.tsx +0 -32
  66. package/src/app/api/__tests__/agents-add-route.test.ts +0 -143
  67. package/src/app/api/__tests__/agents-file-route.test.ts +0 -117
  68. package/src/app/api/__tests__/agents-files-route.test.ts +0 -61
  69. package/src/app/api/__tests__/agents-id-route.test.ts +0 -104
  70. package/src/app/api/__tests__/agents-identity-route.test.ts +0 -92
  71. package/src/app/api/__tests__/agents-route.test.ts +0 -54
  72. package/src/app/api/__tests__/agents-skills-install-route.test.ts +0 -131
  73. package/src/app/api/__tests__/agents-skills-route.test.ts +0 -64
  74. package/src/app/api/__tests__/agents-update-route.test.ts +0 -95
  75. package/src/app/api/__tests__/channels-bindings-route.test.ts +0 -143
  76. package/src/app/api/__tests__/cron-delete-route.test.ts +0 -93
  77. package/src/app/api/__tests__/cron-job-route.test.ts +0 -78
  78. package/src/app/api/__tests__/cron-jobs-route.test.ts +0 -116
  79. package/src/app/api/__tests__/cron-recipe-installed-route.test.ts +0 -114
  80. package/src/app/api/__tests__/gateway-restart-route.test.ts +0 -36
  81. package/src/app/api/__tests__/goals-promote-route.test.ts +0 -200
  82. package/src/app/api/__tests__/goals-route.test.ts +0 -184
  83. package/src/app/api/__tests__/ids-check-route.test.ts +0 -188
  84. package/src/app/api/__tests__/marketplace-recipes-route.test.ts +0 -123
  85. package/src/app/api/__tests__/recipes-clone-route.test.ts +0 -221
  86. package/src/app/api/__tests__/recipes-delete-route.test.ts +0 -248
  87. package/src/app/api/__tests__/recipes-id-route.test.ts +0 -166
  88. package/src/app/api/__tests__/recipes-route.test.ts +0 -57
  89. package/src/app/api/__tests__/recipes-team-agents-route.test.ts +0 -135
  90. package/src/app/api/__tests__/scaffold-route.test.ts +0 -173
  91. package/src/app/api/__tests__/settings-cron-installation-route.test.ts +0 -82
  92. package/src/app/api/__tests__/skills-available-route.test.ts +0 -47
  93. package/src/app/api/__tests__/swarms-start-route.test.ts +0 -79
  94. package/src/app/api/__tests__/swarms-status-route.test.ts +0 -42
  95. package/src/app/api/__tests__/teams-file-route.test.ts +0 -129
  96. package/src/app/api/__tests__/teams-files-route.test.ts +0 -57
  97. package/src/app/api/__tests__/teams-meta-route.test.ts +0 -113
  98. package/src/app/api/__tests__/teams-orchestrator-install-route.test.ts +0 -66
  99. package/src/app/api/__tests__/teams-orchestrator-route.test.ts +0 -59
  100. package/src/app/api/__tests__/teams-remove-team-route.test.ts +0 -122
  101. package/src/app/api/__tests__/teams-skills-install-route.test.ts +0 -78
  102. package/src/app/api/__tests__/teams-skills-route.test.ts +0 -73
  103. package/src/app/api/__tests__/teams-workflow-runs-route.test.ts +0 -85
  104. package/src/app/api/__tests__/teams-workflows-route.test.ts +0 -110
  105. package/src/app/api/__tests__/tickets-move-route.test.ts +0 -60
  106. package/src/app/api/agents/[id]/route.ts +0 -83
  107. package/src/app/api/agents/add/route.ts +0 -114
  108. package/src/app/api/agents/file/route.ts +0 -45
  109. package/src/app/api/agents/files/route.ts +0 -23
  110. package/src/app/api/agents/identity/route.ts +0 -41
  111. package/src/app/api/agents/route.ts +0 -22
  112. package/src/app/api/agents/skills/install/route.ts +0 -34
  113. package/src/app/api/agents/skills/route.ts +0 -39
  114. package/src/app/api/agents/update/route.ts +0 -52
  115. package/src/app/api/channels/bindings/route.ts +0 -63
  116. package/src/app/api/cron/__tests__/helpers.test.ts +0 -164
  117. package/src/app/api/cron/delete/route.ts +0 -23
  118. package/src/app/api/cron/helpers.ts +0 -172
  119. package/src/app/api/cron/job/route.ts +0 -22
  120. package/src/app/api/cron/jobs/route.ts +0 -52
  121. package/src/app/api/cron/recipe-installed/route.ts +0 -65
  122. package/src/app/api/gateway/restart/route.ts +0 -20
  123. package/src/app/api/goals/[id]/promote/route.ts +0 -119
  124. package/src/app/api/goals/[id]/route.ts +0 -54
  125. package/src/app/api/goals/route.ts +0 -44
  126. package/src/app/api/ids/check/route.ts +0 -113
  127. package/src/app/api/marketplace/recipes/[slug]/route.ts +0 -16
  128. package/src/app/api/marketplace/recipes/route.ts +0 -22
  129. package/src/app/api/recipes/[id]/route.ts +0 -62
  130. package/src/app/api/recipes/clone/route.ts +0 -106
  131. package/src/app/api/recipes/custom-team/route.ts +0 -193
  132. package/src/app/api/recipes/delete/helpers.ts +0 -65
  133. package/src/app/api/recipes/delete/route.ts +0 -73
  134. package/src/app/api/recipes/route.ts +0 -21
  135. package/src/app/api/recipes/team-agents/__tests__/helpers.test.ts +0 -156
  136. package/src/app/api/recipes/team-agents/helpers.ts +0 -151
  137. package/src/app/api/recipes/team-agents/route.ts +0 -80
  138. package/src/app/api/scaffold/__tests__/helpers.test.ts +0 -186
  139. package/src/app/api/scaffold/helpers.ts +0 -214
  140. package/src/app/api/scaffold/route.ts +0 -95
  141. package/src/app/api/settings/cron-installation/route.ts +0 -58
  142. package/src/app/api/skills/available/route.ts +0 -23
  143. package/src/app/api/swarms/start/route.ts +0 -100
  144. package/src/app/api/swarms/status/route.ts +0 -31
  145. package/src/app/api/teams/[teamId]/tickets/assign/route.ts +0 -105
  146. package/src/app/api/teams/[teamId]/tickets/assignees/route.ts +0 -27
  147. package/src/app/api/teams/[teamId]/tickets/delete/route.ts +0 -55
  148. package/src/app/api/teams/[teamId]/tickets/move/route.ts +0 -70
  149. package/src/app/api/teams/[teamId]/tickets/move-to-goals/route.ts +0 -56
  150. package/src/app/api/teams/file/route.ts +0 -46
  151. package/src/app/api/teams/files/route.ts +0 -63
  152. package/src/app/api/teams/memory/route.ts +0 -250
  153. package/src/app/api/teams/meta/route.ts +0 -43
  154. package/src/app/api/teams/orchestrator/install/route.ts +0 -129
  155. package/src/app/api/teams/orchestrator/route.ts +0 -216
  156. package/src/app/api/teams/remove-team/route.ts +0 -37
  157. package/src/app/api/teams/skills/install/route.ts +0 -18
  158. package/src/app/api/teams/skills/route.ts +0 -25
  159. package/src/app/api/teams/workflow-runs/route.ts +0 -534
  160. package/src/app/api/teams/workflow-templates/route.ts +0 -71
  161. package/src/app/api/teams/workflows/route.ts +0 -55
  162. package/src/app/api/tickets/assign/route.ts +0 -94
  163. package/src/app/api/tickets/assignees/route.ts +0 -24
  164. package/src/app/api/tickets/move/route.ts +0 -69
  165. package/src/app/channels/channels-client.tsx +0 -271
  166. package/src/app/channels/page.tsx +0 -5
  167. package/src/app/cron-jobs/cron-jobs-client.tsx +0 -243
  168. package/src/app/cron-jobs/page.tsx +0 -34
  169. package/src/app/favicon.ico +0 -0
  170. package/src/app/global-error.tsx +0 -50
  171. package/src/app/globals.css +0 -153
  172. package/src/app/goals/[id]/goal-editor.tsx +0 -162
  173. package/src/app/goals/[id]/page.tsx +0 -6
  174. package/src/app/goals/goals-client.tsx +0 -201
  175. package/src/app/goals/new/page.tsx +0 -81
  176. package/src/app/goals/page.tsx +0 -10
  177. package/src/app/layout.tsx +0 -53
  178. package/src/app/manifest.ts +0 -15
  179. package/src/app/not-found.tsx +0 -8
  180. package/src/app/page.tsx +0 -33
  181. package/src/app/recipes/CreateAgentModal.tsx +0 -156
  182. package/src/app/recipes/CreateCustomTeamModal.tsx +0 -375
  183. package/src/app/recipes/CreateModalShell.tsx +0 -55
  184. package/src/app/recipes/CreateTeamModal.tsx +0 -91
  185. package/src/app/recipes/[id]/RecipeEditor/RecipeEditorCreateModal.tsx +0 -72
  186. package/src/app/recipes/[id]/RecipeEditor/RecipeEditorPanel.tsx +0 -216
  187. package/src/app/recipes/[id]/RecipeEditor/index.tsx +0 -271
  188. package/src/app/recipes/[id]/RecipeEditor/recipe-editor-utils.ts +0 -46
  189. package/src/app/recipes/[id]/RecipeEditor/types.ts +0 -52
  190. package/src/app/recipes/[id]/page.tsx +0 -37
  191. package/src/app/recipes/page.tsx +0 -101
  192. package/src/app/recipes/recipes-client.tsx +0 -620
  193. package/src/app/settings/page.tsx +0 -26
  194. package/src/app/settings/settings-client.tsx +0 -91
  195. package/src/app/teams/[teamId]/CloneTeamModal.tsx +0 -116
  196. package/src/app/teams/[teamId]/OrchestratorPanel.tsx +0 -255
  197. package/src/app/teams/[teamId]/OrchestratorSetupModal.tsx +0 -184
  198. package/src/app/teams/[teamId]/PublishChangesModal.tsx +0 -43
  199. package/src/app/teams/[teamId]/page.tsx +0 -49
  200. package/src/app/teams/[teamId]/team-editor/TeamAgentsTab.tsx +0 -145
  201. package/src/app/teams/[teamId]/team-editor/TeamCronTab.tsx +0 -72
  202. package/src/app/teams/[teamId]/team-editor/TeamFilesTab.tsx +0 -74
  203. package/src/app/teams/[teamId]/team-editor/TeamMemoryTab.tsx +0 -349
  204. package/src/app/teams/[teamId]/team-editor/TeamRecipeTab.tsx +0 -151
  205. package/src/app/teams/[teamId]/team-editor/TeamSkillsTab.tsx +0 -68
  206. package/src/app/teams/[teamId]/team-editor/index.tsx +0 -558
  207. package/src/app/teams/[teamId]/team-editor/team-editor-data.ts +0 -255
  208. package/src/app/teams/[teamId]/team-editor/team-editor-utils.ts +0 -78
  209. package/src/app/teams/[teamId]/team-editor/types.ts +0 -34
  210. package/src/app/teams/[teamId]/tickets/[ticket]/page.tsx +0 -35
  211. package/src/app/teams/[teamId]/tickets/page.tsx +0 -15
  212. package/src/app/teams/[teamId]/workflows/[workflowId]/WorkflowCanvas.tsx +0 -111
  213. package/src/app/teams/[teamId]/workflows/[workflowId]/page.tsx +0 -27
  214. package/src/app/teams/[teamId]/workflows/[workflowId]/workflows-editor-client.tsx +0 -1608
  215. package/src/app/teams/[teamId]/workflows/page.tsx +0 -40
  216. package/src/app/teams/[teamId]/workflows/workflows-client.tsx +0 -494
  217. package/src/app/tickets/TicketDetailClient.tsx +0 -147
  218. package/src/app/tickets/TicketsBoardClient.tsx +0 -200
  219. package/src/app/tickets/[ticket]/TicketAssignControl.tsx +0 -112
  220. package/src/app/tickets/[ticket]/page.tsx +0 -36
  221. package/src/app/tickets/page.tsx +0 -10
  222. package/src/components/AppShell.tsx +0 -286
  223. package/src/components/ConfirmationModal.tsx +0 -81
  224. package/src/components/DeleteEntityModal.tsx +0 -41
  225. package/src/components/ErrorBoundary.tsx +0 -70
  226. package/src/components/FileListWithOptionalToggle.tsx +0 -86
  227. package/src/components/GoalFormFields.tsx +0 -163
  228. package/src/components/ScaffoldOverlay.tsx +0 -78
  229. package/src/components/ThemeToggle.tsx +0 -53
  230. package/src/components/ToastProvider.tsx +0 -163
  231. package/src/components/__tests__/ConfirmationModal.test.tsx +0 -109
  232. package/src/components/__tests__/ErrorBoundary.test.tsx +0 -39
  233. package/src/components/__tests__/FileListWithOptionalToggle.test.tsx +0 -109
  234. package/src/components/__tests__/GoalFormFields.test.tsx +0 -117
  235. package/src/components/delete-modals.tsx +0 -59
  236. package/src/components/icons.tsx +0 -48
  237. package/src/lib/__tests__/agent-workspace.test.ts +0 -44
  238. package/src/lib/__tests__/agents.test.ts +0 -36
  239. package/src/lib/__tests__/api-route-helpers.test.ts +0 -188
  240. package/src/lib/__tests__/cron.test.ts +0 -45
  241. package/src/lib/__tests__/editor-utils.test.ts +0 -38
  242. package/src/lib/__tests__/errors.test.ts +0 -15
  243. package/src/lib/__tests__/exec.test.ts +0 -13
  244. package/src/lib/__tests__/fetch-json.test.ts +0 -118
  245. package/src/lib/__tests__/gateway.test.ts +0 -234
  246. package/src/lib/__tests__/goal-promote.test.ts +0 -39
  247. package/src/lib/__tests__/goals-client.test.ts +0 -26
  248. package/src/lib/__tests__/goals.test.ts +0 -275
  249. package/src/lib/__tests__/json.test.ts +0 -15
  250. package/src/lib/__tests__/kitchen-api.test.ts +0 -32
  251. package/src/lib/__tests__/marketplace.test.ts +0 -116
  252. package/src/lib/__tests__/openclaw.test.ts +0 -129
  253. package/src/lib/__tests__/paths.test.ts +0 -136
  254. package/src/lib/__tests__/poll.test.ts +0 -26
  255. package/src/lib/__tests__/recipe-clone.test.ts +0 -85
  256. package/src/lib/__tests__/recipe-team-agents.test.ts +0 -70
  257. package/src/lib/__tests__/recipes.test.ts +0 -199
  258. package/src/lib/__tests__/scaffold-client.test.ts +0 -106
  259. package/src/lib/__tests__/scaffold.test.ts +0 -64
  260. package/src/lib/__tests__/slugify.test.ts +0 -23
  261. package/src/lib/__tests__/tickets.test.ts +0 -158
  262. package/src/lib/__tests__/type-guards.test.ts +0 -18
  263. package/src/lib/__tests__/use-slugified-id.test.tsx +0 -120
  264. package/src/lib/agent-workspace.ts +0 -14
  265. package/src/lib/agents.ts +0 -17
  266. package/src/lib/api-route-helpers.ts +0 -157
  267. package/src/lib/cron.ts +0 -40
  268. package/src/lib/editor-utils.ts +0 -18
  269. package/src/lib/errors.ts +0 -7
  270. package/src/lib/exec.ts +0 -4
  271. package/src/lib/fetch-json.ts +0 -29
  272. package/src/lib/gateway.ts +0 -100
  273. package/src/lib/goal-promote.ts +0 -27
  274. package/src/lib/goals-client.ts +0 -69
  275. package/src/lib/goals.ts +0 -171
  276. package/src/lib/json.ts +0 -10
  277. package/src/lib/kitchen-api.ts +0 -19
  278. package/src/lib/marketplace.ts +0 -46
  279. package/src/lib/openclaw.ts +0 -59
  280. package/src/lib/paths.ts +0 -69
  281. package/src/lib/poll.ts +0 -18
  282. package/src/lib/recipe-clone.ts +0 -42
  283. package/src/lib/recipe-team-agents.ts +0 -30
  284. package/src/lib/recipes.ts +0 -95
  285. package/src/lib/scaffold-client.ts +0 -31
  286. package/src/lib/scaffold.ts +0 -37
  287. package/src/lib/slugify.ts +0 -25
  288. package/src/lib/swarms.ts +0 -25
  289. package/src/lib/tickets.ts +0 -192
  290. package/src/lib/type-guards.ts +0 -3
  291. package/src/lib/use-slugified-id.ts +0 -35
  292. package/src/lib/workflows/README.md +0 -11
  293. package/src/lib/workflows/__tests__/storage.test.ts +0 -129
  294. package/src/lib/workflows/__tests__/validate.test.ts +0 -92
  295. package/src/lib/workflows/api-handlers.ts +0 -35
  296. package/src/lib/workflows/readdir.ts +0 -23
  297. package/src/lib/workflows/runs-storage.ts +0 -59
  298. package/src/lib/workflows/runs-types.ts +0 -42
  299. package/src/lib/workflows/storage.ts +0 -70
  300. package/src/lib/workflows/templates/index.ts +0 -1
  301. package/src/lib/workflows/templates/marketing-cadence-v1.ts +0 -142
  302. package/src/lib/workflows/types.ts +0 -48
  303. package/src/lib/workflows/validate.ts +0 -92
  304. package/src/proxy.ts +0 -28
  305. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_buildManifest.js +0 -0
  306. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_clientMiddlewareManifest.json +0 -0
  307. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_ssgManifest.js +0 -0
@@ -1,200 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import { POST } from "../goals/[id]/promote/route";
3
- import path from "node:path";
4
-
5
- vi.mock("@/lib/openclaw", () => ({ runOpenClaw: vi.fn() }));
6
-
7
- import { runOpenClaw } from "@/lib/openclaw";
8
- vi.mock("@/lib/goals", async (importOriginal) => {
9
- const actual = await importOriginal<typeof import("@/lib/goals")>();
10
- return { ...actual, readGoal: vi.fn(), writeGoal: vi.fn() };
11
- });
12
- vi.mock("@/lib/paths", () => ({
13
- getTeamWorkspaceDir: vi.fn(),
14
- readOpenClawConfig: vi.fn(),
15
- }));
16
- vi.mock("node:fs/promises", () => ({
17
- default: { mkdir: vi.fn(), writeFile: vi.fn() },
18
- }));
19
-
20
- import { readGoal, writeGoal } from "@/lib/goals";
21
- import { getTeamWorkspaceDir, readOpenClawConfig } from "@/lib/paths";
22
- import fs from "node:fs/promises";
23
-
24
- describe("api goals promote route", () => {
25
- const teamWs = "/mock-workspace/development-team";
26
- const existingGoal = {
27
- id: "my-goal",
28
- frontmatter: { id: "my-goal", title: "My Goal", tags: [], teams: [] },
29
- body: "## Existing body",
30
- raw: "",
31
- };
32
-
33
- beforeEach(() => {
34
- vi.mocked(runOpenClaw).mockReset();
35
- vi.mocked(readGoal).mockReset();
36
- vi.mocked(writeGoal).mockReset();
37
- vi.mocked(getTeamWorkspaceDir).mockReset();
38
- vi.mocked(readOpenClawConfig).mockReset();
39
- vi.mocked(fs.mkdir).mockReset();
40
- vi.mocked(fs.writeFile).mockReset();
41
-
42
- vi.mocked(getTeamWorkspaceDir).mockResolvedValue(teamWs);
43
- vi.mocked(fs.mkdir).mockResolvedValue(undefined);
44
- vi.mocked(fs.writeFile).mockResolvedValue(undefined);
45
- });
46
-
47
- it("returns 404 when goal not found", async () => {
48
- vi.mocked(readGoal).mockResolvedValue(null);
49
-
50
- const res = await POST(
51
- new Request("https://test"),
52
- { params: Promise.resolve({ id: "missing" }) }
53
- );
54
- expect(res.status).toBe(404);
55
- const json = await res.json();
56
- expect(json.error).toBe("Goal not found");
57
- });
58
-
59
- it("creates inbox file and updates goal on success", async () => {
60
- vi.mocked(readGoal).mockResolvedValue(existingGoal);
61
- vi.mocked(writeGoal).mockResolvedValue({
62
- frontmatter: { ...existingGoal.frontmatter, status: "active" },
63
- body: "",
64
- raw: "",
65
- });
66
- vi.mocked(readOpenClawConfig).mockResolvedValue({});
67
-
68
- const res = await POST(
69
- new Request("https://test"),
70
- { params: Promise.resolve({ id: "my-goal" }) }
71
- );
72
- expect(res.status).toBe(200);
73
- const json = await res.json();
74
- expect(json.ok).toBe(true);
75
- expect(json.goal.id).toBe("my-goal");
76
- expect(json.inboxPath).toContain("inbox");
77
- expect(json.inboxPath).toContain("goal");
78
- expect(fs.mkdir).toHaveBeenCalledWith(path.join(teamWs, "inbox"), { recursive: true });
79
- expect(fs.writeFile).toHaveBeenCalledWith(
80
- expect.stringContaining("inbox"),
81
- expect.stringContaining("My Goal"),
82
- expect.any(Object)
83
- );
84
- expect(writeGoal).toHaveBeenCalledWith(
85
- expect.objectContaining({
86
- id: "my-goal",
87
- status: "active",
88
- body: expect.stringContaining("## Workflow"),
89
- })
90
- );
91
- });
92
-
93
- it("pingAttempted false when agentToAgent disabled", async () => {
94
- vi.mocked(readGoal).mockResolvedValue(existingGoal);
95
- vi.mocked(writeGoal).mockResolvedValue({
96
- frontmatter: existingGoal.frontmatter,
97
- body: "",
98
- raw: "",
99
- });
100
- vi.mocked(readOpenClawConfig).mockResolvedValue({
101
- tools: { agentToAgent: { enabled: false } },
102
- });
103
-
104
- const res = await POST(
105
- new Request("https://test"),
106
- { params: Promise.resolve({ id: "my-goal" }) }
107
- );
108
- expect(res.status).toBe(200);
109
- const json = await res.json();
110
- expect(json.pingAttempted).toBe(false);
111
- expect(runOpenClaw).not.toHaveBeenCalled();
112
- });
113
-
114
- it("pingAttempted true and pingOk true when agentToAgent enabled", async () => {
115
- vi.mocked(readGoal).mockResolvedValue(existingGoal);
116
- vi.mocked(writeGoal).mockResolvedValue({
117
- frontmatter: existingGoal.frontmatter,
118
- body: "",
119
- raw: "",
120
- });
121
- vi.mocked(readOpenClawConfig).mockResolvedValue({
122
- tools: { agentToAgent: { enabled: true, allow: ["*"] } },
123
- });
124
- vi.mocked(runOpenClaw).mockResolvedValue({ ok: true, exitCode: 0, stdout: "", stderr: "" });
125
-
126
- const res = await POST(
127
- new Request("https://test"),
128
- { params: Promise.resolve({ id: "my-goal" }) }
129
- );
130
- expect(res.status).toBe(200);
131
- const json = await res.json();
132
- expect(json.pingAttempted).toBe(true);
133
- expect(json.pingOk).toBe(true);
134
- expect(runOpenClaw).toHaveBeenCalledWith(
135
- expect.arrayContaining(["agent", "--agent", "development-team-lead"])
136
- );
137
- });
138
-
139
- it("pingOk false when exec fails", async () => {
140
- vi.mocked(readGoal).mockResolvedValue(existingGoal);
141
- vi.mocked(writeGoal).mockResolvedValue({
142
- frontmatter: existingGoal.frontmatter,
143
- body: "",
144
- raw: "",
145
- });
146
- vi.mocked(readOpenClawConfig).mockResolvedValue({
147
- tools: { agentToAgent: { enabled: true, allow: ["*"] } },
148
- });
149
- vi.mocked(runOpenClaw).mockRejectedValue(new Error("Command failed"));
150
-
151
- const res = await POST(
152
- new Request("https://test"),
153
- { params: Promise.resolve({ id: "my-goal" }) }
154
- );
155
- expect(res.status).toBe(200);
156
- const json = await res.json();
157
- expect(json.pingAttempted).toBe(true);
158
- expect(json.pingOk).toBe(false);
159
- expect(json.pingReason).toBe("Command failed");
160
- });
161
-
162
- it("uses alternate filename when EEXIST", async () => {
163
- vi.mocked(readGoal).mockResolvedValue(existingGoal);
164
- vi.mocked(writeGoal).mockResolvedValue({
165
- frontmatter: existingGoal.frontmatter,
166
- body: "",
167
- raw: "",
168
- });
169
- vi.mocked(readOpenClawConfig).mockResolvedValue({});
170
-
171
- const eexist = new Error("EEXIST") as Error & { code?: string };
172
- eexist.code = "EEXIST";
173
-
174
- vi.mocked(fs.writeFile)
175
- .mockRejectedValueOnce(eexist)
176
- .mockResolvedValueOnce(undefined);
177
-
178
- const res = await POST(
179
- new Request("https://test"),
180
- { params: Promise.resolve({ id: "my-goal" }) }
181
- );
182
- expect(res.status).toBe(200);
183
- expect(fs.writeFile).toHaveBeenCalledTimes(2);
184
- const secondPath = (fs.writeFile as ReturnType<typeof vi.fn>).mock.calls[1][0];
185
- expect(secondPath).toContain("-goal-");
186
- expect(secondPath).toContain(".md");
187
- });
188
-
189
- it("returns 500 when readGoal throws", async () => {
190
- vi.mocked(readGoal).mockRejectedValue(new Error("File system error"));
191
-
192
- const res = await POST(
193
- new Request("https://test"),
194
- { params: Promise.resolve({ id: "my-goal" }) }
195
- );
196
- expect(res.status).toBe(500);
197
- const json = (await res.json()) as { error?: string };
198
- expect(json.error).toBe("File system error");
199
- });
200
- });
@@ -1,184 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import { GET, POST } from "../goals/route";
3
- import { GET as GET_ID, PUT, DELETE } from "../goals/[id]/route";
4
-
5
- vi.mock("@/lib/goals", async (importOriginal) => {
6
- const actual = await importOriginal<typeof import("@/lib/goals")>();
7
- return {
8
- ...actual,
9
- listGoals: vi.fn(),
10
- readGoal: vi.fn(),
11
- writeGoal: vi.fn(),
12
- deleteGoal: vi.fn(),
13
- };
14
- });
15
-
16
- import { listGoals, readGoal, writeGoal, deleteGoal } from "@/lib/goals";
17
-
18
- describe("api goals route", () => {
19
- beforeEach(() => {
20
- vi.mocked(listGoals).mockReset();
21
- vi.mocked(readGoal).mockReset();
22
- vi.mocked(writeGoal).mockReset();
23
- vi.mocked(deleteGoal).mockReset();
24
- });
25
-
26
- describe("GET /api/goals", () => {
27
- it("returns goals when listGoals succeeds", async () => {
28
- vi.mocked(listGoals).mockResolvedValue([
29
- { id: "g1", frontmatter: { id: "g1", title: "Goal 1" }, body: "", raw: "" },
30
- ]);
31
- const res = await GET();
32
- expect(res.status).toBe(200);
33
- const json = await res.json();
34
- expect(json.goals).toHaveLength(1);
35
- expect(json.goals[0].id).toBe("g1");
36
- });
37
-
38
- it("returns 500 when listGoals throws", async () => {
39
- vi.mocked(listGoals).mockRejectedValue(new Error("fs error"));
40
- const res = await GET();
41
- expect(res.status).toBe(500);
42
- const json = await res.json();
43
- expect(json.error).toBe("fs error");
44
- });
45
- });
46
-
47
- describe("POST /api/goals", () => {
48
- it("returns 400 when id or title missing", async () => {
49
- const res = await POST(new Request("https://test", { method: "POST", body: JSON.stringify({}) }));
50
- expect(res.status).toBe(400);
51
- const json = await res.json();
52
- expect(json.error).toBe("id and title are required");
53
- });
54
-
55
- it("returns goal when writeGoal succeeds", async () => {
56
- vi.mocked(writeGoal).mockResolvedValue({
57
- frontmatter: { id: "g1", title: "G1" },
58
- body: "",
59
- raw: "",
60
- });
61
- const res = await POST(
62
- new Request("https://test", {
63
- method: "POST",
64
- body: JSON.stringify({ id: "g1", title: "G1" }),
65
- })
66
- );
67
- expect(res.status).toBe(200);
68
- const json = await res.json();
69
- expect(json.goal.id).toBe("g1");
70
- });
71
- });
72
-
73
- describe("GET /api/goals/[id]", () => {
74
- it("returns goal when found", async () => {
75
- vi.mocked(readGoal).mockResolvedValue({
76
- id: "g1",
77
- frontmatter: { id: "g1", title: "G1" },
78
- body: "body",
79
- raw: "raw",
80
- });
81
- const res = await GET_ID(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
82
- expect(res.status).toBe(200);
83
- const json = await res.json();
84
- expect(json.goal.id).toBe("g1");
85
- expect(json.body).toBe("body");
86
- });
87
-
88
- it("returns 404 when not found", async () => {
89
- vi.mocked(readGoal).mockResolvedValue(null);
90
- const res = await GET_ID(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
91
- expect(res.status).toBe(404);
92
- });
93
-
94
- it("returns 400 when readGoal throws Invalid goal id or Path traversal", async () => {
95
- vi.mocked(readGoal).mockRejectedValue(new Error("Invalid goal id"));
96
- const r1 = await GET_ID(new Request("https://test"), { params: Promise.resolve({ id: "bad" }) });
97
- expect(r1.status).toBe(400);
98
-
99
- vi.mocked(readGoal).mockRejectedValue(new Error("Path traversal"));
100
- const r2 = await GET_ID(new Request("https://test"), { params: Promise.resolve({ id: "../x" }) });
101
- expect(r2.status).toBe(400);
102
- });
103
-
104
- it("returns 500 when readGoal throws other error", async () => {
105
- vi.mocked(readGoal).mockRejectedValue(new Error("fs error"));
106
- const res = await GET_ID(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
107
- expect(res.status).toBe(500);
108
- });
109
- });
110
-
111
- describe("PUT /api/goals/[id]", () => {
112
- it("returns 400 when title missing", async () => {
113
- const res = await PUT(
114
- new Request("https://test", { method: "PUT", body: JSON.stringify({}) }),
115
- { params: Promise.resolve({ id: "g1" }) }
116
- );
117
- expect(res.status).toBe(400);
118
- });
119
-
120
- it("returns goal when writeGoal succeeds", async () => {
121
- vi.mocked(writeGoal).mockResolvedValue({
122
- frontmatter: { id: "g1", title: "Updated" },
123
- body: "",
124
- raw: "",
125
- });
126
- const res = await PUT(
127
- new Request("https://test", {
128
- method: "PUT",
129
- body: JSON.stringify({ title: "Updated" }),
130
- }),
131
- { params: Promise.resolve({ id: "g1" }) }
132
- );
133
- expect(res.status).toBe(200);
134
- const json = await res.json();
135
- expect(json.goal.title).toBe("Updated");
136
- });
137
-
138
- it("returns 400 when writeGoal throws Invalid goal id", async () => {
139
- vi.mocked(writeGoal).mockRejectedValue(new Error("Invalid goal id"));
140
- const res = await PUT(
141
- new Request("https://test", { method: "PUT", body: JSON.stringify({ title: "T" }) }),
142
- { params: Promise.resolve({ id: "bad" }) }
143
- );
144
- expect(res.status).toBe(400);
145
- });
146
-
147
- it("returns 500 when writeGoal throws other error", async () => {
148
- vi.mocked(writeGoal).mockRejectedValue(new Error("fs error"));
149
- const res = await PUT(
150
- new Request("https://test", { method: "PUT", body: JSON.stringify({ title: "T" }) }),
151
- { params: Promise.resolve({ id: "g1" }) }
152
- );
153
- expect(res.status).toBe(500);
154
- });
155
- });
156
-
157
- describe("DELETE /api/goals/[id]", () => {
158
- it("returns 200 when delete succeeds", async () => {
159
- vi.mocked(deleteGoal).mockResolvedValue({ ok: true });
160
- const res = await DELETE(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
161
- expect(res.status).toBe(200);
162
- const json = await res.json();
163
- expect(json.ok).toBe(true);
164
- });
165
-
166
- it("returns 404 when delete returns ok false", async () => {
167
- vi.mocked(deleteGoal).mockResolvedValue({ ok: false });
168
- const res = await DELETE(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
169
- expect(res.status).toBe(404);
170
- });
171
-
172
- it("returns 400 when deleteGoal throws Invalid goal id", async () => {
173
- vi.mocked(deleteGoal).mockRejectedValue(new Error("Invalid goal id"));
174
- const res = await DELETE(new Request("https://test"), { params: Promise.resolve({ id: "bad" }) });
175
- expect(res.status).toBe(400);
176
- });
177
-
178
- it("returns 500 when deleteGoal throws other error", async () => {
179
- vi.mocked(deleteGoal).mockRejectedValue(new Error("fs error"));
180
- const res = await DELETE(new Request("https://test"), { params: Promise.resolve({ id: "g1" }) });
181
- expect(res.status).toBe(500);
182
- });
183
- });
184
- });
@@ -1,188 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
-
3
- vi.mock("@/lib/openclaw", () => ({ runOpenClaw: vi.fn() }));
4
- vi.mock("@/lib/paths", () => ({ readOpenClawConfig: vi.fn() }));
5
- vi.mock("node:fs/promises", () => ({
6
- default: { stat: vi.fn() },
7
- }));
8
-
9
- import { runOpenClaw } from "@/lib/openclaw";
10
- import { readOpenClawConfig } from "@/lib/paths";
11
- import fs from "node:fs/promises";
12
-
13
- describe("api ids check route", () => {
14
- let GET: (req: Request) => Promise<Response>;
15
-
16
- beforeEach(async () => {
17
- vi.resetModules();
18
- vi.useFakeTimers();
19
- vi.advanceTimersByTime(15_000);
20
- vi.mocked(runOpenClaw).mockReset();
21
- vi.mocked(readOpenClawConfig).mockReset();
22
- vi.mocked(fs.stat).mockReset();
23
- const mod = await import("../ids/check/route");
24
- GET = mod.GET;
25
- });
26
-
27
- afterEach(() => {
28
- vi.useRealTimers();
29
- });
30
-
31
- it("returns 400 when kind invalid", async () => {
32
- const res = await GET(new Request("https://test/api/ids/check?kind=invalid&id=foo"));
33
- expect(res.status).toBe(400);
34
- const json = await res.json();
35
- expect(json.error).toBe("kind must be team|agent");
36
- });
37
-
38
- it("returns available:false reason:empty when id empty", async () => {
39
- const res = await GET(new Request("https://test/api/ids/check?kind=agent&id="));
40
- expect(res.status).toBe(200);
41
- const json = await res.json();
42
- expect(json.ok).toBe(true);
43
- expect(json.available).toBe(false);
44
- expect(json.reason).toBe("empty");
45
- });
46
-
47
- it("returns available:false reason:recipe-id-collision when id collides with recipe", async () => {
48
- vi.mocked(runOpenClaw)
49
- .mockResolvedValueOnce({
50
- ok: true,
51
- exitCode: 0,
52
- stdout: JSON.stringify([{ id: "foo" }]),
53
- stderr: "",
54
- })
55
- .mockResolvedValueOnce({
56
- ok: true,
57
- exitCode: 0,
58
- stdout: JSON.stringify([]),
59
- stderr: "",
60
- });
61
-
62
- const res = await GET(new Request("https://test/api/ids/check?kind=agent&id=foo"));
63
- expect(res.status).toBe(200);
64
- const json = await res.json();
65
- expect(json.available).toBe(false);
66
- expect(json.reason).toBe("recipe-id-collision");
67
- });
68
-
69
- it("returns available:false reason:agent-exists for agent id", async () => {
70
- vi.mocked(runOpenClaw)
71
- .mockResolvedValueOnce({
72
- ok: true,
73
- exitCode: 0,
74
- stdout: JSON.stringify([]),
75
- stderr: "",
76
- })
77
- .mockResolvedValueOnce({
78
- ok: true,
79
- exitCode: 0,
80
- stdout: JSON.stringify([{ id: "my-agent" }]),
81
- stderr: "",
82
- });
83
-
84
- const res = await GET(new Request("https://test/api/ids/check?kind=agent&id=my-agent"));
85
- expect(res.status).toBe(200);
86
- const json = await res.json();
87
- expect(json.available).toBe(false);
88
- expect(json.reason).toBe("agent-exists");
89
- });
90
-
91
- it("returns available:true for new agent id", async () => {
92
- vi.mocked(runOpenClaw)
93
- .mockResolvedValueOnce({
94
- ok: true,
95
- exitCode: 0,
96
- stdout: JSON.stringify([]),
97
- stderr: "",
98
- })
99
- .mockResolvedValueOnce({
100
- ok: true,
101
- exitCode: 0,
102
- stdout: JSON.stringify([]),
103
- stderr: "",
104
- });
105
-
106
- const res = await GET(new Request("https://test/api/ids/check?kind=agent&id=new-agent"));
107
- expect(res.status).toBe(200);
108
- const json = await res.json();
109
- expect(json.available).toBe(true);
110
- });
111
-
112
- it("returns available:false reason:team-workspace-exists when team dir exists", async () => {
113
- vi.mocked(runOpenClaw)
114
- .mockResolvedValueOnce({
115
- ok: true,
116
- exitCode: 0,
117
- stdout: JSON.stringify([]),
118
- stderr: "",
119
- })
120
- .mockResolvedValueOnce({
121
- ok: true,
122
- exitCode: 0,
123
- stdout: JSON.stringify([]),
124
- stderr: "",
125
- });
126
- vi.mocked(readOpenClawConfig).mockResolvedValue({
127
- agents: { defaults: { workspace: "/home/user/agents" } },
128
- } as never);
129
- vi.mocked(fs.stat).mockResolvedValue({} as never);
130
-
131
- const res = await GET(new Request("https://test/api/ids/check?kind=team&id=my-team"));
132
- expect(res.status).toBe(200);
133
- const json = await res.json();
134
- expect(json.available).toBe(false);
135
- expect(json.reason).toBe("team-workspace-exists");
136
- });
137
-
138
- it("returns available:false reason:team-agents-exist when agents with prefix exist", async () => {
139
- vi.mocked(runOpenClaw)
140
- .mockResolvedValueOnce({
141
- ok: true,
142
- exitCode: 0,
143
- stdout: JSON.stringify([]),
144
- stderr: "",
145
- })
146
- .mockResolvedValueOnce({
147
- ok: true,
148
- exitCode: 0,
149
- stdout: JSON.stringify([{ id: "my-team-lead" }]),
150
- stderr: "",
151
- });
152
- vi.mocked(readOpenClawConfig).mockResolvedValue({
153
- agents: { defaults: { workspace: "/home/user/agents" } },
154
- } as never);
155
- vi.mocked(fs.stat).mockRejectedValue(new Error("ENOENT"));
156
-
157
- const res = await GET(new Request("https://test/api/ids/check?kind=team&id=my-team"));
158
- expect(res.status).toBe(200);
159
- const json = await res.json();
160
- expect(json.available).toBe(false);
161
- expect(json.reason).toBe("team-agents-exist");
162
- });
163
-
164
- it("returns available:true for new team id", async () => {
165
- vi.mocked(runOpenClaw)
166
- .mockResolvedValueOnce({
167
- ok: true,
168
- exitCode: 0,
169
- stdout: JSON.stringify([]),
170
- stderr: "",
171
- })
172
- .mockResolvedValueOnce({
173
- ok: true,
174
- exitCode: 0,
175
- stdout: JSON.stringify([]),
176
- stderr: "",
177
- });
178
- vi.mocked(readOpenClawConfig).mockResolvedValue({
179
- agents: { defaults: { workspace: "/home/user/agents" } },
180
- } as never);
181
- vi.mocked(fs.stat).mockRejectedValue(new Error("ENOENT"));
182
-
183
- const res = await GET(new Request("https://test/api/ids/check?kind=team&id=new-team"));
184
- expect(res.status).toBe(200);
185
- const json = await res.json();
186
- expect(json.available).toBe(true);
187
- });
188
- });
@@ -1,123 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import { GET } from "../marketplace/recipes/route";
3
- import { GET as GET_SLUG } from "../marketplace/recipes/[slug]/route";
4
-
5
- vi.mock("@/lib/marketplace", () => ({
6
- loadRegistry: vi.fn(),
7
- search: vi.fn(),
8
- getBySlug: vi.fn(),
9
- }));
10
-
11
- import { loadRegistry, search, getBySlug } from "@/lib/marketplace";
12
-
13
- const sampleRecipe = {
14
- slug: "my-agent",
15
- kind: "agent" as const,
16
- name: "My Agent",
17
- description: "Does stuff",
18
- version: "1.0.0",
19
- tags: ["agent"],
20
- sourceUrl: "https://example.com",
21
- };
22
-
23
- describe("api marketplace recipes route", () => {
24
- beforeEach(() => {
25
- vi.mocked(loadRegistry).mockReset();
26
- vi.mocked(search).mockReset();
27
- vi.mocked(getBySlug).mockReset();
28
- });
29
-
30
- describe("GET /api/marketplace/recipes", () => {
31
- it("returns recipes on success without query", async () => {
32
- vi.mocked(loadRegistry).mockResolvedValue({
33
- version: 1,
34
- generatedAt: "2025-01-01",
35
- recipes: [sampleRecipe],
36
- });
37
- vi.mocked(search).mockImplementation((recipes) => recipes);
38
-
39
- const res = await GET(new Request("https://test/api/marketplace/recipes"));
40
- expect(res.status).toBe(200);
41
- const json = await res.json();
42
- expect(json.ok).toBe(true);
43
- expect(json.recipes).toHaveLength(1);
44
- expect(json.recipes[0].slug).toBe("my-agent");
45
- expect(search).toHaveBeenCalledWith([sampleRecipe], null);
46
- });
47
-
48
- it("returns filtered recipes when query provided", async () => {
49
- vi.mocked(loadRegistry).mockResolvedValue({
50
- version: 1,
51
- generatedAt: "2025-01-01",
52
- recipes: [sampleRecipe],
53
- });
54
- vi.mocked(search).mockReturnValue([sampleRecipe]);
55
-
56
- const res = await GET(new Request("https://test/api/marketplace/recipes?q=agent"));
57
- expect(res.status).toBe(200);
58
- const json = await res.json();
59
- expect(json.ok).toBe(true);
60
- expect(search).toHaveBeenCalledWith([sampleRecipe], "agent");
61
- });
62
-
63
- it("returns 500 when loadRegistry throws", async () => {
64
- vi.mocked(loadRegistry).mockRejectedValue(new Error("Registry error"));
65
-
66
- const res = await GET(new Request("https://test/api/marketplace/recipes"));
67
- expect(res.status).toBe(500);
68
- const json = await res.json();
69
- expect(json.ok).toBe(false);
70
- expect(json.error).toBe("Registry error");
71
- });
72
- });
73
-
74
- describe("GET /api/marketplace/recipes/[slug]", () => {
75
- it("returns recipe when found", async () => {
76
- vi.mocked(loadRegistry).mockResolvedValue({
77
- version: 1,
78
- generatedAt: "2025-01-01",
79
- recipes: [sampleRecipe],
80
- });
81
- vi.mocked(getBySlug).mockReturnValue(sampleRecipe);
82
-
83
- const res = await GET_SLUG(
84
- new Request("https://test"),
85
- { params: Promise.resolve({ slug: "my-agent" }) }
86
- );
87
- expect(res.status).toBe(200);
88
- const json = await res.json();
89
- expect(json.ok).toBe(true);
90
- expect(json.recipe.slug).toBe("my-agent");
91
- });
92
-
93
- it("returns 404 when not found", async () => {
94
- vi.mocked(loadRegistry).mockResolvedValue({
95
- version: 1,
96
- generatedAt: "2025-01-01",
97
- recipes: [],
98
- });
99
- vi.mocked(getBySlug).mockReturnValue(null);
100
-
101
- const res = await GET_SLUG(
102
- new Request("https://test"),
103
- { params: Promise.resolve({ slug: "missing" }) }
104
- );
105
- expect(res.status).toBe(404);
106
- const json = await res.json();
107
- expect(json.ok).toBe(false);
108
- expect(json.error).toBe("Not found");
109
- });
110
-
111
- it("returns 500 when loadRegistry throws", async () => {
112
- vi.mocked(loadRegistry).mockRejectedValue(new Error("Load failed"));
113
-
114
- const res = await GET_SLUG(
115
- new Request("https://test"),
116
- { params: Promise.resolve({ slug: "any" }) }
117
- );
118
- expect(res.status).toBe(500);
119
- const json = await res.json();
120
- expect(json.error).toBe("Load failed");
121
- });
122
- });
123
- });