@aaronshaf/plane 0.1.2 → 0.1.5

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.
@@ -1,255 +1,299 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test"
2
- import { Effect } from "effect"
3
- import { http, HttpResponse } from "msw"
4
- import { setupServer } from "msw/node"
5
- import { _clearProjectCache } from "@/resolve"
6
-
7
- const BASE = "http://modules-test.local"
8
- const WS = "testws"
9
-
10
- const PROJECTS = [{ id: "proj-acme", identifier: "ACME", name: "Acme Project" }]
11
- const ISSUES = [{ id: "i1", sequence_id: 29, name: "Migrate Button", priority: "high", state: "s1" }]
1
+ import {
2
+ afterAll,
3
+ afterEach,
4
+ beforeAll,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ it,
9
+ } from "bun:test";
10
+ import { Effect } from "effect";
11
+ import { http, HttpResponse } from "msw";
12
+ import { setupServer } from "msw/node";
13
+ import { _clearProjectCache } from "@/resolve";
14
+
15
+ const BASE = "http://modules-test.local";
16
+ const WS = "testws";
17
+
18
+ const PROJECTS = [
19
+ { id: "proj-acme", identifier: "ACME", name: "Acme Project" },
20
+ ];
21
+ const ISSUES = [
22
+ {
23
+ id: "i1",
24
+ sequence_id: 29,
25
+ name: "Migrate Button",
26
+ priority: "high",
27
+ state: "s1",
28
+ },
29
+ ];
12
30
  const MODULES = [
13
- { id: "mod1", name: "Sprint 1", status: "in_progress" },
14
- { id: "mod2", name: "Sprint 2", status: "backlog" },
15
- ]
31
+ { id: "mod1", name: "Sprint 1", status: "in_progress" },
32
+ { id: "mod2", name: "Sprint 2", status: "backlog" },
33
+ ];
16
34
  const MODULE_ISSUES = [
17
- {
18
- id: "mi1",
19
- issue: "i1",
20
- issue_detail: { id: "i1", sequence_id: 29, name: "Migrate Button" },
21
- },
22
- ]
35
+ {
36
+ id: "mi1",
37
+ issue: "i1",
38
+ issue_detail: { id: "i1", sequence_id: 29, name: "Migrate Button" },
39
+ },
40
+ ];
23
41
 
