1import { spawnSync } from "node:child_process";
2import {
3 mkdtempSync,
4 mkdirSync,
5 readdirSync,
6 rmSync,
7 writeFileSync,
8} from "node:fs";
9import { tmpdir } from "node:os";
10import { join } from "node:path";
11import { fileURLToPath } from "node:url";
12import { expect, it } from "alchemy-test";
13
14const cli = fileURLToPath(new URL("../../bin/cli.js", import.meta.url));
15
16it(
17 "state unsafe nuke requires approval and clears every stack with --yes",
18 () => {
19 const cwd = mkdtempSync(join(tmpdir(), "alchemy-state-nuke-"));
20 const state = join(cwd, ".alchemy", "state");
21 try {
22 for (const stack of ["First", "Second"]) {
23 for (const stage of ["dev", "prod"]) {
24 const directory = join(state, stack, stage);
25 mkdirSync(directory, { recursive: true });
26 writeFileSync(join(directory, "output.json"), "{}");
27 }
28 }
29 const run = (args: string[]) =>
30 spawnSync("bun", [cli, ...args], {
31 cwd,
32 env: { ...process.env, ALCHEMY_HOME: join(cwd, "home") },
33 encoding: "utf8",
34 timeout: 10000,
35 });
36 const args = ["state", "unsafe", "nuke", "--backend", "local"];
37 expect(run(args).status).toBe(1);
38 expect(readdirSync(state).sort()).toEqual(["First", "Second"]);
39 const cleared = run([...args, "--yes"]);
40 expect(cleared.status).toBe(0);
41 expect(cleared.stdout).toContain("• First\n• Second");
42 expect(cleared.stdout).toContain("Cleared First");
43 expect(cleared.stdout).toContain("Cleared Second");
44 expect(readdirSync(state)).toEqual([]);
45 const empty = run(args);
46 expect(empty.status).toBe(0);
47 expect(empty.stdout).toContain("Nothing to clear");
48 expect(
49 run(["state", "delete", "/", "--recursive", "--backend", "local"])
50 .status,
51 ).toBe(1);
52 } finally {
53 rmSync(cwd, { recursive: true, force: true });
54 }
55 },
56 { tags: ["unit", "local"] },
57);