import { existsSync, mkdtempSync } from "node:fs";import { tmpdir } from "node:os";import { join } from "node:path";import { afterAll, expect, test } from "@effect/vitest";// Fully zero-config boot: NO BETTER_AUTH_SECRET and NO bootstrap admin env, so// the secret is generated + persisted and the org is created with no members —// the turnkey first-run path.const DATA_DIR = mkdtempSync(join(tmpdir(), "eh-firstrun-"));process.env.EXECUTOR_DATA_DIR = DATA_DIR;delete process.env.BETTER_AUTH_SECRET;delete process.env.AUTH_SECRET;delete process.env.EXECUTOR_BOOTSTRAP_ADMIN_EMAIL;delete process.env.EXECUTOR_BOOTSTRAP_ADMIN_PASSWORD;const { makeSelfHostApiHandler } = await import("./app");const { handler, dispose } = await makeSelfHostApiHandler();afterAll(() => dispose());const BASE = "http://localhost:4788";const get = (path: string) => handler(new Request(`${BASE}${path}`));const signUp = (body: Record<string, unknown>) => handler( new Request(`${BASE}/api/auth/sign-up/email`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), }), );test("zero-config boot generates and persists a session secret in the data dir", () => { expect(existsSync(join(DATA_DIR, "auth-secret.key"))).toBe(true);});test("health endpoint reports ok", async () => { const res = await get("/api/health"); expect(res.status).toBe(200); expect(((await res.json()) as { status: string }).status).toBe("ok");});test("a fresh instance needs setup, admits the first signup as owner, then gates the rest", async () => { // Before anyone signs up, the org has zero members. const before = await get("/api/setup-status"); expect(before.status).toBe(200); expect(((await before.json()) as { needsSetup: boolean }).needsSetup).toBe(true); // The first signup needs NO invite code and claims the org. const first = await signUp({ email: "owner@firstrun.test", password: "password-12345678", name: "Owner", }); expect(first.status).toBe(200); const token = first.headers.get("set-auth-token") ?? ""; expect(token).not.toBe(""); // Setup is now complete. const after = await get("/api/setup-status"); expect(((await after.json()) as { needsSetup: boolean }).needsSetup).toBe(false); // The first user is the owner: the admin API admits them. const invites = await handler( new Request(`${BASE}/api/admin/invites`, { headers: { authorization: `Bearer ${token}` } }), ); expect(invites.status).toBe(200); // A second signup with no code is now rejected — the invite gate is in force. const second = await signUp({ email: "intruder@firstrun.test", password: "password-12345678", name: "Intruder", }); expect(second.status).not.toBe(200);});
import { existsSync, mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, expect, test } from "@effect/vitest";
// Fully zero-config boot: NO BETTER_AUTH_SECRET and NO bootstrap admin env, so
// the secret is generated + persisted and the org is created with no members —
// the turnkey first-run path.
const DATA_DIR = mkdtempSync(join(tmpdir(), "eh-firstrun-"));
process.env.EXECUTOR_DATA_DIR = DATA_DIR;
delete process.env.BETTER_AUTH_SECRET;
delete process.env.AUTH_SECRET;
delete process.env.EXECUTOR_BOOTSTRAP_ADMIN_EMAIL;
delete process.env.EXECUTOR_BOOTSTRAP_ADMIN_PASSWORD;
const { makeSelfHostApiHandler } = await import("./app");
const { handler, dispose } = await makeSelfHostApiHandler();
afterAll(() => dispose());
const BASE = "http://localhost:4788";
const get = (path: string) => handler(new Request(`${BASE}${path}`));
const signUp = (body: Record<string, unknown>) =>
handler(
new Request(`${BASE}/api/auth/sign-up/email`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
}),
);
test("zero-config boot generates and persists a session secret in the data dir", () => {
expect(existsSync(join(DATA_DIR, "auth-secret.key"))).toBe(true);
});
test("health endpoint reports ok", async () => {
const res = await get("/api/health");
expect(res.status).toBe(200);
expect(((await res.json()) as { status: string }).status).toBe("ok");
});
test("a fresh instance needs setup, admits the first signup as owner, then gates the rest", async () => {
// Before anyone signs up, the org has zero members.
const before = await get("/api/setup-status");
expect(before.status).toBe(200);
expect(((await before.json()) as { needsSetup: boolean }).needsSetup).toBe(true);
// The first signup needs NO invite code and claims the org.
const first = await signUp({
email: "owner@firstrun.test",
password: "password-12345678",
name: "Owner",
});
expect(first.status).toBe(200);
const token = first.headers.get("set-auth-token") ?? "";
expect(token).not.toBe("");
// Setup is now complete.
const after = await get("/api/setup-status");
expect(((await after.json()) as { needsSetup: boolean }).needsSetup).toBe(false);
// The first user is the owner: the admin API admits them.
const invites = await handler(
new Request(`${BASE}/api/admin/invites`, { headers: { authorization: `Bearer ${token}` } }),
);
expect(invites.status).toBe(200);
// A second signup with no code is now rejected — the invite gate is in force.
const second = await signUp({
email: "intruder@firstrun.test",
password: "password-12345678",
name: "Intruder",
});
expect(second.status).not.toBe(200);
});