120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
import { describe, it, expect, afterAll } from "vitest";
|
|
import { rm, access } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { startServer, type ServerConfig } from "../src/server.js";
|
|
import { ensureJournalFixture, ensureRefinedFixture, ensureCcFixture } from "./helpers.js";
|
|
|
|
const OUT = "/tmp/test-server-out";
|
|
const PORT = 17801;
|
|
const BASE = `http://127.0.0.1:${PORT}`;
|
|
|
|
const cfg: ServerConfig = {
|
|
journal: ensureJournalFixture(),
|
|
refinedDir: ensureRefinedFixture(),
|
|
ccDir: ensureCcFixture(),
|
|
outDir: OUT,
|
|
mode: "dev",
|
|
port: PORT,
|
|
host: "127.0.0.1",
|
|
};
|
|
|
|
let serverHandle: Awaited<ReturnType<typeof startServer>> | null = null;
|
|
|
|
async function boot(): Promise<void> {
|
|
if (serverHandle) return;
|
|
serverHandle = await startServer(cfg);
|
|
}
|
|
|
|
afterAll(async () => {
|
|
if (serverHandle) { serverHandle.server.close(); serverHandle = null; }
|
|
await rm(OUT, { recursive: true, force: true });
|
|
});
|
|
|
|
async function jget(p: string): Promise<any> {
|
|
const r = await fetch(BASE + p);
|
|
expect(r.status).toBe(200);
|
|
return r.json();
|
|
}
|
|
async function jpost(p: string, body: unknown): Promise<any> {
|
|
const r = await fetch(BASE + p, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
return r.json();
|
|
}
|
|
|
|
describe("dashboard server", () => {
|
|
it("serves the dashboard page at /", async () => {
|
|
await boot();
|
|
const r = await fetch(BASE + "/");
|
|
expect(r.status).toBe(200);
|
|
const html = await r.text();
|
|
expect(html).toContain("Foundry ⇄ Obsidian merge");
|
|
});
|
|
|
|
it("GET /api/status reports dev mode and the configured dirs", async () => {
|
|
await boot();
|
|
const s = await jget("/api/status");
|
|
expect(s.mode).toBe("dev");
|
|
expect(s.refinedDir).toBe(cfg.refinedDir);
|
|
expect(s.ccDir).toBe(cfg.ccDir);
|
|
expect(s.outDir).toBe(OUT);
|
|
});
|
|
|
|
it("GET /api/index returns the expected counts", async () => {
|
|
await boot();
|
|
const idx = await jget("/api/index");
|
|
expect(idx.counts.matched).toBe(107);
|
|
expect(idx.counts.ccOnly).toBe(52);
|
|
expect(idx.counts.refinedOnly).toBe(0);
|
|
});
|
|
|
|
it("GET /api/file returns refined, cc, and previews for a known note", async () => {
|
|
await boot();
|
|
const f = await jget("/api/file?name=" + encodeURIComponent("Fenris of House Quche"));
|
|
expect(f.row.name).toBe("Fenris of House Quche");
|
|
expect(f.refined).toBeTruthy();
|
|
expect(f.cc).toBeTruthy();
|
|
expect(f.seedPreview).toContain("foundry:");
|
|
expect(f.syncPreview).toContain("cc_id");
|
|
});
|
|
|
|
it("dry-run seedAll writes nothing and returns previews only", async () => {
|
|
await boot();
|
|
const out = await jpost("/api/action", { op: "seedAll", dryRun: true });
|
|
expect(out.written.length).toBe(0);
|
|
expect(out.preview.length).toBe(107);
|
|
// Sandbox out dir must not be created by a dry-run.
|
|
await expect(access(OUT)).rejects.toThrow();
|
|
});
|
|
|
|
it("dev seedAll writes only under --out, never to the source refined dir", async () => {
|
|
await boot();
|
|
const out = await jpost("/api/action", { op: "seedAll", dryRun: false });
|
|
expect(out.written.length).toBe(107);
|
|
expect(out.skipped.length).toBe(0);
|
|
for (const w of out.written) {
|
|
expect(w.path.startsWith(OUT)).toBe(true);
|
|
expect(w.path).not.toContain(cfg.refinedDir);
|
|
}
|
|
});
|
|
|
|
it("dev syncAll baselines both sides: writes refined + cc files under --out", async () => {
|
|
await boot();
|
|
const out = await jpost("/api/action", { op: "syncAll", dryRun: false });
|
|
// sync now writes BOTH the refreshed refined note and the regenerated cc per row.
|
|
expect(out.written.length).toBe(214);
|
|
const paths = out.written.map((w: { path: string }) => w.path);
|
|
// Every write lands under the sandbox out dir, never in a source dir.
|
|
for (const p of paths) {
|
|
expect(p.startsWith(OUT)).toBe(true);
|
|
expect(p).not.toContain(cfg.ccDir);
|
|
expect(p).not.toContain(cfg.refinedDir);
|
|
}
|
|
});
|
|
|
|
it("dev repullAll writes refined notes only under --out", async () => {
|
|
await boot();
|
|
const out = await jpost("/api/action", { op: "repullAll", dryRun: false });
|
|
// Cold start: no row is seeded, so re-pull still produces output for every linked row.
|
|
expect(out.written.length).toBe(107);
|
|
for (const w of out.written) expect(w.path.startsWith(OUT)).toBe(true);
|
|
});
|
|
}); |