package stacks import ( "os" "path/filepath" "testing" ) // D47 (operator ruling 2026-10-25; R-1269) — the app's record names the install its data belongs to. Through the // PRODUCTION writers: the restore write (PersistUnitRedeployConfigGate), a later load-then-save, EnsureInstallID. // COMPANION RED-PROOF: drop the `case gate.InstallID != ""` branch in PersistUnitRedeployConfigGate → // TestD47_ARestoreTakesTheUnitsInstall fails (the app keeps the replaced install's id, and its next backup would // set aside the very unit it was restored from). // A restore from a unit that records an install: the app now holds that unit's data, so it IS that install. func TestD47_ARestoreTakesTheUnitsInstall(t *testing.T) { m := gateManager(t, gatedYml) writeAppYaml(t, gappDir(m), "deployed: true\ninstall_id: bbbb000000000002\nenv:\n SUBDOMAIN: gapp\n") must(t, m.PersistUnitRedeployConfigGate("gapp", gateEnv, GateAtBackup{InstallID: "aaaa000000000001"})) if got := LoadAppConfig(gappDir(m)); got == nil || got.InstallID != "aaaa000000000001" { t.Fatalf("the restored app must be the unit's install, got %+v", got) } } // A unit that records no install (before D47): the app keeps its own identity — a restore is not a new install. func TestD47_ARestoreOfAnUnrecordedUnitKeepsTheAppsInstall(t *testing.T) { m := gateManager(t, gatedYml) writeAppYaml(t, gappDir(m), "deployed: true\ninstall_id: bbbb000000000002\nenv:\n SUBDOMAIN: gapp\n") must(t, m.PersistUnitRedeployConfigGate("gapp", gateEnv, GateAtBackup{})) if got := LoadAppConfig(gappDir(m)); got == nil || got.InstallID != "bbbb000000000002" { t.Fatalf("an unrecorded unit's restore keeps the app's own install, got %+v", got) } } // A removed app restored from an unrecorded unit gets an identity (never empty after a restore). func TestD47_ARemovedAppRestoredGetsAnInstall(t *testing.T) { m := gateManager(t, gatedYml) must(t, m.PersistUnitRedeployConfigGate("gapp", gateEnv, GateAtBackup{})) if got := LoadAppConfig(gappDir(m)); got == nil || len(got.InstallID) != 16 { t.Fatalf("a restored app must have an install id, got %+v", got) } } // An app installed before D47 gets one identity once; a second call returns the same one. func TestD47_EnsureInstallIDMintsOnce(t *testing.T) { m := gateManager(t, gatedYml) writeAppYaml(t, gappDir(m), "deployed: true\nenv:\n SUBDOMAIN: gapp\ndesired_state: running\n") m.mu.Lock() m.stacks["gapp"].Deployed = true m.mu.Unlock() a, err := m.EnsureInstallID("gapp") must(t, err) b, err := m.EnsureInstallID("gapp") must(t, err) if len(a) != 16 || a != b { t.Fatalf("one identity, minted once: %q then %q", a, b) } if got := LoadAppConfig(gappDir(m)); got.DesiredState != DesiredStateRunning { t.Fatalf("minting the identity must change nothing else: %+v", got) } } func writeAppYaml(t *testing.T, dir, body string) { t.Helper() if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "app.yaml"), []byte(body), 0o600); err != nil { t.Fatal(err) } }