24
42
  const server = setupServer(
25
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
26
- HttpResponse.json({ results: PROJECTS }),
27
- ),
28
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`, () =>
29
- HttpResponse.json({ results: ISSUES }),
30
- ),
31
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`, () =>
32
- HttpResponse.json({ results: MODULES }),
33
- ),
34
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`, () =>
35
- HttpResponse.json({ results: MODULE_ISSUES }),
36
- ),
37
- )
38
-
39
- beforeAll(() => server.listen({ onUnhandledRequest: "error" }))
40
- afterAll(() => server.close())
43
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
44
+ HttpResponse.json({ results: PROJECTS }),
45
+ ),
46
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`, () =>
47
+ HttpResponse.json({ results: ISSUES }),
48
+ ),
49
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`, () =>
50
+ HttpResponse.json({ results: MODULES }),
51
+ ),
52
+ http.get(
53
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
54
+ () => HttpResponse.json({ results: MODULE_ISSUES }),
55
+ ),
56
+ );
57
+
58
+ beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
59
+ afterAll(() => server.close());
41
60
 
42
61
  beforeEach(() => {
43
- _clearProjectCache()
44
- process.env["PLANE_HOST"] = BASE
45
- process.env["PLANE_WORKSPACE"] = WS
46
- process.env["PLANE_API_TOKEN"] = "test-token"
47
- })
62
+ _clearProjectCache();
63
+ process.env["PLANE_HOST"] = BASE;
64
+ process.env["PLANE_WORKSPACE"] = WS;
65
+ process.env["PLANE_API_TOKEN"] = "test-token";
66
+ });
48
67
 
49
68
  afterEach(() => {
50
- server.resetHandlers()
51
- delete process.env["PLANE_HOST"]
52
- delete process.env["PLANE_WORKSPACE"]
53
- delete process.env["PLANE_API_TOKEN"]
54
- })
69
+ server.resetHandlers();
70
+ delete process.env["PLANE_HOST"];
71
+ delete process.env["PLANE_WORKSPACE"];
72
+ delete process.env["PLANE_API_TOKEN"];
73
+ });
55
74
 
56
75
  describe("modulesList", () => {
57
- it("lists modules for a project", async () => {
58
- const { modulesList } = await import("@/commands/modules")
59
- const logs: string[] = []
60
- const orig = console.log
61
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
62
-
63
- try {
64
- await Effect.runPromise((modulesList as any).handler({ project: "ACME" }))
65
- } finally {
66
- console.log = orig
67
- }
68
-
69
- const output = logs.join("\n")
70
- expect(output).toContain("mod1")
71
- expect(output).toContain("Sprint 1")
72
- expect(output).toContain("in_progress")
73
- expect(output).toContain("mod2")
74
- expect(output).toContain("Sprint 2")
75
- })
76
-
77
- it("shows 'No modules found' when empty", async () => {
78
- server.use(
79
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`, () =>
80
- HttpResponse.json({ results: [] }),
81
- ),
82
- )
83
-
84
- const { modulesList } = await import("@/commands/modules")
85
- const logs: string[] = []
86
- const orig = console.log
87
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
88
-
89
- try {
90
- await Effect.runPromise((modulesList as any).handler({ project: "ACME" }))
91
- } finally {
92
- console.log = orig
93
- }
94
-
95
- expect(logs.join("\n")).toBe("No modules found")
96
- })
97
-
98
- it("shows '?' for missing status", async () => {
99
- server.use(
100
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`, () =>
101
- HttpResponse.json({ results: [{ id: "mod3", name: "Unstarted Sprint" }] }),
102
- ),
103
- )
104
-
105
- const { modulesList } = await import("@/commands/modules")
106
- const logs: string[] = []
107
- const orig = console.log
108
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
109
-
110
- try {
111
- await Effect.runPromise((modulesList as any).handler({ project: "ACME" }))
112
- } finally {
113
- console.log = orig
114
- }
115
-
116
- expect(logs.join("\n")).toContain("?")
117
- })
118
- })
76
+ it("lists modules for a project", async () => {
77
+ const { modulesList } = await import("@/commands/modules");
78
+ const logs: string[] = [];
79
+ const orig = console.log;
80
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
81
+
82
+ try {
83
+ await Effect.runPromise(
84
+ (modulesList as any).handler({ project: "ACME" }),
85
+ );
86
+ } finally {
87
+ console.log = orig;
88
+ }
89
+
90
+ const output = logs.join("\n");
91
+ expect(output).toContain("mod1");
92
+ expect(output).toContain("Sprint 1");
93
+ expect(output).toContain("in_progress");
94
+ expect(output).toContain("mod2");
95
+ expect(output).toContain("Sprint 2");
96
+ });
97
+
98
+ it("shows 'No modules found' when empty", async () => {
99
+ server.use(
100
+ http.get(
101
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`,
102
+ () => HttpResponse.json({ results: [] }),
103
+ ),
104
+ );
105
+
106
+ const { modulesList } = await import("@/commands/modules");
107
+ const logs: string[] = [];
108
+ const orig = console.log;
109
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
110
+
111
+ try {
112
+ await Effect.runPromise(
113
+ (modulesList as any).handler({ project: "ACME" }),
114
+ );
115
+ } finally {
116
+ console.log = orig;
117
+ }
118
+
119
+ expect(logs.join("\n")).toBe("No modules found");
120
+ });
121
+
122
+ it("shows '?' for missing status", async () => {
123
+ server.use(
124
+ http.get(
125
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/`,
126
+ () =>
127
+ HttpResponse.json({
128
+ results: [{ id: "mod3", name: "Unstarted Sprint" }],
129
+ }),
130
+ ),
131
+ );
132
+
133
+ const { modulesList } = await import("@/commands/modules");
134
+ const logs: string[] = [];
135
+ const orig = console.log;
136
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
137
+
138
+ try {
139
+ await Effect.runPromise(
140
+ (modulesList as any).handler({ project: "ACME" }),
141
+ );
142
+ } finally {
143
+ console.log = orig;
144
+ }
145
+
146
+ expect(logs.join("\n")).toContain("?");
147
+ });
148
+ });
119
149
 
120
150
  describe("moduleIssuesList", () => {
121
- it("lists issues in a module with detail", async () => {
122
- const { moduleIssuesList } = await import("@/commands/modules")
123
- const logs: string[] = []
124
- const orig = console.log
125
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
126
-
127
- try {
128
- await Effect.runPromise(
129
- (moduleIssuesList as any).handler({ project: "ACME", moduleId: "mod1" }),
130
- )
131
- } finally {
132
- console.log = orig
133
- }
134
-
135
- const output = logs.join("\n")
136
- expect(output).toContain("ACME-")
137
- expect(output).toContain("29")
138
- expect(output).toContain("Migrate Button")
139
- })
140
-
141
- it("falls back to issue UUID when no issue_detail", async () => {
142
- server.use(
143
- http.get(
144
- `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
145
- () => HttpResponse.json({ results: [{ id: "mi2", issue: "bare-uuid" }] }),
146
- ),
147
- )
148
-
149
- const { moduleIssuesList } = await import("@/commands/modules")
150
- const logs: string[] = []
151
- const orig = console.log
152
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
153
-
154
- try {
155
- await Effect.runPromise(
156
- (moduleIssuesList as any).handler({ project: "ACME", moduleId: "mod1" }),
157
- )
158
- } finally {
159
- console.log = orig
160
- }
161
-
162
- expect(logs.join("\n")).toContain("bare-uuid")
163
- })
164
-
165
- it("shows 'No issues in module' when empty", async () => {
166
- server.use(
167
- http.get(
168
- `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
169
- () => HttpResponse.json({ results: [] }),
170
- ),
171
- )
172
-
173
- const { moduleIssuesList } = await import("@/commands/modules")
174
- const logs: string[] = []
175
- const orig = console.log
176
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
177
-
178
- try {
179
- await Effect.runPromise(
180
- (moduleIssuesList as any).handler({ project: "ACME", moduleId: "mod1" }),
181
- )
182
- } finally {
183
- console.log = orig
184
- }
185
-
186
- expect(logs.join("\n")).toBe("No issues in module")
187
- })
188
- })
151
+ it("lists issues in a module with detail", async () => {
152
+ const { moduleIssuesList } = await import("@/commands/modules");
153
+ const logs: string[] = [];
154
+ const orig = console.log;
155
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
156
+
157
+ try {
158
+ await Effect.runPromise(
159
+ (moduleIssuesList as any).handler({
160
+ project: "ACME",
161
+ moduleId: "mod1",
162
+ }),
163
+ );
164
+ } finally {
165
+ console.log = orig;
166
+ }
167
+
168
+ const output = logs.join("\n");
169
+ expect(output).toContain("ACME-");
170
+ expect(output).toContain("29");
171
+ expect(output).toContain("Migrate Button");
172
+ });
173
+
174
+ it("falls back to issue UUID when no issue_detail", async () => {
175
+ server.use(
176
+ http.get(
177
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
178
+ () =>
179
+ HttpResponse.json({ results: [{ id: "mi2", issue: "bare-uuid" }] }),
180
+ ),
181
+ );
182
+
183
+ const { moduleIssuesList } = await import("@/commands/modules");
184
+ const logs: string[] = [];
185
+ const orig = console.log;
186
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
187
+
188
+ try {
189
+ await Effect.runPromise(
190
+ (moduleIssuesList as any).handler({
191
+ project: "ACME",
192
+ moduleId: "mod1",
193
+ }),
194
+ );
195
+ } finally {
196
+ console.log = orig;
197
+ }
198
+
199
+ expect(logs.join("\n")).toContain("bare-uuid");
200
+ });
201
+
202
+ it("shows 'No issues in module' when empty", async () => {
203
+ server.use(
204
+ http.get(
205
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
206
+ () => HttpResponse.json({ results: [] }),
207
+ ),
208
+ );
209
+
210
+ const { moduleIssuesList } = await import("@/commands/modules");
211
+ const logs: string[] = [];
212
+ const orig = console.log;
213
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
214
+
215
+ try {
216
+ await Effect.runPromise(
217
+ (moduleIssuesList as any).handler({
218
+ project: "ACME",
219
+ moduleId: "mod1",
220
+ }),
221
+ );
222
+ } finally {
223
+ console.log = orig;
224
+ }
225
+
226
+ expect(logs.join("\n")).toBe("No issues in module");
227
+ });
228
+ });
189
229
 
190
230
  describe("moduleIssuesAdd", () => {
191
- it("adds an issue to a module", async () => {
192
- let postedBody: unknown
193
- server.use(
194
- http.post(
195
- `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
196
- async ({ request }) => {
197
- postedBody = await request.json()
198
- return HttpResponse.json({ issues: ["i1"] }, { status: 201 })
199
- },
200
- ),
201
- )
202
-
203
- const { moduleIssuesAdd } = await import("@/commands/modules")
204
- const logs: string[] = []
205
- const orig = console.log
206
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
207
-
208
- try {
209
- await Effect.runPromise(
210
- (moduleIssuesAdd as any).handler({ project: "ACME", moduleId: "mod1", ref: "ACME-29" }),
211
- )
212
- } finally {
213
- console.log = orig
214
- }
215
-
216
- expect((postedBody as any).issues).toContain("i1")
217
- expect(logs.join("\n")).toContain("ACME-29")
218
- expect(logs.join("\n")).toContain("mod1")
219
- })
220
- })
231
+ it("adds an issue to a module", async () => {
232
+ let postedBody: unknown;
233
+ server.use(
234
+ http.post(
235
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/`,
236
+ async ({ request }) => {
237
+ postedBody = await request.json();
238
+ return HttpResponse.json({ issues: ["i1"] }, { status: 201 });
239
+ },
240
+ ),
241
+ );
242
+
243
+ const { moduleIssuesAdd } = await import("@/commands/modules");
244
+ const logs: string[] = [];
245
+ const orig = console.log;
246
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
247
+
248
+ try {
249
+ await Effect.runPromise(
250
+ (moduleIssuesAdd as any).handler({
251
+ project: "ACME",
252
+ moduleId: "mod1",
253
+ ref: "ACME-29",
254
+ }),
255
+ );
256
+ } finally {
257
+ console.log = orig;
258
+ }
259
+
260
+ expect((postedBody as any).issues).toContain("i1");
261
+ expect(logs.join("\n")).toContain("ACME-29");
262
+ expect(logs.join("\n")).toContain("mod1");
263
+ });
264
+ });
221
265
 
222
266
  describe("moduleIssuesRemove", () => {
223
- it("removes a module-issue", async () => {
224
- let deleted = false
225
- server.use(
226
- http.delete(
227
- `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/mi1/`,
228
- () => {
229
- deleted = true
230
- return new HttpResponse(null, { status: 204 })
231
- },
232
- ),
233
- )
234
-
235
- const { moduleIssuesRemove } = await import("@/commands/modules")
236
- const logs: string[] = []
237
- const orig = console.log
238
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
239
-
240
- try {
241
- await Effect.runPromise(
242
- (moduleIssuesRemove as any).handler({
243
- project: "ACME",
244
- moduleId: "mod1",
245
- moduleIssueId: "mi1",
246
- }),
247
- )
248
- } finally {
249
- console.log = orig
250
- }
251
-
252
- expect(deleted).toBe(true)
253
- expect(logs.join("\n")).toContain("mi1")
254
- })
255
- })
267
+ it("removes a module-issue", async () => {
268
+ let deleted = false;
269
+ server.use(
270
+ http.delete(
271
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/modules/mod1/module-issues/mi1/`,
272
+ () => {
273
+ deleted = true;
274
+ return new HttpResponse(null, { status: 204 });
275
+ },
276
+ ),
277
+ );
278
+
279
+ const { moduleIssuesRemove } = await import("@/commands/modules");
280
+ const logs: string[] = [];
281
+ const orig = console.log;
282
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
283
+
284
+ try {
285
+ await Effect.runPromise(
286
+ (moduleIssuesRemove as any).handler({
287
+ project: "ACME",
288
+ moduleId: "mod1",
289
+ moduleIssueId: "mi1",
290
+ }),
291
+ );
292
+ } finally {
293
+ console.log = orig;
294
+ }
295
+
296
+ expect(deleted).toBe(true);
297
+ expect(logs.join("\n")).toContain("mi1");
298
+ });
299
+ });