Files
freno-dev/src/lib/site-context.test.ts
Michael Freno 92890ce259 feat: add subdomain-based site context with SSR resolution
Resolve the active site from the request Host header on the server and
expose it through a SiteContext provider so the app can vary rendering
per subdomain. Serialize the resolved site into the document shell
(data-site attribute + window.__SITE__) so client hydration matches the
server render. Add host-based Vercel rewrites mapping each subdomain to
its site root, and include unit tests for the site resolution logic.
2026-07-23 09:18:55 -04:00

100 lines
3.2 KiB
TypeScript

/**
* Unit tests for the shared site-context resolver (task 01).
*
* `resolveSiteFromHost` is pure — no env / no I/O — so the cases below are
* straightforward synchronous assertions mirroring the acceptance matrix in
* the task spec.
*/
import { describe, it, expect } from "bun:test";
import {
resolveSiteFromHost,
SITE_CONFIG,
type SiteId
} from "./site-context";
function expectSite(host: string, id: SiteId) {
expect(resolveSiteFromHost(host).id).toBe(id);
}
describe("resolveSiteFromHost", () => {
it("maps each product subdomain to its own site config", () => {
expectSite("nessa.freno.me", "nessa");
expectSite("lineage.freno.me", "lineage");
expectSite("gaze.freno.me", "gaze");
expectSite("inputhalo.freno.me", "inputhalo");
});
it("maps the apex and www hosts to main", () => {
expectSite("freno.me", "main");
expectSite("www.freno.me", "main");
});
it("falls back to main for unknown subdomains", () => {
expectSite("unknown.freno.me", "main");
expectSite("blog.freno.me", "main");
});
it("handles ports", () => {
expectSite("freno.me:3000", "main");
expectSite("nessa.freno.me:8787", "nessa");
expectSite("www.freno.me:443", "main");
});
it("handles localhost dev hosts", () => {
expectSite("localhost", "main");
expectSite("localhost:3000", "main");
expectSite("nessa.localhost:3000", "nessa");
expectSite("nessa.localhost", "nessa");
expectSite("gaze.localhost", "gaze");
expectSite("lineage.localhost", "lineage");
expectSite("inputhalo.localhost", "inputhalo");
});
it("treats unknown *.localhost as main", () => {
expectSite("wat.localhost", "main");
});
it("handles empty / null / undefined hosts by returning main", () => {
expect(resolveSiteFromHost("").id).toBe("main");
expect(resolveSiteFromHost(null).id).toBe("main");
expect(resolveSiteFromHost(undefined).id).toBe("main");
expect(resolveSiteFromHost(" ").id).toBe("main");
});
it("case-insensitively normalizes hosts", () => {
expectSite("NeSsA.Freno.Me", "nessa");
expectSite("WWW.Freno.Me", "main");
expectSite("Gaze.LOCALHOST:3000", "gaze");
});
it("preserves exact dot-match semantics (no prefix bleed)", () => {
// `x-nessa.freno.me` must NOT match `nessa.freno.me`.
expectSite("x-nessa.freno.me", "main");
expectSite("notgaze.freno.me", "main");
});
it("returns the matching SITE_CONFIG entry (full object, not just id)", () => {
expect(resolveSiteFromHost("nessa.freno.me")).toEqual(SITE_CONFIG.nessa);
expect(resolveSiteFromHost("gaze.freno.me")).toEqual(SITE_CONFIG.gaze);
expect(resolveSiteFromHost("freno.me")).toEqual(SITE_CONFIG.main);
});
it("every SITE_CONFIG entry has a non-empty baseRoutePrefix for subdomains", () => {
for (const id of [
"nessa",
"lineage",
"gaze",
"inputhalo"
] as SiteId[]) {
expect(SITE_CONFIG[id].baseRoutePrefix).toBe(`/${id}`);
expect(SITE_CONFIG[id].subdomain).toBe(id);
expect(SITE_CONFIG[id].titleSuffix).toBe(` | ${SITE_CONFIG[id].displayName}`);
}
});
it("main has empty subdomain and empty baseRoutePrefix", () => {
expect(SITE_CONFIG.main.subdomain).toBe("");
expect(SITE_CONFIG.main.baseRoutePrefix).toBe("");
});
});