import { describe, expect, it } from "bun:test"; import { Effect, Schema } from "effect"; import { ActivitySchema, ActivitiesResponseSchema, IssueLinkSchema, IssueLinksResponseSchema, ModuleSchema, ModulesResponseSchema, ModuleIssueSchema, ModuleIssuesResponseSchema, } from "@/config"; async function decode( schema: Schema.Schema, data: unknown, ): Promise { return Effect.runPromise( Schema.decodeUnknown(schema)(data).pipe( Effect.mapError((e) => new Error(String(e))), ), ); } describe("ActivitySchema", () => { it("decodes a full activity", async () => { const a = await decode(ActivitySchema, { id: "act1", actor_detail: { display_name: "Aaron" }, field: "state", old_value: "Backlog", new_value: "In Progress", verb: "updated", created_at: "2025-01-15T10:30:00Z", }); expect(a.id).toBe("act1"); expect(a.actor_detail?.display_name).toBe("Aaron"); expect(a.field).toBe("state"); expect(a.old_value).toBe("Backlog"); expect(a.new_value).toBe("In Progress"); expect(a.created_at).toBe("2025-01-15T10:30:00Z"); }); it("decodes activity with null field values", async () => { const a = await decode(ActivitySchema, { id: "act2", field: null, old_value: null, new_value: null, created_at: "2025-01-15T10:30:00Z", }); expect(a.field).toBeNull(); expect(a.old_value).toBeNull(); }); it("decodes minimal activity (no optional fields)", async () => { const a = await decode(ActivitySchema, { id: "act3", created_at: "2025-01-15T10:30:00Z", }); expect(a.id).toBe("act3"); }); it("rejects missing id", async () => { await expect( decode(ActivitySchema, { created_at: "2025-01-15T10:30:00Z" }), ).rejects.toThrow(); }); }); describe("ActivitiesResponseSchema", () => { it("decodes results array", async () => { const resp = await decode(ActivitiesResponseSchema, { results: [ { id: "a1", created_at: "2025-01-15T10:30:00Z" }, { id: "a2", created_at: "2025-01-16T10:30:00Z" }, ], }); expect(resp.results).toHaveLength(2); }); it("decodes empty results", async () => { const resp = await decode(ActivitiesResponseSchema, { results: [] }); expect(resp.results).toHaveLength(0); }); }); describe("IssueLinkSchema", () => { it("decodes a full link", async () => { const l = await decode(IssueLinkSchema, { id: "link1", title: "PR #42", url: "https://github.com/org/repo/pull/42", created_at: "2025-01-15T10:30:00Z", }); expect(l.id).toBe("link1"); expect(l.title).toBe("PR #42"); expect(l.url).toBe("https://github.com/org/repo/pull/42"); }); it("decodes link with null title", async () => { const l = await decode(IssueLinkSchema, { id: "link2", title: null, url: "https://example.com", created_at: "2025-01-15T10:30:00Z", }); expect(l.title).toBeNull(); }); it("rejects missing url", async () => { await expect( decode(IssueLinkSchema, { id: "link3", created_at: "2025-01-15T10:30:00Z", }), ).rejects.toThrow(); }); }); describe("IssueLinksResponseSchema", () => { it("decodes results", async () => { const resp = await decode(IssueLinksResponseSchema, { results: [ { id: "l1", url: "https://example.com/1", created_at: "2025-01-15T10:00:00Z", }, ], }); expect(resp.results).toHaveLength(1); }); it("decodes empty results", async () => { const resp = await decode(IssueLinksResponseSchema, { results: [] }); expect(resp.results).toHaveLength(0); }); }); describe("ModuleSchema", () => { it("decodes a module", async () => { const m = await decode(ModuleSchema, { id: "mod1", name: "Sprint 1" }); expect(m.id).toBe("mod1"); expect(m.name).toBe("Sprint 1"); }); it("accepts optional status and description", async () => { const m = await decode(ModuleSchema, { id: "mod1", name: "Sprint 1", status: "in_progress", description: "Focus on migration", }); expect(m.status).toBe("in_progress"); expect(m.description).toBe("Focus on migration"); }); it("accepts null description", async () => { const m = await decode(ModuleSchema, { id: "mod1", name: "Sprint 1", description: null, }); expect(m.description).toBeNull(); }); it("rejects missing name", async () => { await expect(decode(ModuleSchema, { id: "mod1" })).rejects.toThrow(); }); }); describe("ModulesResponseSchema", () => { it("decodes results", async () => { const resp = await decode(ModulesResponseSchema, { results: [ { id: "m1", name: "Sprint 1" }, { id: "m2", name: "Sprint 2" }, ], }); expect(resp.results).toHaveLength(2); }); }); describe("ModuleIssueSchema", () => { it("decodes a module issue with detail", async () => { const mi = await decode(ModuleIssueSchema, { id: "mi1", issue: "issue-uuid", issue_detail: { id: "issue-uuid", sequence_id: 29, name: "Migrate Button", }, }); expect(mi.id).toBe("mi1"); expect(mi.issue_detail?.sequence_id).toBe(29); }); it("decodes without issue_detail", async () => { const mi = await decode(ModuleIssueSchema, { id: "mi1", issue: "issue-uuid", }); expect(mi.issue).toBe("issue-uuid"); expect(mi.issue_detail).toBeUndefined(); }); it("rejects missing issue", async () => { await expect(decode(ModuleIssueSchema, { id: "mi1" })).rejects.toThrow(); }); }); describe("ModuleIssuesResponseSchema", () => { it("decodes results", async () => { const resp = await decode(ModuleIssuesResponseSchema, { results: [{ id: "mi1", issue: "uuid1" }], }); expect(resp.results).toHaveLength(1); }); });