import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { fetchInstallationStatus, getInstallationToken, resolveInstallationView } from "../installation-status"; describe("fetchInstallationStatus", () => { const originalFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = originalFetch; }); it("reports installed with installedAt when the tenant's /api/health says installed: true", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ status: "healthy", database: "reachable", installed: true, installedAt: "2026-01-15T10:00:00.000Z" }), }) as unknown as typeof fetch; const result = await fetchInstallationStatus("acme"); expect(result).toEqual({ status: "installed", installedAt: "2026-01-15T10:00:00.000Z" }); }); it("reports not_installed (with no installedAt) when installed: false", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ status: "healthy", database: "reachable", installed: false, installedAt: null }), }) as unknown as typeof fetch; const result = await fetchInstallationStatus("acme"); expect(result).toEqual({ status: "not_installed", installedAt: null }); }); it("reports unknown — never not_installed — when the fetch throws (container down/unreachable)", async () => { globalThis.fetch = vi.fn().mockRejectedValue(new Error("ECONNREFUSED")); const result = await fetchInstallationStatus("acme"); expect(result).toEqual({ status: "unknown", installedAt: null }); }); it("reports unknown when the response isn't ok (e.g. 503 unhealthy)", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: false, json: async () => ({}) }) as unknown as typeof fetch; const result = await fetchInstallationStatus("acme"); expect(result).toEqual({ status: "unknown", installedAt: null }); }); it("reports unknown for a malformed body (no boolean `installed` field)", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ unexpected: true }) }) as unknown as typeof fetch; const result = await fetchInstallationStatus("acme"); expect(result).toEqual({ status: "unknown", installedAt: null }); }); it("targets the internal docker network hostname for the given slug, not the public domain", async () => { const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ installed: false }) }); globalThis.fetch = fetchMock as unknown as typeof fetch; await fetchInstallationStatus("acme-barbers"); expect(fetchMock).toHaveBeenCalledWith("http://next-chair-acme-barbers:3000/api/health", expect.any(Object)); }); }); describe("getInstallationToken / resolveInstallationView", () => { let stacksDir: string; const originalStacksDir = process.env.STACKS_DIR; const originalFetch = globalThis.fetch; beforeEach(async () => { stacksDir = await mkdtemp(path.join(tmpdir(), "nc-manager-test-")); process.env.STACKS_DIR = stacksDir; }); afterEach(async () => { globalThis.fetch = originalFetch; if (originalStacksDir === undefined) delete process.env.STACKS_DIR; else process.env.STACKS_DIR = originalStacksDir; await rm(stacksDir, { recursive: true, force: true }); }); async function writeStackEnv(slug: string, envContent: string) { const dir = path.join(stacksDir, slug); await mkdir(dir, { recursive: true }); await writeFile(path.join(dir, ".env"), envContent, "utf8"); } it("reads the existing token straight from that business's own .env — an already-generated token is preserved, not regenerated", async () => { await writeStackEnv("acme", 'BUSINESS_NAME="Acme"\nINSTALLATION_TOKEN="existing-token-value"\n'); const token = await getInstallationToken("acme"); expect(token).toBe("existing-token-value"); }); it("returns null (not an error) for a business with no token in its .env", async () => { await writeStackEnv("legacy-biz", 'BUSINESS_NAME="Legacy"\n'); const token = await getInstallationToken("legacy-biz"); expect(token).toBeNull(); }); it("returns null for a business directory that doesn't exist", async () => { const token = await getInstallationToken("does-not-exist"); expect(token).toBeNull(); }); it("tenant isolation: never returns another business's token", async () => { await writeStackEnv("tenant-a", 'INSTALLATION_TOKEN="token-for-a"\n'); await writeStackEnv("tenant-b", 'INSTALLATION_TOKEN="token-for-b"\n'); expect(await getInstallationToken("tenant-a")).toBe("token-for-a"); expect(await getInstallationToken("tenant-b")).toBe("token-for-b"); }); it("resolveInstallationView never includes a token once installed — the token is not even read from disk in that branch", async () => { await writeStackEnv("acme", 'INSTALLATION_TOKEN="should-never-appear"\n'); globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ installed: true, installedAt: "2026-01-15T10:00:00.000Z" }), }) as unknown as typeof fetch; const view = await resolveInstallationView("acme", "acme.example.com"); expect(view).toEqual({ kind: "installed", installedAt: "2026-01-15T10:00:00.000Z" }); expect(JSON.stringify(view)).not.toContain("should-never-appear"); }); it("resolveInstallationView surfaces the token pre-install, with the public install URL", async () => { await writeStackEnv("acme", 'INSTALLATION_TOKEN="real-token"\n'); globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ installed: false }) }) as unknown as typeof fetch; const view = await resolveInstallationView("acme", "acme.example.com"); expect(view).toEqual({ kind: "not_installed", token: "real-token", installUrl: "https://acme.example.com/install" }); }); it("resolveInstallationView still surfaces the token (not hidden) when status can't be confirmed", async () => { await writeStackEnv("acme", 'INSTALLATION_TOKEN="real-token"\n'); globalThis.fetch = vi.fn().mockRejectedValue(new Error("ECONNREFUSED")); const view = await resolveInstallationView("acme", "acme.example.com"); expect(view.kind).toBe("unknown"); expect(view).toMatchObject({ token: "real-token" }); }); });