import assert from "node:assert/strict"; import test from "node:test"; import { GrimmoryClient, query, unwrap } from "../src/client.js"; test("unwrap handles Grimmory API response envelope", () => { assert.deepEqual(unwrap({ status: 200, data: { id: 7 } }), { id: 7 }); assert.deepEqual(unwrap([1, 2]), [1, 2]); }); test("query repeats array values and skips empty values", () => { assert.equal( query({ page: 0, status: ["READING", "READ"], search: "" }), "page=0&status=READING&status=READ", ); }); test("libraries falls back to the current user on the v3.2.4 endpoint bug", async () => { const responses = [ new Response(JSON.stringify({ message: "broken" }), { status: 500 }), new Response(JSON.stringify({ data: { assignedLibraries: [{ id: 3 }] } })), ]; const client = new GrimmoryClient({ baseUrl: "https://example.test", username: "ada", passwordFile: "/unused", fetchImpl: async () => responses.shift(), }); client.accessToken = "test"; assert.deepEqual(await client.libraries(), [{ id: 3 }]); }); test("springPage supports the nested Spring page metadata", async () => { let request = 0; const client = new GrimmoryClient({ baseUrl: "https://example.test", username: "ada", passwordFile: "/unused", fetchImpl: async () => { const page = request++; return new Response( JSON.stringify({ data: { content: page === 0 ? Array.from({ length: 100 }, (_, id) => id) : [100], page: { number: page, totalPages: 2 }, }, }), ); }, }); client.accessToken = "test"; assert.equal((await client.springPage("/sessions")).length, 101); assert.equal(request, 2); }); test("allBooks follows nested pagination and rejects missing metadata", async () => { let request = 0; const client = new GrimmoryClient({ baseUrl: "https://example.test", username: "ada", passwordFile: "/unused", fetchImpl: async () => { const page = request++; return new Response(JSON.stringify({ data: { content: page === 0 ? Array.from({ length: 50 }, (_, id) => ({ id })) : [{ id: 50 }], page: { number: page, totalPages: 2 }, } })); }, }); client.accessToken = "test"; assert.equal((await client.allBooks()).length, 51); assert.equal(request, 2); }); test("concurrent first requests share one login", async () => { let logins = 0; const client = new GrimmoryClient({ baseUrl: "https://example.test", username: "ada", passwordFile: "/unused", fetchImpl: async () => new Response(JSON.stringify({ data: { ok: true } })), }); client.login = async () => { logins += 1; await new Promise((resolve) => setTimeout(resolve, 10)); client.accessToken = "test"; }; await Promise.all([client.request("/one"), client.request("/two")]); assert.equal(logins, 1); }); test("springPage rejects missing pagination metadata on a full page", async () => { const client = new GrimmoryClient({ baseUrl: "https://example.test", username: "ada", passwordFile: "/unused", fetchImpl: async () => new Response(JSON.stringify({ data: { content: Array.from({ length: 100 }, (_, id) => ({ id })), } })), }); client.accessToken = "test"; await assert.rejects(() => client.springPage("/sessions"), /no pagination metadata/); });