package backup import ( "context" "errors" "testing" "time" "gitea.dooplex.hu/admin/felhom-agent/internal/proxmox" ) const ( thisBoxKey = "de:51:7a:18:cb:39:22:30:2c:84:f5:8b:d1:91:4b:7e:81:bb:69:b8:89:0f:57:ac:d3:59:e1:1a:62:25:11:2c" earlierBox1 = "6b:ca:5f:3f:ca:0f:e2:3f:fb:24:62:89:bf:e7:64:59:9a:41:c5:e6:e3:9f:3f:5f:e1:71:7b:a1:9d:24:67:82" earlierBox2 = "fe:3d:db:95:d4:df:ab:e1:7d:4a:89:fa:2b:07:53:6a:e4:d2:85:95:d1:90:27:4b:d9:c6:92:20:95:04:e5:d4" ) // R-727 (v0.138.0) — the 2026-09-30 shape, measured on a fresh box for a returning customer: the PBS // namespace held two archives of earlier boxes (same guest 9201, same token) and this box's own, which was not // settled yet. The old picker chose the earlier box's newest settled archive and failed `wrong key`. // The CONSEQUENCE asserted: no archive of another box is ever picked; with this box's archive settled it is picked. // COMPANION RED-PROOF: remove the `ownKey != "" && !EqualFold(...)` skip → the first case picks 2026-09-16T21:59:54Z. func TestR727_TheRestoreTestTakesOnlyThisBoxsArchives(t *testing.T) { day := int64(86400) now := int64(1790740000) // 2026-09-30 ~04:00Z own := proxmox.StorageContent{VolID: "felhom-pbs:backup/ct/9201/2026-09-29T19:37:07Z", Content: "backup", VMID: 9201, Size: 3490689830, CTime: 1790710627, Encrypted: thisBoxKey} api := &fakeBackupAPI{ storages: []proxmox.Storage{{Storage: "felhom-pbs", Type: "pbs", EncryptionKey: thisBoxKey}}, content: []proxmox.StorageContent{ {VolID: "felhom-pbs:backup/ct/9201/2026-09-16T17:27:32Z", Content: "backup", VMID: 9201, Size: 4774114206, CTime: 1789579652, Encrypted: earlierBox2}, {VolID: "felhom-pbs:backup/ct/9201/2026-09-16T21:59:54Z", Content: "backup", VMID: 9201, Size: 20811501236, CTime: 1789595994, Encrypted: earlierBox1}, own, }, } r := NewBackupRunner(api, "local", proxmox.ModeSnapshot, "", "keep-last=1", quiet()) // 1. The night of 2026-09-30: this box's own archive is ~6 h old, not settled (cutoff 24 h) — nothing to prove. got, _, err := r.PickSettledRestoreCandidateOn(context.Background(), "felhom-pbs", time.Unix(now-day, 0).UTC()) if err != nil { t.Fatal(err) } if got != "" { t.Fatalf("picked %q — an archive of ANOTHER box is never this box's proof (R-727)", got) } // 2. A day later this box's own archive is settled — it is the one picked. got, _, err = r.PickSettledRestoreCandidateOn(context.Background(), "felhom-pbs", time.Unix(now+day, 0).UTC()) if err != nil { t.Fatal(err) } if got != own.VolID { t.Fatalf("picked %q, want this box's own %q", got, own.VolID) } } // An unencrypted storage (a local dir) holds only this box's vzdumps — no key filter applies. func TestR727_UnencryptedStorageIsNotFiltered(t *testing.T) { api := &fakeBackupAPI{ storages: []proxmox.Storage{{Storage: "local", Type: "dir"}}, content: []proxmox.StorageContent{{VolID: "local:backup/vzdump-lxc-9201-2026_09_29-21_27_05.tar.zst", Content: "backup", VMID: 9201, Size: 955425507, CTime: 1790710025}}, } r := NewBackupRunner(api, "local", proxmox.ModeSnapshot, "", "keep-last=1", quiet()) if got, _, err := r.PickSettledRestoreCandidateOn(context.Background(), "local", time.Time{}); err != nil || got == "" { t.Fatalf("got %q err %v", got, err) } } // A storage-list failure makes the tier UNKNOWN (an error), never "nothing to prove". func TestR727_KeyLookupFailureIsUnknown(t *testing.T) { api := &fakeBackupAPI{storageErr: errors.New("proxmox: GET /storage -> HTTP 500"), content: []proxmox.StorageContent{{VolID: "felhom-pbs:backup/ct/9201/x", Content: "backup", VMID: 9201}}} r := NewBackupRunner(api, "local", proxmox.ModeSnapshot, "", "keep-last=1", quiet()) if _, _, err := r.PickSettledRestoreCandidateOn(context.Background(), "felhom-pbs", time.Time{}); err == nil { t.Fatal("a failed key lookup must surface as an error (tier UNKNOWN)") } }