import { expect, test } from "bun:test"; import { buildApiRequest } from "../src/api-request.ts"; import { REACTIVE_RUNTIME } from "../../csr/src/reactive-runtime.ts"; test("GET builds a query string", () => { expect(buildApiRequest("/api/users", "GET", { name: "Ajay" }).url).toBe("/api/users?name=Ajay"); }); test("GET omits undefined, null and empty string", () => { const built = buildApiRequest("/api/users", "GET", { name: "Ajay", age: undefined, team: null, note: "", }); expect(built.url).toBe("/api/users?name=Ajay"); }); test("GET keeps 0 and false", () => { // A filter of 0 or false is a real value; dropping it silently would be a bug. const built = buildApiRequest("/api/users", "GET", { count: 0, active: false }); expect(built.url).toContain("count=0"); expect(built.url).toContain("active=false"); }); test("GET has no body", () => { expect(buildApiRequest("/api/users", "GET", { name: "Ajay" }).body).toBeUndefined(); }); test("POST sends a JSON body and no query", () => { const built = buildApiRequest("/api/users", "POST", { name: "Ajay" }); expect(built.url).toBe("/api/users"); expect(built.body).toBe(JSON.stringify({ name: "Ajay" })); expect(built.contentType).toBe("application/json"); }); test("POST with no input sends an empty object", () => { expect(buildApiRequest("/api/users", "POST", undefined).body).toBe("{}"); }); test("values are encoded", () => { expect(buildApiRequest("/api/users", "GET", { name: "a b&c" }).url).toBe( "/api/users?name=a%20b%26c", ); }); test("the browser runtime and the shared builder agree", () => { // Cheap structural guard: the runtime must apply the same omission rule. // If someone changes one side's rules, this fails. expect(REACTIVE_RUNTIME).toContain('value === ""'); expect(REACTIVE_RUNTIME).toContain("application/json"); });