import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import path from "node:path"; import { test } from "node:test"; import { fileURLToPath } from "node:url"; import { OVERRIDE_LABEL, classifyChangedFiles, mayAccompanyTerraform, } from "./check-terraform-isolation.mjs"; const SCRIPT = path.join( path.dirname(fileURLToPath(import.meta.url)), "check-terraform-isolation.mjs", ); function runGate(files, env = {}) { return spawnSync(process.execPath, [SCRIPT, "--stdin"], { input: `${files.join("\n")}\n`, encoding: "utf8", env: { ...process.env, TERRAFORM_ISOLATION_OVERRIDE: "", ...env }, }); } test("terraform tree, docs, and terraform tooling may accompany a Terraform change", () => { for (const file of [ "terraform/live/dev/main.tf", "terraform/live/modules/environment-owned/main.tf", "terraform/README.md", "README.md", "docs/adr/0003-terraform.md", "scripts/check-terraform-import-plan.py", "scripts/terraform_import_plan_resources.py", "scripts/test-terraform-import-plan-check.py", "scripts/terraform-validate.mjs", "scripts/check-terraform-isolation.mjs", ]) { assert.equal(mayAccompanyTerraform(file), true, file); } }); test("application, workflow, CDK, and dependency files count as application changes", () => { for (const file of [ "src/App.tsx", "public/favicon.ico", "index.html", "package.json", "package-lock.json", ".env.production", "vite.config.ts", ".github/workflows/deploy.yml", "infra/cdk/lib/frontend-stack.ts", "scripts/deploy-web.sh", "scripts/governance-check.mjs", "e2e/login.spec.ts", ]) { assert.equal(mayAccompanyTerraform(file), false, file); } }); test("terraform-only and application-only changes are not mixed", () => { assert.equal( classifyChangedFiles(["terraform/live/dev/main.tf", "terraform/README.md"]).mixed, false, ); assert.equal( classifyChangedFiles(["src/App.tsx", ".github/workflows/deploy.yml", "README.md"]).mixed, false, ); assert.equal(classifyChangedFiles([]).mixed, false); }); test("terraform plus application is mixed and lists the offending files", () => { const result = classifyChangedFiles([ "terraform/live/dev/main.tf", "src/App.tsx", "README.md", " ", "src/App.tsx", ]); assert.equal(result.mixed, true); assert.deepEqual(result.terraform, ["terraform/live/dev/main.tf"]); assert.deepEqual(result.application, ["src/App.tsx"]); }); test("CLI exits 1 on a mixed change and 0 when isolated", () => { const mixed = runGate(["terraform/live/dev/main.tf", "src/App.tsx"]); assert.equal(mixed.status, 1, mixed.stdout + mixed.stderr); assert.match(mixed.stdout, /FAIL/); assert.match(mixed.stdout, /src\/App\.tsx/); const isolated = runGate(["terraform/live/dev/main.tf", "terraform/README.md"]); assert.equal(isolated.status, 0, isolated.stdout + isolated.stderr); assert.match(isolated.stdout, /PASS/); }); test("CLI override downgrades a mixed change to a warning that names the label", () => { const result = runGate(["terraform/live/dev/main.tf", "src/App.tsx"], { TERRAFORM_ISOLATION_OVERRIDE: "true", }); assert.equal(result.status, 0, result.stdout + result.stderr); assert.match(result.stdout, /WARNING/); assert.match(result.stdout, new RegExp(OVERRIDE_LABEL)); const notTrue = runGate(["terraform/live/dev/main.tf", "src/App.tsx"], { TERRAFORM_ISOLATION_OVERRIDE: "yes", }); assert.equal(notTrue.status, 1); }); test("CLI refuses to run without a base ref or --stdin", () => { const result = spawnSync(process.execPath, [SCRIPT], { encoding: "utf8" }); assert.notEqual(result.status, 0); });