100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import {
|
|
checkPerformanceBudgets,
|
|
createContext,
|
|
createTracer,
|
|
dedupe,
|
|
defineAction,
|
|
defineEndpoint,
|
|
defineFeatureFlags,
|
|
defineLoader,
|
|
tenantFromSubdomain,
|
|
assertTenantAccess,
|
|
createTenantDirectory,
|
|
tenantKey,
|
|
tracingMiddleware,
|
|
} from "../src/index.ts";
|
|
|
|
function context(url = "https://acme.example.com/dashboard") {
|
|
return createContext(new Request(url), new URL(url));
|
|
}
|
|
|
|
test("typed endpoints validate authentication and preserve a stable JSON envelope", async () => {
|
|
const endpoint = defineEndpoint<{ value: number }, { doubled: number }>({
|
|
auth: "required",
|
|
input: {
|
|
parse(input) {
|
|
const value = Number((input as { value?: unknown })?.value);
|
|
if (!Number.isFinite(value)) throw new Error("invalid");
|
|
return { value };
|
|
},
|
|
},
|
|
handler: ({ value }) => ({ doubled: value * 2 }),
|
|
});
|
|
|
|
expect((await endpoint(context(), { value: 4 })).status).toBe(401);
|
|
const authenticated = context();
|
|
authenticated.user = { id: "user-1" };
|
|
expect(await (await endpoint(authenticated, { value: 4 })).json()).toEqual({
|
|
data: { doubled: 8 },
|
|
});
|
|
});
|
|
|
|
test("tenant boundaries, memberships, workspaces, quotas, and audit events fail closed", async () => {
|
|
const events: string[] = [];
|
|
const directory = createTenantDirectory({
|
|
audit: (event) => {
|
|
events.push(event.action);
|
|
},
|
|
now: () => 10,
|
|
});
|
|
await directory.addMembership({ tenantId: "acme", userId: "u1", workspaceIds: ["north"] });
|
|
expect(await directory.switchWorkspace("acme", "u1", "north")).toEqual({
|
|
tenantId: "acme",
|
|
workspaceId: "north",
|
|
});
|
|
await expect(directory.switchWorkspace("acme", "u1", "south")).rejects.toThrow(
|
|
"WRN-TENANT-WORKSPACE-DENIED",
|
|
);
|
|
directory.setQuota("acme", "storage", 100);
|
|
expect(() => directory.enforceQuota("acme", "storage", 90, 11)).toThrow("WRN-TENANT-QUOTA");
|
|
expect(() => assertTenantAccess({ id: "acme" }, { tenantId: "other" })).toThrow(
|
|
"WRN-TENANT-CROSS-ACCESS",
|
|
);
|
|
expect(tenantKey("acme", "cache", 1)).toBe("tenant:acme:cache:1");
|
|
expect(events).toEqual(["membership.added", "workspace.switched"]);
|
|
});
|
|
|
|
test("loaders, actions, and request-local dedupe remain framework-agnostic", async () => {
|
|
let calls = 0;
|
|
const loader = defineLoader({ load: async () => ({ ready: true }) });
|
|
const action = defineAction<{ name: string }, string>({ run: async (input) => input.name });
|
|
const ctx = context();
|
|
const first = dedupe(ctx, "profile", async () => ++calls);
|
|
const second = dedupe(ctx, "profile", async () => ++calls);
|
|
|
|
expect(await loader(ctx)).toEqual({ ready: true });
|
|
expect(await action({ name: "Ajay" }, ctx)).toBe("Ajay");
|
|
expect(await Promise.all([first, second])).toEqual([1, 1]);
|
|
expect(calls).toBe(1);
|
|
});
|
|
|
|
test("feature flags, tenant resolution, budgets, and tracing compose", async () => {
|
|
const ctx = context();
|
|
const resolveTenant = tenantFromSubdomain(async (slug) => ({ id: slug, slug }), ["example.com"]);
|
|
expect(await resolveTenant(ctx)).toEqual({ id: "acme", slug: "acme" });
|
|
|
|
const flags = defineFeatureFlags({ dashboardV2: true, seats: 25 });
|
|
expect(await flags.enabled("dashboardV2", ctx)).toBe(true);
|
|
expect(await flags.get("seats", ctx)).toBe(25);
|
|
|
|
expect(checkPerformanceBudgets({ routeJsBytes: 100 }, { routeJsBytes: 130 })).toEqual([
|
|
{ metric: "routeJsBytes", budget: 100, actual: 130, overBy: 30 },
|
|
]);
|
|
|
|
const tracer = createTracer(() => 10);
|
|
const middleware = tracingMiddleware(() => tracer, { serverTiming: true });
|
|
const response = await middleware(ctx, () => new Response("ok"));
|
|
expect(response.headers.get("server-timing")).toContain("http.request");
|
|
});
|