package backup import ( "os" "path/filepath" "testing" "time" "gitea.dooplex.hu/admin/felhom-agent/internal/hub" ) // R-894: the on-disk newest success per tier survives a restart (a new state from the same file). func TestBackupSuccessState_SurvivesRestart(t *testing.T) { path := filepath.Join(t.TempDir(), "backup-success-state.json") s := NewBackupSuccessState(path) at := time.Date(2026, 10, 1, 20, 15, 0, 0, time.UTC) if err := s.RecordBackupSuccess("felhom-pbs", hub.Backup{VMID: 9201, Success: true, StartedAt: at.Format(time.RFC3339)}); err != nil { t.Fatal(err) } got, ok := NewBackupSuccessState(path).LastKnownSuccess("felhom-pbs", 9201) if !ok || !got.Equal(at) { t.Fatalf("after a restart the saved copy must read back; got %v ok=%v", got, ok) } if _, ok := NewBackupSuccessState(path).LastKnownSuccess("local", 9201); ok { t.Fatal("another tier must not borrow this tier's copy") } } // Only a NEWER success replaces the saved one; failures and unparseable times are ignored. func TestBackupSuccessState_KeepsNewestSuccessOnly(t *testing.T) { path := filepath.Join(t.TempDir(), "s.json") s := NewBackupSuccessState(path) newer := time.Date(2026, 10, 5, 0, 0, 0, 0, time.UTC) older := newer.Add(-48 * time.Hour) for _, b := range []hub.Backup{ {VMID: 1, Success: true, StartedAt: newer.Format(time.RFC3339)}, {VMID: 1, Success: true, StartedAt: older.Format(time.RFC3339)}, // older: ignored {VMID: 1, Success: false, StartedAt: newer.Add(time.Hour).Format(time.RFC3339)}, // failure: ignored {VMID: 1, Success: true, StartedAt: "not-a-time"}, // unparseable: ignored } { if err := s.RecordBackupSuccess("t", b); err != nil { t.Fatal(err) } } if got, _ := NewBackupSuccessState(path).LastKnownSuccess("t", 1); !got.Equal(newer) { t.Fatalf("want the newest success %v, got %v", newer, got) } } // A corrupt file degrades to "nothing known" (the pre-R-894 DUE answer), never a crash. func TestBackupSuccessState_CorruptFileIsEmpty(t *testing.T) { path := filepath.Join(t.TempDir(), "s.json") if err := os.WriteFile(path, []byte("{not json"), 0o600); err != nil { t.Fatal(err) } if _, ok := NewBackupSuccessState(path).LastKnownSuccess("t", 1); ok { t.Fatal("a corrupt file must read as nothing known") } }