test/shared/external-order.test.ts
Rama main · réplica de lectura del repositorio original.
import { assert, assertStringIncludes } from "@std/assert";
import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import {
buildCatalog,
resolveAllowOrigin,
serializeCatalog,
} from "#shared/external-order.ts";
import { testListing } from "#test-utils/factories.ts";
const base = {
currency: "GBP",
debug: false,
decimalPlaces: 2,
generatedAt: "2026-06-28T20:00:00Z",
origin: "https://tickets.test",
};
describe("external-order", () => {
describe("resolveAllowOrigin", () => {
test("empty allow-list returns wildcard for any origin", () => {
expect(resolveAllowOrigin("https://anywhere.test", [])).toBe("*");
});
test("echoes an exactly-listed origin", () => {
expect(resolveAllowOrigin("https://example.com", ["example.com"])).toBe(
"https://example.com",
);
});
test("matches the origin host case-insensitively", () => {
expect(resolveAllowOrigin("https://EXAMPLE.com", ["example.com"])).toBe(
"https://EXAMPLE.com",
);
});
test("ignores the origin port", () => {
expect(
resolveAllowOrigin("https://example.com:8443", ["example.com"]),
).toBe("https://example.com:8443");
});
test("matches a wildcard subdomain", () => {
expect(
resolveAllowOrigin("https://www.example.com", ["*.example.com"]),
).toBe("https://www.example.com");
});
test("wildcard does not match the bare apex", () => {
expect(
resolveAllowOrigin("https://example.com", ["*.example.com"]),
).toBeNull();
});
test("rejects an unlisted origin", () => {
expect(
resolveAllowOrigin("https://evil.com", ["example.com"]),
).toBeNull();
});
test("wildcard requires a dot boundary, not just the suffix text", () => {
// `bbexample.com` ends with `example.com` but not with the `.example.com`
// the wildcard demands — the host-length check alone would wrongly allow
// it, so the boundary `&&` must hold.
expect(
resolveAllowOrigin("https://bbexample.com", ["*.example.com"]),
).toBeNull();
});
test("rejects a missing origin against a non-empty list", () => {
expect(resolveAllowOrigin(null, ["example.com"])).toBeNull();
});
test("rejects an empty-string origin against a non-empty list", () => {
// An empty `Origin` header is falsy but still a string; it must be denied
// (null), not echoed back as the allowed origin.
expect(resolveAllowOrigin("", ["example.com"])).toBeNull();
});
test("rejects a malformed origin", () => {
expect(resolveAllowOrigin("not a url", ["example.com"])).toBeNull();
});
});
describe("buildCatalog", () => {
test("keys active non-hidden entries by slug, excludes hidden + inactive", () => {
const catalog = buildCatalog({
...base,
listings: [
testListing({ id: 1, name: "Open", slug: "open", unit_price: 1500 }),
testListing({ hidden: true, id: 2, name: "Secret", slug: "secret" }),
testListing({ active: false, id: 3, name: "Closed", slug: "closed" }),
],
});
expect(Object.keys(catalog.listings)).toEqual(["open"]);
expect(catalog.listings.open).toEqual({
id: 1,
name: "Open",
slug: "open",
unitPrice: 1500,
variablePrice: false,
});
expect(catalog.origin).toBe("https://tickets.test");
expect(catalog.currency).toBe("GBP");
expect(catalog.decimalPlaces).toBe(2);
expect(catalog.generatedAt).toBe("2026-06-28T20:00:00Z");
expect(catalog.debug).toBe(false);
});
test("carries the debug flag through to the catalog", () => {
const catalog = buildCatalog({ ...base, debug: true, listings: [] });
expect(catalog.debug).toBe(true);
});
test("flags variablePrice for daily, customisable-days, and PWYW", () => {
const catalog = buildCatalog({
...base,
listings: [
testListing({ id: 1, listing_type: "daily", slug: "daily" }),
testListing({ customisable_days: true, id: 2, slug: "days" }),
testListing({ can_pay_more: true, id: 3, slug: "pwyw" }),
testListing({ id: 4, slug: "fixed" }),
],
});
expect(
(catalog.listings.daily as { variablePrice: boolean }).variablePrice,
).toBe(true);
expect(
(catalog.listings.days as { variablePrice: boolean }).variablePrice,
).toBe(true);
expect(
(catalog.listings.pwyw as { variablePrice: boolean }).variablePrice,
).toBe(true);
expect(
(catalog.listings.fixed as { variablePrice: boolean }).variablePrice,
).toBe(false);
});
test("keys package bundles by slug, separate from listings", () => {
const catalog = buildCatalog({
...base,
listings: [testListing({ id: 1, name: "Solo", slug: "solo" })],
packages: [
{ name: "Camp Bundle", slug: "camp" },
{ name: "Beach Bundle", slug: "beach" },
],
});
expect(Object.keys(catalog.listings)).toEqual(["solo"]);
expect(catalog.packages).toEqual({
beach: { name: "Beach Bundle", slug: "beach" },
camp: { name: "Camp Bundle", slug: "camp" },
});
});
test("defaults packages to an empty object when none are given", () => {
const catalog = buildCatalog({ ...base, listings: [] });
expect(catalog.packages).toEqual({});
});
});
describe("serializeCatalog", () => {
test("produces a CATALOG const that round-trips", () => {
const catalog = buildCatalog({ ...base, listings: [] });
const out = serializeCatalog(catalog);
expect(out).toMatch(/^const CATALOG = \{/);
const json = out.slice("const CATALOG = ".length, -1);
expect(JSON.parse(json)).toEqual(catalog);
});
test("escapes < so a name cannot form a closing script tag", () => {
const catalog = buildCatalog({
...base,
listings: [
testListing({ id: 1, name: "</script><img src=x>", slug: "x" }),
],
});
const out = serializeCatalog(catalog);
assert(!out.includes("</script>"));
assertStringIncludes(out, "\\u003c");
});
});
});