v0.270.0: no update for a current app (R-679); an interrupted install is reported (R-681); a restore brings back the pinned version's health check (R-669); R-674
gates / gates (push) Successful in 24s
gates / gates (push) Successful in 24s
Five red-proofs. MinAgent 0.131.0 unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package stacks
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-681 (v0.270.0): an install a controller restart cut off is FINISHED through the failure path and
|
||||
// REPORTED — never silent. The consequences asserted: `compose down` ran (never real Docker — a fake), the
|
||||
// household is told (the deploy-done hook fires as a failure with the reason), the stale pin records are
|
||||
// gone, and the page's flag survives a rescan.
|
||||
//
|
||||
// COMPANION RED-PROOF (REPORT.md): make RecoverInterruptedInstalls return without acting (v0.269.1: no
|
||||
// install marker, nothing looked) → "an interrupted install was not reported".
|
||||
func TestR681_InterruptedInstallIsFinishedAndReported(t *testing.T) {
|
||||
m, dir := newPinManager(t, pinTplOld, pinTplOld, "deployed: false\nenv: {}\n")
|
||||
m.stacks["nextcloud"].Deployed = false
|
||||
mustWrite(t, filepath.Join(dir, installPendingFile), "2026-09-24T11:48:42Z\n")
|
||||
mustWrite(t, AppliedComposePath(dir), pinTplOld) // stale, from an earlier install
|
||||
var downs []string
|
||||
m.composeDownFn = func(d string) error { downs = append(downs, d); return nil }
|
||||
type call struct {
|
||||
name string
|
||||
ok bool
|
||||
detail string
|
||||
}
|
||||
var hooks []call
|
||||
m.SetDeployDoneHook(func(n string, ok bool, d string) { hooks = append(hooks, call{n, ok, d}) })
|
||||
|
||||
got := m.RecoverInterruptedInstalls()
|
||||
if len(got) != 1 || len(hooks) != 1 || hooks[0].ok || hooks[0].detail != installInterruptedReason {
|
||||
t.Fatalf("an interrupted install was not reported: resolved=%v hooks=%+v", got, hooks)
|
||||
}
|
||||
if len(downs) != 1 || downs[0] != dir {
|
||||
t.Fatalf("what the interrupted install started was not removed: downs=%v", downs)
|
||||
}
|
||||
if _, err := os.Stat(AppliedComposePath(dir)); err == nil {
|
||||
t.Fatal("the stale pin record of a version that never ran is still there")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, installPendingFile)); err == nil {
|
||||
t.Fatal("the pending marker survived — the next start would report it again")
|
||||
}
|
||||
if err := m.ScanStacks(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st, _ := m.GetStack("nextcloud"); !st.InstallInterrupted || st.Deployed {
|
||||
t.Fatalf("after a rescan: interrupted=%v deployed=%v — the page must still say it", st.InstallInterrupted, st.Deployed)
|
||||
}
|
||||
// A second start finds nothing to report.
|
||||
if again := m.RecoverInterruptedInstalls(); len(again) != 0 || len(hooks) != 1 {
|
||||
t.Fatalf("reported twice: %v", again)
|
||||
}
|
||||
}
|
||||
|
||||
// An install that FINISHED before the restart (its record says deployed) is left alone: no down, no event.
|
||||
func TestR681_FinishedInstallIsLeftAlone(t *testing.T) {
|
||||
m, dir := newPinManager(t, pinTplOld, pinTplOld, "deployed: true\nenv: {}\n")
|
||||
mustWrite(t, filepath.Join(dir, installPendingFile), "x\n")
|
||||
downs, hooks := 0, 0
|
||||
m.composeDownFn = func(string) error { downs++; return nil }
|
||||
m.SetDeployDoneHook(func(string, bool, string) { hooks++ })
|
||||
if got := m.RecoverInterruptedInstalls(); len(got) != 0 || downs != 0 || hooks != 0 {
|
||||
t.Fatalf("a finished install was treated as interrupted: resolved=%v downs=%d hooks=%d", got, downs, hooks)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, installPendingFile)); err == nil {
|
||||
t.Fatal("the marker of a finished install was not cleared")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user