package stacks import ( "bytes" "context" "log" "os" "path/filepath" "strings" "testing" "time" ) // R-670 (v0.272.0) — every undo logged `[ERROR] .felhom.yml backup block rejected … docker-compose.yml // unreadable`, because the undo read the previous version's `.felhom.yml` from `pre-update-meta/`, which has // no compose beside it, through the validating loader. The consequence asserted: a REAL undo of an app whose // `.felhom.yml` carries a backup block writes no such line to the process log. // // COMPANION RED-PROOF (REPORT.md): read PrevMeta with LoadMetadata again — this test fails at "the undo logged // a false backup-block ERROR". func TestR670_ProbeLoaderLogsNoFalseError(t *testing.T) { var buf bytes.Buffer prev := log.Writer() log.SetOutput(&buf) defer log.SetOutput(prev) m, dir, _, _, _ := ladderManager(t, true) withBlock := "display_name: Nextcloud\nbackup:\n userdata:\n - path: data\n class: excluded\n" mustWrite(t, filepath.Join(dir, ".felhom.yml"), withBlock) if err := os.MkdirAll(filepath.Join(dir, appliedMetaDir), 0o755); err != nil { t.Fatal(err) } mustWrite(t, filepath.Join(dir, appliedMetaDir, ".felhom.yml"), withBlock) m.undoCopier = newFakeCopier(map[string]string{undoVol: "OLD"}) m.updateHealthFn = func(context.Context, string, time.Duration) (bool, string) { return false, "B unhealthy" } m.updateUndoHealthFn = func(context.Context, string, time.Duration, *Metadata) (bool, string) { return true, "A answers" } if err := m.StartGuardedUpdate("nextcloud"); err != nil { t.Fatal(err) } if st := waitUpdateDone(t, m, "nextcloud"); st.UpdatePhase != UpdatePhaseUndone { t.Fatalf("the scenario must reach an undo; ended %q", st.UpdatePhase) } if strings.Contains(buf.String(), "docker-compose.yml unreadable") { t.Fatalf("the undo logged a false backup-block ERROR:\n%s", buf.String()) } // control: the validating loader on the same compose-less copy DOES log it (the line is real elsewhere) buf.Reset() LoadMetadata(filepath.Join(dir, preUpdateMetaDirForTest(t, dir, withBlock))) if !strings.Contains(buf.String(), "docker-compose.yml unreadable") { t.Fatal("control failed: LoadMetadata on a compose-less copy must still reject the block") } if pm := LoadProbeMetadata(filepath.Join(dir, "probe-copy")); pm.Backup != nil || pm.DataPaths != nil { t.Fatal("the probe loader must never hand out a backup block") } } // preUpdateMetaDirForTest writes a compose-less copy and returns its dir name (relative to dir). func preUpdateMetaDirForTest(t *testing.T, dir, body string) string { t.Helper() d := filepath.Join(dir, "probe-copy") if err := os.MkdirAll(d, 0o755); err != nil { t.Fatal(err) } mustWrite(t, filepath.Join(d, ".felhom.yml"), body) return "probe-copy" }