Files
felhom-controller/controller/internal/backup/kept_load_test.go
T
admin 43e99d160c
gates / gates (push) Successful in 26s
kept data: the choice at reinstall, the list, the read-only view, the load (09 decision 36); R-690 fixed
An install over an app's kept drive folder (appdata/<app> non-empty) asks the household:
"use my kept data" (a load from the newest copy of THIS drive's install, own unit or
second-drive mirror, then the template's after_load) or "start fresh" (the folder is
renamed into <drive>/kept/<app>/<date>/ with the removed app's unit; nothing deleted).
The install API answers 409 kept_data_choice until one is chosen; DeployStack refuses
too. New page Megorzott adatok / Kept data (/kept-data): Load / Look / Delete (typed
confirmation, the only deletion of kept data). FileBrowser gets a read-only source.
The drive-full warning names the kept folders. <drive>/kept is protected and outside
every backup leg.

R-690: the removed-app restore (R-487) never found a unit on a DATA drive — it asked
GetStackComposePath (true for every catalog app) and restored nextcloud with no env.
Now isStackDeployed; pinned with a production-shaped provider.

Red-proofs: audits/night-2026-09-26/E/redproofs/.

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

66 lines
2.3 KiB
Go

package backup
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
)
func writeKeptUnit(t *testing.T, unitDir, hdd string, withData bool, at time.Time) {
t.Helper()
if err := os.MkdirAll(filepath.Join(unitDir, "compose"), 0o755); err != nil {
t.Fatal(err)
}
man := RecoveryManifest{SchemaVersion: 2, AppName: "nextcloud"}
if withData {
man.DBDumps = []string{"nextcloud-mariadb.sql"}
}
b, _ := json.Marshal(man)
if err := os.WriteFile(UnitManifestFile(unitDir), b, 0o644); err != nil {
t.Fatal(err)
}
_ = os.Chtimes(UnitManifestFile(unitDir), at, at)
if hdd != "" {
if err := os.WriteFile(filepath.Join(unitDir, "compose", "app.yaml"), []byte("env:\n HDD_PATH: "+hdd+"\n"), 0o600); err != nil {
t.Fatal(err)
}
}
}
// `09` §3 decision 36 — „use my kept data" loads only a copy that (1) holds the app's data and (2) was
// taken of THIS drive's install; the newest such copy on any local tier wins.
// COMPANION RED-PROOF: drop the HDD_PATH comparison in KeptCopyAt → a unit taken of ANOTHER drive is
// offered and the first assertion fails.
func TestKept_DBCopyIsTheNewestUsableForThisDrive(t *testing.T) {
m, sett, drive := r690Manager(t)
_ = sett
unit := RecoveryUnitPath(m.namespaceRoot(drive), "nextcloud")
writeKeptUnit(t, unit, "/mnt/felhom-drives/other", true, time.Now().Add(-time.Hour))
if c, ok := m.KeptDBCopy("nextcloud", drive); ok {
t.Fatalf("a unit taken of another drive was offered: %+v", c)
}
writeKeptUnit(t, unit, drive, false, time.Now().Add(-time.Hour))
if c, ok := m.KeptDBCopy("nextcloud", drive); ok {
t.Fatalf("a unit holding no data was offered: %+v", c)
}
writeKeptUnit(t, unit, drive, true, time.Now().Add(-time.Hour))
c, ok := m.KeptDBCopy("nextcloud", drive)
if !ok || c.UnitDir != unit || c.Tier != 1 {
t.Fatalf("own unit not offered: %+v ok=%v", c, ok)
}
// An INSTALLED app's unit is its live backup, never a kept-data offer.
if c, ok := m.KeptDBCopy("still-here", drive); ok {
t.Fatalf("an installed app's unit was offered: %+v", c)
}
// The start-fresh hook names the unit only on the same drive.
if got := m.RemovedUnitOnDrive("nextcloud", drive); got != unit {
t.Fatalf("RemovedUnitOnDrive = %q, want %q", got, unit)
}
if got := m.RemovedUnitOnDrive("nextcloud", "/elsewhere"); got != "" {
t.Fatalf("RemovedUnitOnDrive named a unit on another drive: %q", got)
}
}