import { afterAll, describe, expect, mock, test } from "bun:test"; import type { GraphQLResponse } from "../../../src/mutator/branch/client"; import { GraphQLClient } from "../../../src/mutator/branch/client"; const originalFetch = globalThis.fetch; function mockFetch(response: Partial & { jsonData?: unknown }) { globalThis.fetch = mock(() => Promise.resolve({ ok: response.ok ?? true, status: response.status ?? 200, statusText: response.statusText ?? "OK", json: mock(() => Promise.resolve(response.jsonData ?? { data: {} })), text: mock(() => Promise.resolve(JSON.stringify(response.jsonData ?? { data: {} })), ), }), ) as any; } afterAll(() => { globalThis.fetch = originalFetch; }); describe("GraphQLClient", () => { test("throws when constructed without token", () => { expect(() => new GraphQLClient("")).toThrow( "A GitHub token is required to construct a GraphQLClient.", ); }); test("throws on non-2xx response", async () => { mockFetch({ ok: false, status: 401, statusText: "Unauthorized" }); const client = new GraphQLClient("fake-token"); await expect(client.execute("query { viewer { login } }")).rejects.toThrow( "GitHub GraphQL request failed (status=401)", ); }); test("throws on GraphQL errors in execute()", async () => { mockFetch({ ok: true, jsonData: { errors: [{ message: "Something went wrong" }] }, }); const client = new GraphQLClient("fake-token"); await expect(client.execute("query { x }")).rejects.toThrow( "GraphQL errors: Something went wrong", ); }); test("throws when no data returned in execute()", async () => { mockFetch({ ok: true, jsonData: { errors: [] }, }); const client = new GraphQLClient("fake-token"); await expect(client.execute("query { x }")).rejects.toThrow( "No data returned from GitHub API", ); }); test("returns data on successful query", async () => { mockFetch({ ok: true, jsonData: { data: { viewer: { login: "testuser" } } }, }); const client = new GraphQLClient("fake-token"); const result = await client.execute("query { viewer { login } }"); expect(result).toEqual({ viewer: { login: "testuser" } }); }); test("executeWithPartial returns data + errors without throwing", async () => { mockFetch({ ok: true, jsonData: { data: { b0: { commit: { oid: "abc" } }, b1: null }, errors: [{ message: "Branch protected", path: ["b1"] }], }, }); const client = new GraphQLClient("fake-token"); const result = await client.executeWithPartial("mutation { ... }"); expect(result.data).toBeDefined(); expect(result.errors).toHaveLength(1); expect(result.errors![0].message).toBe("Branch protected"); }); test("executeWithPartial throws on non-2xx", async () => { mockFetch({ ok: false, status: 500, statusText: "Internal Server Error" }); const client = new GraphQLClient("fake-token"); await expect(client.executeWithPartial("query { x }")).rejects.toThrow( "GitHub GraphQL request failed (status=500)", ); }); test("sends correct headers and body", async () => { mockFetch({ ok: true, jsonData: { data: {} } }); const client = new GraphQLClient("ghp_token123"); await client.execute("query Test($x: String!) { node(id: $x) }", { x: "abc", }); const fetchMock = globalThis.fetch as any; const call = fetchMock.mock.calls[0]; const [url, options] = call; expect(url).toBe("https://api.github.com/graphql"); expect(options.method).toBe("POST"); expect(options.headers.Authorization).toBe("Bearer ghp_token123"); expect(options.headers["Content-Type"]).toBe("application/json"); const body = JSON.parse(options.body); expect(body.query).toBe("query Test($x: String!) { node(id: $x) }"); expect(body.variables).toEqual({ x: "abc" }); }); });