61 lines
4 KiB
TypeScript
61 lines
4 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { astroComponent } from "../modules/astro/index.ts";
|
|
import { TemplateContext } from "../core/context.ts";
|
|
import { createViews } from "../views/api.ts";
|
|
import { authFromRequest, authGuard, authIntegration } from "../integrations/auth.ts";
|
|
import { App } from "../core/app.ts";
|
|
import { resolveConfig } from "../core/config.ts";
|
|
import { renderMdx } from "../modules/mdx/index.ts";
|
|
import { compileCss } from "../views/css/index.ts";
|
|
|
|
test("Astro imports compose with typed props and SPUG slots", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "astro-compose-"));
|
|
try {
|
|
await writeFile(join(root, "Title.astro"), '---\nconst { title } = Astro.props;\n---\n<h2>{title}</h2>');
|
|
await writeFile(join(root, "Card.astro"), '---\nimport Title from "./Title.astro";\nconst { title } = Astro.props;\n---\n<article><Title title={title} /><slot /></article>');
|
|
await writeFile(join(root, "index.spug"), 'component Card\n title: "Composed"\n render content\n p Slot body');
|
|
const context = new TemplateContext().import("Card", astroComponent(join(root, "Card.astro")));
|
|
const views = createViews({ viewsDir: root, context });
|
|
const html = await views.renderFile("index");
|
|
expect(html).toContain("<h2>Composed</h2>");
|
|
expect(html).toContain("<p>Slot body</p>");
|
|
} finally { await rm(root, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test("form authentication issues an opaque session and rejects forged identity cookies", async () => {
|
|
const app = await App.create({ config: resolveConfig(), integrations: [authIntegration({ login: (email, password) => password === "secret" ? { id: email } : undefined })] });
|
|
const response = await app.fetch(new Request("https://example.test/auth/login", { method: "POST", headers: { origin: "https://example.test" }, body: new URLSearchParams({ email: "ada", password: "secret" }) }));
|
|
expect(response.status).toBe(302);
|
|
const cookie = response.headers.get("set-cookie")!;
|
|
expect((await authFromRequest(new Request("https://example.test", { headers: { cookie } })))?.id).toBe("ada");
|
|
expect(await authFromRequest(new Request("https://example.test", { headers: { cookie: 'muddle_auth=%7B%22id%22%3A%22admin%22%7D' } }))).toBeUndefined();
|
|
});
|
|
|
|
test("OAuth callback validates browser-bound state and exchanges a PKCE code", async () => {
|
|
const original = globalThis.fetch;
|
|
let exchanged = false;
|
|
globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
if (String(input).endsWith("/token")) {
|
|
const body = init?.body as URLSearchParams;
|
|
expect(body.get("code_verifier")?.length).toBeGreaterThan(40);
|
|
exchanged = true;
|
|
return Response.json({ access_token: "test-token" });
|
|
}
|
|
return Response.json({ sub: "oauth-user" });
|
|
}) as typeof fetch;
|
|
try {
|
|
const app = await App.create({ config: resolveConfig(), integrations: [authIntegration({ providers: [{ name: "test", authorizationUrl: "https://provider.test/authorize", tokenUrl: "https://provider.test/token", userInfoUrl: "https://provider.test/user", clientId: "test", redirectUri: "https://app.test/auth/test/callback", identity: (profile) => ({ id: String(profile.sub) }) }] })] });
|
|
const start = await app.fetch(new Request("https://app.test/auth/test"));
|
|
const authorization = new URL(start.headers.get("location")!);
|
|
expect(authorization.searchParams.get("code_challenge_method")).toBe("S256");
|
|
const callback = `https://app.test/auth/test/callback?code=test-code&state=${authorization.searchParams.get("state")}`;
|
|
expect((await app.fetch(new Request(callback))).status).toBe(400);
|
|
const response = await app.fetch(new Request(callback, { headers: { cookie: start.headers.get("set-cookie")! } }));
|
|
expect(response.status).toBe(302);
|
|
expect(exchanged).toBe(true);
|
|
expect((await app.fetch(new Request(callback, { headers: { cookie: start.headers.get("set-cookie")! } }))).status).toBe(400);
|
|
} finally { globalThis.fetch = original; }
|
|
});
|