Files
felhom-controller/controller/internal/stacks/r694_restored_login_test.go
T
admin b6810f14ff
gates / gates (push) Successful in 23s
v0.275.0: a backup's data and its version travel together (R-696, 07 §6.6, D4 option A); R-695, R-691, R-694
The unit's data files are stamped with the versions that wrote them; the capture keeps the
definition the data belongs to; a restore never starts data under another version's
definition (unit restores refuse a mismatch; the off-site restore writes the snapshot's
definition); every tier's time is its data's; the conversion-copy release needs a dump on
the new engine. File-browser sync single-flight + no empty kept folder (R-695); the kept
view joins the folder's owning group, language switch resyncs (R-691); a restore-generated
login is not shown as the password (R-694). Red-proofs in
felhom.eu/documentation/audits/version-travel-2026-09-26/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-26 10:35:22 +02:00

58 lines
2.4 KiB
Go

package stacks
import (
"io"
"log"
"os"
"path/filepath"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
)
// R-694 (v0.275.0) — a restore with no guest app.yaml (a load of kept data, a removed app, a rebuilt
// guest) GENERATES the withheld admin login (D5: the unit never carries it), while the app's own login
// comes back with its data. For six of the seven catalog apps with such a field the old password is the
// one that works (measured from each entrypoint, `audits/version-travel-2026-09-26/D4/`); the page used
// to show the new value as "the first password set at install".
//
// Through the PRODUCTION write (PersistUnitRedeployConfig), not the helper alone.
//
// COMPANION RED-PROOF (REPORT.md): make restoredLoginFields return nil — the nextcloud case then records
// nothing and this fails at "restored_logins".
func TestR694_ARestoreThatGeneratesTheLoginRecordsIt(t *testing.T) {
for _, c := range []struct {
name string
app, env string
guestHad bool
wantNoted bool
}{
{"nextcloud, guest app.yaml gone", "nextcloud", "NEXTCLOUD_ADMIN_PASSWORD", false, true},
{"nextcloud, guest kept the household's value", "nextcloud", "NEXTCLOUD_ADMIN_PASSWORD", true, false},
{"code-server applies the env at every start", "code-server", "PASSWORD", false, false},
} {
t.Run(c.name, func(t *testing.T) {
dir := t.TempDir()
cfg := &config.Config{}
cfg.Paths.StacksDir = filepath.Join(dir, "stacks")
cfg.Stacks.ComposeCommand = "docker compose"
app := filepath.Join(cfg.Paths.StacksDir, c.app)
must(t, os.MkdirAll(app, 0o755))
must(t, os.WriteFile(filepath.Join(app, "docker-compose.yml"), []byte("services:\n web:\n image: x/y:1\n"), 0o644))
must(t, os.WriteFile(filepath.Join(app, ".felhom.yml"), []byte("display_name: X\ndeploy_fields:\n - env_var: "+c.env+"\n label: Admin\n type: password\n"), 0o644))
if c.guestHad {
must(t, os.WriteFile(filepath.Join(app, "app.yaml"), []byte("deployed: true\nenv:\n "+c.env+": households-own\n"), 0o600))
}
m, err := NewManager(cfg, log.New(io.Discard, "", 0))
must(t, err)
must(t, m.ScanStacks())
must(t, m.PersistUnitRedeployConfig(c.app, map[string]string{c.env: "generated-at-restore"}))
got := LoadAppConfig(app)
noted := got != nil && len(got.RestoredLogins) == 1 && got.RestoredLogins[0] == c.env
if noted != c.wantNoted {
t.Fatalf("restored_logins = %v, want noted=%v", got.RestoredLogins, c.wantNoted)
}
})
}
}