|
| 1 | +import assert from "node:assert/strict"; |
| 2 | +import { execFileSync } from "node:child_process"; |
| 3 | +import { mkdtempSync, writeFileSync } from "node:fs"; |
| 4 | +import { tmpdir } from "node:os"; |
| 5 | +import { join } from "node:path"; |
| 6 | +import { test } from "node:test"; |
| 7 | +import { main } from "../src/cli.mjs"; |
| 8 | + |
| 9 | +// cli.mjs dynamic-imports bullmq (in the `run` enqueue and `worker` paths), so the VALIDATION |
| 10 | +// paths -- which return before any enqueue -- run everywhere. That is exactly the safety surface |
| 11 | +// worth testing: nothing should reach the queue if the inputs are bad. |
| 12 | + |
| 13 | +const env = { VALKEY_URL: "redis://127.0.0.1:6399" }; |
| 14 | + |
| 15 | +test("no args prints usage and exits 0", async () => { |
| 16 | + assert.equal(await main([], env), 0); |
| 17 | +}); |
| 18 | + |
| 19 | +test("an unknown command exits 1", async () => { |
| 20 | + assert.equal(await main(["frobnicate"], env), 1); |
| 21 | +}); |
| 22 | + |
| 23 | +test("run with no folder fails", async () => { |
| 24 | + assert.equal(await main(["run"], env), 1); |
| 25 | +}); |
| 26 | + |
| 27 | +test("run with a missing folder fails before touching the queue", async () => { |
| 28 | + assert.equal(await main(["run", "/no/such/folder", "--task", "x"], env), 1); |
| 29 | +}); |
| 30 | + |
| 31 | +test("run with no --task fails", async () => { |
| 32 | + const dir = mkdtempSync(join(tmpdir(), "cli-")); |
| 33 | + assert.equal(await main(["run", dir, "--flow", "tidy"], env), 1); |
| 34 | +}); |
| 35 | + |
| 36 | +function gitRepo({ dirty }) { |
| 37 | + const dir = mkdtempSync(join(tmpdir(), "cli-git-")); |
| 38 | + const g = (args) => |
| 39 | + execFileSync("git", ["-C", dir, ...args], { |
| 40 | + env: { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@t", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@t" }, |
| 41 | + }); |
| 42 | + g(["init", "-q"]); |
| 43 | + g(["config", "core.autocrlf", "false"]); |
| 44 | + writeFileSync(join(dir, "f.txt"), "one\n"); |
| 45 | + g(["add", "-A"]); |
| 46 | + g(["commit", "-qm", "init"]); |
| 47 | + if (dirty) writeFileSync(join(dir, "f.txt"), "one\ntwo\n"); // uncommitted change |
| 48 | + return dir; |
| 49 | +} |
| 50 | + |
| 51 | +test("run refuses a dirty git working tree (edits are in place, no undo)", async () => { |
| 52 | + const dir = gitRepo({ dirty: true }); |
| 53 | + assert.equal(await main(["run", dir, "--task", "x"], env), 1); |
| 54 | +}); |
| 55 | + |
| 56 | +test("--force overrides the dirty-tree refusal (reaches the enqueue, which needs Valkey)", async () => { |
| 57 | + // Without Valkey the enqueue will throw; we only assert the guard did NOT stop it (it got past |
| 58 | + // validation). If a Valkey is present it returns 0. Either way it must not return 1-from-guard. |
| 59 | + const dir = gitRepo({ dirty: true }); |
| 60 | + let reachedEnqueue = false; |
| 61 | + try { |
| 62 | + const code = await main(["run", dir, "--task", "x", "--force"], env); |
| 63 | + reachedEnqueue = code === 0; // Valkey present |
| 64 | + } catch { |
| 65 | + reachedEnqueue = true; // tried to connect => passed the guard |
| 66 | + } |
| 67 | + assert.ok(reachedEnqueue, "--force must let a dirty tree through to the enqueue"); |
| 68 | +}); |
0 commit comments