Files

66 lines
1.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { buildRequestTarget } from "../../src/adapters/http/request-builder.ts";
import type { ApiOperation } from "../../src/contracts/api-operations.ts";
const operation: ApiOperation = {
method: "GET",
path: "/api/resources/{resourceId}",
operationId: "GET_RESOURCE",
auth: "none",
timeoutMs: null,
idempotency: "safe",
retry: "runtime",
requestSource: "search",
requestSchema: "ResourceQuery",
responseSchema: "ResourcePayload",
owner: "test",
};
describe("deterministic HTTP request target", () => {
it("escapes path values and serializes optional/array search in key order", () => {
const result = buildRequestTarget(
"https://api.test/base/",
operation,
{ resourceId: "folder/item" },
{
tags: ["beta", "alpha"],
omitted: undefined,
limit: 20,
cursor: "next page",
},
);
expect(result.success).toBe(true);
if (!result.success) return;
expect(result.url.href).toBe(
"https://api.test/base/api/resources/folder%2Fitem?cursor=next+page&limit=20&tags=beta&tags=alpha",
);
});
it("fails closed when a path value or scalar search value is invalid", () => {
expect(
buildRequestTarget("https://api.test", operation, {}, {}),
).toEqual({ success: false, code: "PATH_PARAMETER_MISSING" });
expect(
buildRequestTarget(
"https://api.test",
operation,
{ resourceId: "one" },
{ nested: { secret: true } },
),
).toEqual({ success: false, code: "SEARCH_PARAMETER_INVALID" });
expect(
buildRequestTarget(
"https://api.test",
operation,
{ resourceId: "one", unexpected: "two" },
{},
),
).toEqual({ success: false, code: "PATH_PARAMETER_UNEXPECTED" });
expect(
buildRequestTarget("http://api.test", operation, { resourceId: "one" }),
).toEqual({ success: false, code: "BASE_URL_INVALID" });
});
});