fuckingtech/tests/one-session.test.ts
Elias Rhouzlane 5e0ebd2f94 chore: port to autokit 39 (client-db 17, admin 24, operator 11), server components kept
Replaces the legacy Bureau CMS stack (@bureaudouble/bureau, deno-sqlite-blob, Fresh-style RSC)
with the fleet layout: a defineProgram schema over the legacy tables (rows carry over by id), the
timeline-selected blob database (autokit.sqlite), the operator CLI and mf predeploy, the
package-shipped admin with a plugin per table, and the /api rpc/mcp/openapi door.

The pages still render as server components, wired like wtfutures: one Autokit session per
process, the request's db minted once in a createStart request middleware, read as context.db by
each page's server function and handed to the server components as a db prop; the rsc graph never
imports the session module (tests/one-session.test.ts).

French stays at / and English at /en, now through Autokit's locales: the *_en columns became the
translationsJson bag beside their French base text. The integer order columns of persons, roles
and resources became order keys (drag-to-reorder in the admin); media columns are stored-media
lists resolved through autokit-media. The age gate is the same shared page for every visitor: a
head script marks a visitor who already answered, before the first paint.
2026-10-31 19:08:41 +01:00

109 lines
4.2 KiB
TypeScript

import { execFileSync, spawn, type ChildProcess } from "node:child_process";
import { readdirSync, readFileSync } from "node:fs";
import { createServer, type Server } from "node:http";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vite-plus/test";
const root = join(import.meta.dirname, "..");
const scripts = (directory: string): string[] =>
readdirSync(join(root, directory), { recursive: true, encoding: "utf8" })
.filter((file) => file.endsWith(".js"))
.map((file) => readFileSync(join(root, directory, file), "utf8"));
// String literals that survive minification: the session module's own (`src/autokit/server.ts`)
// and its executor's.
const sessionModule = "sharp.elias.systems";
const sessionMarkers = [
sessionModule,
"createAutokitSession",
"createSqliteExecutor",
"compress:`zstd`",
];
describe("the built server (run after `vp build`)", () => {
it("boots the app's session once, in the SSR graph: the rsc graph reaches none of it", () => {
for (const text of scripts("dist/server/rsc")) {
for (const marker of sessionMarkers) expect(text).not.toContain(marker);
}
expect(
scripts("dist/server/assets").filter((text) => text.includes(sessionModule)),
).toHaveLength(1);
});
});
// The page cache, end to end, against a rehearsal database the test may write to (a `sites`
// commit that rewrites the title it holds): `ONE_SESSION_TIMELINE=<branch>` (`db branch create`).
const timeline = process.env["ONE_SESSION_TIMELINE"];
describe.runIf(timeline !== undefined)("a page rendered through server components", () => {
const purges: string[] = [];
let stub: Server;
let server: ChildProcess;
let origin: string;
beforeAll(async () => {
stub = createServer((request, response) => {
if (request.method === "PURGE") purges.push(String(request.headers["surrogate-key"]));
response.end();
});
await new Promise<void>((resolve) => stub.listen(0, "127.0.0.1", resolve));
const address = stub.address();
if (address === null || typeof address === "string") throw new Error("stub has no port");
execFileSync("vp", ["build", "--config", "tests/one-session.vite.config.ts"], {
cwd: root,
stdio: "ignore",
});
server = spawn(
"deno",
["serve", "-A", "--env", "--port", "0", ".output/one-session/server/one-session-entry.js"],
{
cwd: root,
env: {
...process.env,
MF_TIMELINE: timeline,
ONE_SESSION_PURGE_URL: `http://127.0.0.1:${address.port}/`,
},
stdio: ["ignore", "inherit", "pipe"],
},
);
origin = await new Promise<string>((resolve) => {
server.stderr?.on("data", (chunk: Buffer) => {
const listening = /Listening on http:\/\/[^:]+:(\d+)/.exec(String(chunk));
if (listening) resolve(`http://127.0.0.1:${listening[1]}`);
});
});
}, 300_000);
afterAll(() => {
server.kill();
stub.close();
});
it("is shared, tagged by the reads its server components made", async () => {
const response = await fetch(`${origin}/`);
await response.text();
expect(response.status).toBe(200);
expect(response.headers.get("cdn-cache-control")).toBeTruthy();
expect(response.headers.get("etag")).toBeTruthy();
const tags = response.headers.get("surrogate-key")?.split(/,\s*/) ?? [];
expect(tags).toEqual(expect.arrayContaining(["autokit", "t:sites"]));
expect(tags.some((tag) => tag.startsWith("u:"))).toBe(true);
}, 60_000);
it("is not shared with a signed-in request", async () => {
const response = await fetch(`${origin}/`, {
headers: { cookie: "autokit-auth.session_token=x" },
});
await response.text();
expect(response.status).toBe(200);
expect(response.headers.get("cdn-cache-control")).toBeNull();
expect(response.headers.get("surrogate-key")).toBeNull();
}, 60_000);
it("is purged by a commit on a table it read", async () => {
const response = await fetch(`${origin}/__one-session/commit`, { method: "POST" });
expect(response.status).toBe(200);
await response.text();
expect(purges.some((tags) => tags.split(/,\s*/).includes("t:sites"))).toBe(true);
}, 60_000);
});