import { execFile } from "node:child_process"; import { mkdtemp, readFile, realpath, rm, stat } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; /** Runs the published command as a real process, in a directory of its own. */ const run = promisify(execFile); const CLI = path.join(import.meta.dirname, "onvif-dashboard.mjs"); let dir: string; beforeEach(async () => { // realpath because macOS temp folders are symlinks, and the CLI reports resolved paths. dir = await realpath(await mkdtemp(path.join(os.tmpdir(), "cli-"))); }); afterEach(() => rm(dir, { recursive: true, force: true })); const cli = (args: string[]) => run(process.execPath, ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", CLI, ...args], { cwd: dir, env: { ...process.env, XDG_CONFIG_HOME: path.join(dir, "config"), CAMERAS_BIN_DIR: path.join(dir, "bin"), CAMERAS_DATA_DIR: undefined, }, }).catch((err: Error & { code?: number; stdout?: string; stderr?: string }) => ({ code: err.code, stdout: err.stdout ?? "", stderr: err.stderr ?? "", })); describe("onvif-dashboard", () => { it("explains itself, and says so when the command is unknown", async () => { expect((await cli(["help"])).stdout).toContain("onvif-dashboard setup"); const bad = (await cli(["wat"])) as { code?: number; stderr: string }; expect(bad.code).toBe(2); expect(bad.stderr).toContain("Unknown command: wat"); }); it("sets a machine up without a terminal: default folder, a key, and what to do next", async () => { const { stdout } = await cli(["setup"]); const data = path.join(dir, ".data"); expect(stdout).toContain("No terminal to ask"); expect(stdout).toContain(`Data folder: ${data}`); expect(stdout).toContain("install-video"); expect(stdout).toContain("No admin login yet"); expect(stdout).toContain("onvif-dashboard start"); const secrets = path.join(data, "secrets.env"); expect((await stat(secrets)).mode & 0o777).toBe(0o600); expect(await readFile(secrets, "utf8")).toMatch(/CAMERA_CREDENTIALS_KEY=[0-9a-f]{64}/); expect(JSON.parse(await readFile(path.join(dir, "config/onvif-dashboard/config.json"), "utf8"))).toEqual({ dataDir: data, }); }); it("offers the admin command the messages point people at", async () => { const { stdout } = await cli(["admin", "--help"]); expect(stdout).toContain("--force"); }); it("running it again changes nothing", async () => { await cli(["setup"]); const secrets = path.join(dir, ".data", "secrets.env"); const before = await readFile(secrets, "utf8"); const { stdout } = await cli(["setup"]); expect(await readFile(secrets, "utf8")).toBe(before); expect(stdout).not.toContain("Made a key"); }); });