Files
felhom-controller/controller/internal/stacks/r670_probe_meta_test.go
T
2026-09-25 11:19:27 +02:00

67 lines
2.7 KiB
Go

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"
}