opencode-swarm-plugin 0.23.6 → 0.25.0

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.
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Tests for Bead Event Schemas
3
+ *
4
+ * Validates event creation, validation, and type guards.
5
+ */
6
+ import { describe, expect, test } from "bun:test";
7
+ import {
8
+ type BeadCreatedEvent,
9
+ BeadEventSchema,
10
+ createBeadEvent,
11
+ getBeadIdFromEvent,
12
+ isAgentEvent,
13
+ isBeadEventType,
14
+ isEpicEvent,
15
+ isStateTransitionEvent,
16
+ } from "./bead-events.js";
17
+
18
+ describe("BeadEventSchema", () => {
19
+ const projectKey = "/path/to/repo";
20
+
21
+ describe("createBeadEvent", () => {
22
+ test("creates valid bead_created event", () => {
23
+ const event = createBeadEvent("bead_created", {
24
+ project_key: projectKey,
25
+ bead_id: "bd-123",
26
+ title: "Add authentication",
27
+ issue_type: "feature",
28
+ priority: 2,
29
+ });
30
+
31
+ expect(event.type).toBe("bead_created");
32
+ expect(event.bead_id).toBe("bd-123");
33
+ expect(event.title).toBe("Add authentication");
34
+ expect(event.timestamp).toBeGreaterThan(0);
35
+ });
36
+
37
+ test("creates valid bead_closed event", () => {
38
+ const event = createBeadEvent("bead_closed", {
39
+ project_key: projectKey,
40
+ bead_id: "bd-123",
41
+ reason: "Implemented OAuth flow",
42
+ closed_by: "BlueLake",
43
+ files_touched: ["src/auth.ts", "src/oauth.ts"],
44
+ duration_ms: 45000,
45
+ });
46
+
47
+ expect(event.type).toBe("bead_closed");
48
+ expect(event.reason).toBe("Implemented OAuth flow");
49
+ expect(event.closed_by).toBe("BlueLake");
50
+ expect(event.files_touched).toHaveLength(2);
51
+ });
52
+
53
+ test("creates valid bead_dependency_added event", () => {
54
+ const event = createBeadEvent("bead_dependency_added", {
55
+ project_key: projectKey,
56
+ bead_id: "bd-123",
57
+ dependency: {
58
+ id: "bd-456",
59
+ type: "blocks",
60
+ },
61
+ reason: "Needs database schema before service layer",
62
+ });
63
+
64
+ expect(event.type).toBe("bead_dependency_added");
65
+ expect(event.dependency.type).toBe("blocks");
66
+ expect(event.dependency.id).toBe("bd-456");
67
+ });
68
+
69
+ test("creates valid bead_epic_child_added event", () => {
70
+ const event = createBeadEvent("bead_epic_child_added", {
71
+ project_key: projectKey,
72
+ bead_id: "bd-epic-1",
73
+ child_id: "bd-epic-1.1",
74
+ child_index: 0,
75
+ });
76
+
77
+ expect(event.type).toBe("bead_epic_child_added");
78
+ expect(event.child_id).toBe("bd-epic-1.1");
79
+ });
80
+
81
+ test("throws on invalid event data", () => {
82
+ expect(() =>
83
+ createBeadEvent("bead_created", {
84
+ project_key: projectKey,
85
+ bead_id: "bd-123",
86
+ title: "Test",
87
+ // @ts-expect-error - Testing invalid issue_type
88
+ issue_type: "invalid_type",
89
+ priority: 2,
90
+ }),
91
+ ).toThrow("Invalid bead event");
92
+ });
93
+ });
94
+
95
+ describe("type guards", () => {
96
+ test("isBeadEventType narrows type correctly", () => {
97
+ const event: BeadCreatedEvent = createBeadEvent("bead_created", {
98
+ project_key: projectKey,
99
+ bead_id: "bd-123",
100
+ title: "Test",
101
+ issue_type: "task",
102
+ priority: 2,
103
+ });
104
+
105
+ if (isBeadEventType(event, "bead_created")) {
106
+ // TypeScript knows this is BeadCreatedEvent
107
+ expect(event.title).toBe("Test");
108
+ }
109
+ });
110
+
111
+ test("isStateTransitionEvent identifies status changes", () => {
112
+ const closedEvent = createBeadEvent("bead_closed", {
113
+ project_key: projectKey,
114
+ bead_id: "bd-123",
115
+ reason: "Done",
116
+ });
117
+
118
+ expect(isStateTransitionEvent(closedEvent)).toBe(true);
119
+
120
+ const createdEvent = createBeadEvent("bead_created", {
121
+ project_key: projectKey,
122
+ bead_id: "bd-123",
123
+ title: "Test",
124
+ issue_type: "task",
125
+ priority: 2,
126
+ });
127
+
128
+ expect(isStateTransitionEvent(createdEvent)).toBe(false);
129
+ });
130
+
131
+ test("isEpicEvent identifies epic operations", () => {
132
+ const epicEvent = createBeadEvent("bead_epic_child_added", {
133
+ project_key: projectKey,
134
+ bead_id: "bd-epic",
135
+ child_id: "bd-epic.1",
136
+ });
137
+
138
+ expect(isEpicEvent(epicEvent)).toBe(true);
139
+
140
+ const regularEvent = createBeadEvent("bead_created", {
141
+ project_key: projectKey,
142
+ bead_id: "bd-123",
143
+ title: "Test",
144
+ issue_type: "task",
145
+ priority: 2,
146
+ });
147
+
148
+ expect(isEpicEvent(regularEvent)).toBe(false);
149
+ });
150
+
151
+ test("isAgentEvent detects agent-triggered events", () => {
152
+ const agentEvent = createBeadEvent("bead_assigned", {
153
+ project_key: projectKey,
154
+ bead_id: "bd-123",
155
+ agent_name: "BlueLake",
156
+ });
157
+
158
+ expect(isAgentEvent(agentEvent)).toBe(true);
159
+
160
+ const closedByAgentEvent = createBeadEvent("bead_closed", {
161
+ project_key: projectKey,
162
+ bead_id: "bd-123",
163
+ reason: "Done",
164
+ closed_by: "agent",
165
+ });
166
+
167
+ expect(isAgentEvent(closedByAgentEvent)).toBe(true);
168
+ });
169
+ });
170
+
171
+ describe("getBeadIdFromEvent", () => {
172
+ test("extracts bead_id from any event", () => {
173
+ const events = [
174
+ createBeadEvent("bead_created", {
175
+ project_key: projectKey,
176
+ bead_id: "bd-123",
177
+ title: "Test",
178
+ issue_type: "task",
179
+ priority: 2,
180
+ }),
181
+ createBeadEvent("bead_closed", {
182
+ project_key: projectKey,
183
+ bead_id: "bd-456",
184
+ reason: "Done",
185
+ }),
186
+ createBeadEvent("bead_epic_child_added", {
187
+ project_key: projectKey,
188
+ bead_id: "bd-epic",
189
+ child_id: "bd-epic.1",
190
+ }),
191
+ ];
192
+
193
+ expect(getBeadIdFromEvent(events[0])).toBe("bd-123");
194
+ expect(getBeadIdFromEvent(events[1])).toBe("bd-456");
195
+ expect(getBeadIdFromEvent(events[2])).toBe("bd-epic");
196
+ });
197
+ });
198
+
199
+ describe("discriminated union validation", () => {
200
+ test("validates against full BeadEventSchema", () => {
201
+ const rawEvent = {
202
+ type: "bead_created",
203
+ project_key: projectKey,
204
+ timestamp: Date.now(),
205
+ bead_id: "bd-123",
206
+ title: "Test bead",
207
+ issue_type: "feature",
208
+ priority: 1,
209
+ };
210
+
211
+ const result = BeadEventSchema.safeParse(rawEvent);
212
+ expect(result.success).toBe(true);
213
+ });
214
+
215
+ test("rejects invalid event type", () => {
216
+ const rawEvent = {
217
+ type: "invalid_event",
218
+ project_key: projectKey,
219
+ timestamp: Date.now(),
220
+ bead_id: "bd-123",
221
+ };
222
+
223
+ const result = BeadEventSchema.safeParse(rawEvent);
224
+ expect(result.success).toBe(false);
225
+ });
226
+
227
+ test("validates dependency types", () => {
228
+ const validTypes = [
229
+ "blocks",
230
+ "blocked-by",
231
+ "related",
232
+ "discovered-from",
233
+ ] as const;
234
+
235
+ for (const depType of validTypes) {
236
+ const event = createBeadEvent("bead_dependency_added", {
237
+ project_key: projectKey,
238
+ bead_id: "bd-123",
239
+ dependency: {
240
+ id: "bd-456",
241
+ type: depType,
242
+ },
243
+ });
244
+
245
+ expect(event.dependency.type).toBe(depType);
246
+ }
247
+ });
248
+ });
249
+
250
+ describe("event metadata", () => {
251
+ test("supports metadata field", () => {
252
+ const event = createBeadEvent("bead_created", {
253
+ project_key: projectKey,
254
+ bead_id: "bd-123",
255
+ title: "Test",
256
+ issue_type: "task",
257
+ priority: 2,
258
+ metadata: {
259
+ epic_context: "bd-epic-1",
260
+ swarm_strategy: "file-based",
261
+ estimated_duration: 30,
262
+ },
263
+ });
264
+
265
+ expect(event.metadata).toBeDefined();
266
+ expect(event.metadata?.epic_context).toBe("bd-epic-1");
267
+ });
268
+ });
269
+
270
+ describe("epic closure eligible event", () => {
271
+ test("creates valid closure eligible event", () => {
272
+ const event = createBeadEvent("bead_epic_closure_eligible", {
273
+ project_key: projectKey,
274
+ bead_id: "bd-epic",
275
+ child_ids: ["bd-epic.1", "bd-epic.2", "bd-epic.3"],
276
+ total_duration_ms: 120000,
277
+ all_files_touched: ["src/a.ts", "src/b.ts"],
278
+ });
279
+
280
+ expect(event.type).toBe("bead_epic_closure_eligible");
281
+ expect(event.child_ids).toHaveLength(3);
282
+ expect(event.total_duration_ms).toBe(120000);
283
+ });
284
+ });
285
+
286
+ describe("status changed event", () => {
287
+ test("tracks status transitions", () => {
288
+ const event = createBeadEvent("bead_status_changed", {
289
+ project_key: projectKey,
290
+ bead_id: "bd-123",
291
+ from_status: "open",
292
+ to_status: "in_progress",
293
+ changed_by: "agent",
294
+ });
295
+
296
+ expect(event.from_status).toBe("open");
297
+ expect(event.to_status).toBe("in_progress");
298
+ });
299
+
300
+ test("includes optional reason for blocked/closed", () => {
301
+ const event = createBeadEvent("bead_status_changed", {
302
+ project_key: projectKey,
303
+ bead_id: "bd-123",
304
+ from_status: "in_progress",
305
+ to_status: "blocked",
306
+ reason: "Waiting for API credentials",
307
+ });
308
+
309
+ expect(event.reason).toBe("Waiting for API credentials");
310
+ });
311
+ });
312
+
313
+ describe("comment events", () => {
314
+ test("creates comment with optional parent", () => {
315
+ const event = createBeadEvent("bead_comment_added", {
316
+ project_key: projectKey,
317
+ bead_id: "bd-123",
318
+ author: "BlueLake",
319
+ body: "Progress update: auth service implemented",
320
+ parent_comment_id: 42,
321
+ });
322
+
323
+ expect(event.author).toBe("BlueLake");
324
+ expect(event.parent_comment_id).toBe(42);
325
+ });
326
+ });
327
+
328
+ describe("work tracking events", () => {
329
+ test("tracks work start with file reservations", () => {
330
+ const event = createBeadEvent("bead_work_started", {
331
+ project_key: projectKey,
332
+ bead_id: "bd-123",
333
+ agent_name: "BlueLake",
334
+ reserved_files: ["src/auth/**"],
335
+ });
336
+
337
+ expect(event.agent_name).toBe("BlueLake");
338
+ expect(event.reserved_files).toEqual(["src/auth/**"]);
339
+ });
340
+ });
341
+ });