56 lines
2.2 KiB
Go
56 lines
2.2 KiB
Go
package backup
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// R-1178 (data-loss hunt 2026-10-20, R5): the second drive's „Teljes visszaállítás" merged files, THEN the unit half
|
|
// refused (a cut-off database copy), left the app STOPPED, and said „az alkalmazás és az adatai érintetlenek". Now the
|
|
// unit's refusals are asked before the file half: nothing moves, the app is never stopped, and the sentence is true.
|
|
// RED-PROOF: without the CheckOnly pre-check in RestoreTier2Whole this fails on „files changed before the refusal"
|
|
// (1 restored + 1 replaced, the measured R5 outcome — audits/dataloss-hunt-2026-10-20/C3-R5-whole-restore/).
|
|
func TestR1178_AUnitRefusalComesBeforeAnyFileMoves(t *testing.T) {
|
|
cut := strings.Replace(pgDump(1), "PostgreSQL database dump complete", "", 1) // a cut-off copy (R-640)
|
|
f := r102Tier2Fixture(t, []string{"app_data.tar"}, cut)
|
|
f.m.stackProvider = fileAppProvider{f.fake}
|
|
mid := time.Date(2026, 9, 22, 10, 0, 0, 0, time.UTC)
|
|
mirrorLeg := filepath.Join(f.destBase, "hdd", "appdata", "app")
|
|
liveLeg := filepath.Join(f.liveDrive, "appdata", "app")
|
|
writeAt(t, filepath.Join(mirrorLeg, "p1.jpg"), "P1", mid)
|
|
writeAt(t, filepath.Join(mirrorLeg, "p2.jpg"), "P2 mirror NEWER", mid.Add(72*time.Hour))
|
|
writeAt(t, filepath.Join(liveLeg, "p2.jpg"), "P2 live older", mid.Add(48*time.Hour))
|
|
before := fingerprint(t, liveLeg)
|
|
|
|
res, err := f.m.RestoreTier2Whole("app")
|
|
if err == nil {
|
|
t.Fatal("a cut-off database copy must refuse the whole restore")
|
|
}
|
|
if res.Files.Restored+res.Files.Replaced != 0 {
|
|
t.Fatalf("files changed before the refusal: %+v", res.Files)
|
|
}
|
|
if after := fingerprint(t, liveLeg); !sameFingerprint(before, after) {
|
|
t.Fatalf("the live files changed: before %v after %v", before, after)
|
|
}
|
|
if f.fake.stopped {
|
|
t.Fatalf("the app was stopped by a restore that refused — calls %v", f.fake.calls)
|
|
}
|
|
if !strings.Contains(err.Error(), "rintetlenek") { // „érintetlenek" — now true
|
|
t.Fatalf("the refusal must keep its „untouched\" sentence (true now), got %v", err)
|
|
}
|
|
}
|
|
|
|
func sameFingerprint(a, b map[string]string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for k, v := range a {
|
|
if b[k] != v {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|