72 lines
3.0 KiB
Go
72 lines
3.0 KiB
Go
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)
|
|
}
|
|
}
|