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

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:
2026-09-24 16:49:43 +02:00
parent 7c6140d95b
commit 504eae018b
23 changed files with 1121 additions and 16 deletions
@@ -0,0 +1,57 @@
package backup
import (
"io"
"log"
"os"
"path/filepath"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)
// R-669 (v0.270.0): the recovery unit captures the PINNED version's .felhom.yml (the applied record),
// not the stack dir's — which the sync may already have replaced with a newer, even failing, step's file.
// MEASURED 2026-09-24 (A2): the unit held port 8999 (the failing step's probe), a restore wrote it back,
// and the next undo judged the correct version with it and held the app.
//
// COMPANION RED-PROOF (REPORT.md): capture the stack dir's .felhom.yml again → "the unit carries the
// failing step's probe".
func TestR669_UnitCapturesThePinnedVersionsMeta(t *testing.T) {
tmp := t.TempDir()
stackDir := filepath.Join(tmp, "stack")
drive := filepath.Join(tmp, "drive")
mustWrite(t, filepath.Join(stackDir, "docker-compose.yml"), "services:\n app:\n image: example/app:1.2.3\n")
mustWrite(t, filepath.Join(stackDir, ".felhom.yml"), "display_name: Example\nhealthcheck:\n checks:\n - type: api\n port: 8999\n") // flowed by the sync
mustWrite(t, stacks.AppliedMetaFile(stackDir), "display_name: Example\nhealthcheck:\n checks:\n - type: api\n port: 80\n") // the pinned version's
mustWrite(t, filepath.Join(stackDir, "app.yaml"), "deployed: true\nenv: {}\n")
m := &Manager{
logger: log.New(io.Discard, "", 0),
systemDataPath: filepath.Join(tmp, "system"),
stackProvider: &fakeRecoveryProvider{info: RecoveryInfo{StackDir: stackDir, DisplayName: "Example", ImagePins: []string{"example/app:1.2.3"}}, hdd: drive},
version: "vtest",
}
if err := m.CaptureRecoveryUnit("example"); err != nil {
t.Fatalf("capture: %v", err)
}
b, err := os.ReadFile(filepath.Join(RecoveryUnitComposePath(drive, "example"), ".felhom.yml"))
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(b), "8999") || !strings.Contains(string(b), "port: 80") {
t.Fatalf("the unit carries the failing step's probe, not the pinned version's:\n%s", b)
}
// Control: no applied record (an app pinned before v0.263.2) → the stack dir's file, as before.
if err := os.RemoveAll(filepath.Dir(stacks.AppliedMetaFile(stackDir))); err != nil {
t.Fatal(err)
}
mustWrite(t, filepath.Join(stackDir, ".felhom.yml"), "display_name: Example changed\n") // force a recapture
if err := m.CaptureRecoveryUnit("example"); err != nil {
t.Fatal(err)
}
b, _ = os.ReadFile(filepath.Join(RecoveryUnitComposePath(drive, "example"), ".felhom.yml"))
if !strings.Contains(string(b), "Example changed") {
t.Fatalf("without an applied record the stack dir's file must be captured: %s", b)
}
}