import { describe, expect, test } from 'bun:test'; import { planAfterImportVerdict, validateTerraformPlanSafety } from './terraform-safety'; /** * The plan terraform actually produced on celilo-mgr at 2026-09-16T00:04Z, * after `module deploy signal` imported LXC 207 and re-planned. Abridged to the * lines the parser reads, and kept verbatim otherwise: a fixture written from * memory of the format would pass while the product still destroyed the box. */ const POST_IMPORT_REPLACEMENT_PLAN = ` proxmox_lxc.signal: Refreshing state... [id=node3/lxc/207] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: -/+ destroy and then create replacement Terraform will perform the following actions: # proxmox_lxc.signal must be replaced -/+ resource "proxmox_lxc" "signal" { + ostemplate = "datacenter:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst" # forces replacement ~ rootfs { + storage = "datacenter" # forces replacement } } Plan: 1 to add, 0 to change, 1 to destroy. `; const CLEAN_PLAN = ` proxmox_lxc.signal: Refreshing state... [id=node3/lxc/207] No changes. Your infrastructure matches the configuration. `; const IN_PLACE_UPDATE_PLAN = ` # proxmox_lxc.signal will be updated in-place ~ resource "proxmox_lxc" "signal" { ~ memory = 1024 -> 2048 } Plan: 0 to add, 1 to change, 0 to destroy. `; describe('planAfterImportVerdict', () => { // THE recurrence gate for celilo#1374. This exact plan was applied without // validation and destroyed signal's container on a converged fleet. If this // test ever passes the plan through, the defect is back. test('refuses the replacement plan an import produces', () => { const verdict = planAfterImportVerdict(2, POST_IMPORT_REPLACEMENT_PLAN); expect(verdict.action).toBe('refuse'); if (verdict.action !== 'refuse') throw new Error('unreachable'); expect(verdict.error).toContain('after importing an existing resource'); }); // The outcome the recovery path is FOR: the import reconciled state with // reality and there is nothing left to apply. Adopt, don't apply. test('applies nothing when the post-import plan is clean', () => { expect(planAfterImportVerdict(0, CLEAN_PLAN)).toEqual({ action: 'done' }); }); // A resize must still go through — refusing everything would be a different // bug wearing the same fix (terraform-safety.ts rule 2, ISS-0055). test('lets an in-place update through', () => { expect(planAfterImportVerdict(2, IN_PLACE_UPDATE_PLAN)).toEqual({ action: 'apply' }); }); // An unreadable plan is not evidence of safety (D7). test('refuses when the plan itself failed', () => { const verdict = planAfterImportVerdict(1, 'Error: failed to refresh state'); expect(verdict.action).toBe('refuse'); }); }); describe('validateTerraformPlanSafety', () => { // Characterises what was already true: the validator would have caught this. // The defect was never a missing rule, it was a plan that never reached one. test('already refuses a replacement — it was simply never asked', () => { expect(validateTerraformPlanSafety(POST_IMPORT_REPLACEMENT_PLAN).safe).toBe(false); }); });