180 lines
6.5 KiB
TypeScript
180 lines
6.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer } from "./generated/core/bodySerializer.gen.js";
|
|
import { buildClientParams } from "./generated/core/params.gen.js";
|
|
import {
|
|
defaultPathSerializer,
|
|
getUrl,
|
|
getValidRequestBody,
|
|
} from "./generated/core/utils.gen.js";
|
|
import {
|
|
separatorArrayExplode,
|
|
separatorArrayNoExplode,
|
|
separatorObjectExplode,
|
|
serializeArrayParam,
|
|
serializeObjectParam,
|
|
serializePrimitiveParam,
|
|
} from "./generated/core/pathSerializer.gen.js";
|
|
import { serializeQueryKeyValue, stringifyToJsonValue } from "./generated/core/queryKeySerializer.gen.js";
|
|
|
|
describe("generated core body serializers", () => {
|
|
it("serializes JSON bodies with bigint values", () => {
|
|
expect(jsonBodySerializer.bodySerializer({ id: 12n, name: "tool" })).toBe('{"id":"12","name":"tool"}');
|
|
});
|
|
|
|
it("serializes FormData bodies while skipping nullish values", async () => {
|
|
const file = new Blob(["hello"], { type: "text/plain" });
|
|
const date = new Date("2026-05-25T10:30:00.000Z");
|
|
|
|
const data = formDataBodySerializer.bodySerializer({
|
|
ignored: undefined,
|
|
list: ["a", 2],
|
|
meta: { ok: true },
|
|
stamp: date,
|
|
upload: file,
|
|
});
|
|
|
|
expect(data.getAll("list")).toEqual(["a", "2"]);
|
|
expect(data.get("meta")).toBe('{"ok":true}');
|
|
expect(data.get("stamp")).toBe(date.toISOString());
|
|
expect(data.get("upload")).toBeInstanceOf(Blob);
|
|
expect(data.has("ignored")).toBe(false);
|
|
});
|
|
|
|
it("serializes URLSearchParams bodies with repeated array entries", () => {
|
|
expect(
|
|
urlSearchParamsBodySerializer.bodySerializer({
|
|
filters: ["active", { owner: "me" }],
|
|
page: 2,
|
|
skipped: null,
|
|
})
|
|
).toBe("filters=active&filters=%7B%22owner%22%3A%22me%22%7D&page=2");
|
|
});
|
|
});
|
|
|
|
describe("generated core parameter mapping", () => {
|
|
it("maps positional and object parameters into request slots", () => {
|
|
const params = buildClientParams(
|
|
[
|
|
"project-1",
|
|
{ raw: true },
|
|
{
|
|
$headers_traceparent: "00-ab",
|
|
$query_search: "drill",
|
|
apiVersion: "2026-05",
|
|
extra: "kept",
|
|
payload: { name: "new tool" },
|
|
},
|
|
],
|
|
[
|
|
{ in: "path", key: "projectId", map: "project_id" },
|
|
{ in: "body" },
|
|
{
|
|
allowExtra: { query: true },
|
|
args: [
|
|
{ in: "headers", key: "apiVersion", map: "X-Api-Version" },
|
|
{ key: "payload", map: "body" },
|
|
],
|
|
},
|
|
]
|
|
);
|
|
|
|
expect(params).toEqual({
|
|
body: { name: "new tool" },
|
|
headers: {
|
|
"X-Api-Version": "2026-05",
|
|
traceparent: "00-ab",
|
|
},
|
|
path: { project_id: "project-1" },
|
|
query: {
|
|
extra: "kept",
|
|
search: "drill",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("removes empty slots from the mapped params", () => {
|
|
expect(buildClientParams(["abc"], [{ in: "path", key: "id" }])).toEqual({ path: { id: "abc" } });
|
|
});
|
|
});
|
|
|
|
describe("generated core path and URL serializers", () => {
|
|
it("serializes primitive, array, and object path params", () => {
|
|
expect(
|
|
defaultPathSerializer({
|
|
path: {
|
|
coords: ["10", "20"],
|
|
filter: { status: "open", owner: "ops" },
|
|
id: "tool/a",
|
|
tag: "beta",
|
|
},
|
|
url: "/v1/tools/{id}/{.tag}/{;coords*}/{filter}",
|
|
})
|
|
).toBe("/v1/tools/tool%2Fa/.beta/;coords=10;coords=20/status,open,owner,ops");
|
|
});
|
|
|
|
it("serializes query and path parameters into a full URL", () => {
|
|
const url = getUrl({
|
|
baseUrl: "https://api.example.test",
|
|
path: { id: "a/b" },
|
|
query: { active: true, tags: ["red", "blue"] },
|
|
querySerializer: (query) => `?active=${query.active}&tags=${(query.tags as string[]).join(",")}`,
|
|
url: "v1/tools/{id}",
|
|
});
|
|
|
|
expect(url).toBe("https://api.example.test/v1/tools/a%2Fb?active=true&tags=red,blue");
|
|
});
|
|
|
|
it("exposes OpenAPI separator and primitive serialization behavior", () => {
|
|
expect(separatorArrayExplode("label")).toBe(".");
|
|
expect(separatorArrayNoExplode("pipeDelimited")).toBe("|");
|
|
expect(separatorObjectExplode("matrix")).toBe(";");
|
|
expect(serializeArrayParam({ explode: false, name: "ids", style: "pipeDelimited", value: ["a/b", "c"] })).toBe("ids=a%2Fb|c");
|
|
expect(serializePrimitiveParam({ allowReserved: true, name: "q", value: "a/b?c" })).toBe("q=a/b?c");
|
|
expect(() => serializePrimitiveParam({ name: "bad", value: { nested: true } as never })).toThrow(/Deeply-nested/);
|
|
});
|
|
|
|
it("serializes object params for dates, labels, and deep objects", () => {
|
|
const date = new Date("2026-05-25T12:00:00.000Z");
|
|
|
|
expect(serializeObjectParam({ explode: true, name: "at", style: "form", value: date, valueOnly: true })).toBe(date.toISOString());
|
|
expect(serializeObjectParam({ explode: false, name: "filter", style: "label", value: { status: "open" } })).toBe(".status,open");
|
|
expect(serializeObjectParam({ explode: true, name: "filter", style: "deepObject", value: { status: "open" } })).toBe(
|
|
"filter[status]=open"
|
|
);
|
|
});
|
|
|
|
it("returns the valid request body shape for serialized, plain, and empty bodies", () => {
|
|
expect(getValidRequestBody({ body: { ok: true } })).toEqual({ ok: true });
|
|
expect(getValidRequestBody({ body: "", bodySerializer: JSON.stringify })).toBeNull();
|
|
expect(getValidRequestBody({ body: { ok: true }, bodySerializer: JSON.stringify, serializedBody: '{"ok":true}' })).toBe('{"ok":true}');
|
|
expect(getValidRequestBody({ body: { ok: true }, bodySerializer: JSON.stringify, serializedBody: "" })).toBeNull();
|
|
expect(getValidRequestBody({})).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("generated core query key serialization", () => {
|
|
it("normalizes query key values into JSON-safe shapes", () => {
|
|
const params = new URLSearchParams([
|
|
["b", "2"],
|
|
["a", "1"],
|
|
["a", "3"],
|
|
]);
|
|
|
|
expect(serializeQueryKeyValue(params)).toEqual({ a: ["1", "3"], b: "2" });
|
|
expect(serializeQueryKeyValue({ createdAt: new Date("2026-05-25T00:00:00.000Z"), id: 123n })).toEqual({
|
|
createdAt: "2026-05-25T00:00:00.000Z",
|
|
id: "123",
|
|
});
|
|
expect(serializeQueryKeyValue(Object.create(null))).toEqual({});
|
|
expect(serializeQueryKeyValue(Symbol("skip"))).toBeUndefined();
|
|
});
|
|
|
|
it("returns undefined when JSON serialization cannot produce a value", () => {
|
|
const circular: Record<string, unknown> = {};
|
|
circular.self = circular;
|
|
|
|
expect(stringifyToJsonValue(circular)).toBeUndefined();
|
|
expect(stringifyToJsonValue(() => undefined)).toBeUndefined();
|
|
});
|
|
});
|