import fs from "node:fs/promises"; import http from "node:http"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { createCharterServer, startSpecWatcher } from "../src/server.js"; import { createCharterDatabase } from "../src/db/factory.js"; async function makeProject(): Promise { const root = await fs.mkdtemp(path.join(os.tmpdir(), "charter-server-")); await fs.mkdir(path.join(root, "spec")); await fs.writeFile(path.join(root, "spec", "vision.md"), `--- id: vision title: Vision kind: experience status: active --- # Vision Original. `, "utf8"); return root; } async function listen(server: http.Server): Promise { await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", () => resolve()); }); const address = server.address(); if (!address || typeof address === "string") throw new Error("server did not expose a TCP address"); return `http://127.0.0.1:${address.port}`; } async function getJson(baseUrl: string, route: string): Promise { const response = await fetch(`${baseUrl}${route}`); expect(response.ok).toBe(true); return await response.json() as T; } async function postJson(baseUrl: string, route: string, body: unknown, method = "POST"): Promise { const response = await fetch(`${baseUrl}${route}`, { method, headers: { "content-type": "application/json" }, body: JSON.stringify(body), }); expect(response.ok).toBe(true); return await response.json() as T; } async function waitFor(read: () => Promise, pass: (value: T) => boolean): Promise { const started = Date.now(); let latest: T | undefined; do { latest = await read(); if (pass(latest)) return latest; await new Promise((resolve) => setTimeout(resolve, 25)); } while (Date.now() - started < 2500); throw new Error(`condition did not pass; latest=${JSON.stringify(latest)}`); } describe("Charter server", () => { const previousAdapter = process.env.CHARTER_DB_ADAPTER; beforeEach(() => { process.env.CHARTER_DB_ADAPTER = "json"; }); afterEach(() => { if (previousAdapter === undefined) delete process.env.CHARTER_DB_ADAPTER; else process.env.CHARTER_DB_ADAPTER = previousAdapter; }); test("serves a terminal-first status page instead of an editor UI", async () => { const root = await makeProject(); const server = createCharterServer({ projectRoot: root }); const baseUrl = await listen(server); try { const response = await fetch(baseUrl); expect(response.ok).toBe(true); const html = await response.text(); expect(html).toContain("Edit files. Watch events."); expect(html).toContain("npm run watch"); expect(html).not.toContain("newDocumentButton"); expect(html).not.toContain("Project machinery"); expect(html).not.toContain("