package backup import ( "context" "sync/atomic" "testing" "time" "gitea.dooplex.hu/admin/felhom-agent/internal/proxmox" ) // 8B.2: in snapshot mode, the runner fires onSnapshot when the storage-snapshot marker appears in // the task log — mid-backup, before completion. func TestBackupWithSnapshotHook_FiresOnMarker(t *testing.T) { old := snapshotWatchInterval snapshotWatchInterval = 2 * time.Millisecond defer func() { snapshotWatchInterval = old }() api := &fakeBackupAPI{ vzdumpUPID: "UPID:backup", waitGate: make(chan struct{}), // hold the backup open so the watcher gets to poll logLines: []string{"INFO: backup mode: snapshot", "INFO: create storage snapshot 'vzdump'"}, content: []proxmox.StorageContent{{VolID: "local:backup/vzdump-lxc-9001-x", Content: "backup", VMID: 9001, Size: 100, CTime: 1}}, } r := NewBackupRunner(api, "local", "", "", quiet()) var fired int32 done := make(chan struct{}) go func() { _, _ = r.BackupWithSnapshotHook(context.Background(), 9001, func() { atomic.StoreInt32(&fired, 1) }) close(done) }() // the watcher should fire onSnapshot well before we release the backup deadline := time.Now().Add(time.Second) for time.Now().Before(deadline) && atomic.LoadInt32(&fired) == 0 { time.Sleep(2 * time.Millisecond) } if atomic.LoadInt32(&fired) != 1 { t.Fatal("onSnapshot did not fire on the storage-snapshot marker") } close(api.waitGate) // let the backup complete <-done } // Stop/downgraded mode → no storage-snapshot marker → onSnapshot never fires (the 8B.2 fallback). func TestBackupWithSnapshotHook_StopMode_NeverFires(t *testing.T) { old := snapshotWatchInterval snapshotWatchInterval = 2 * time.Millisecond defer func() { snapshotWatchInterval = old }() api := &fakeBackupAPI{ vzdumpUPID: "UPID:backup", waitGate: make(chan struct{}), logLines: []string{"INFO: backup mode: stop"}, // downgraded; no snapshot marker content: []proxmox.StorageContent{{VolID: "local:backup/vzdump-lxc-9001-x", Content: "backup", VMID: 9001, Size: 100, CTime: 1}}, } r := NewBackupRunner(api, "local", "", "", quiet()) var fired int32 done := make(chan struct{}) go func() { _, _ = r.BackupWithSnapshotHook(context.Background(), 9001, func() { atomic.StoreInt32(&fired, 1) }) close(done) }() time.Sleep(40 * time.Millisecond) // the watcher polls several times + sees stop mode → returns if atomic.LoadInt32(&fired) != 0 { t.Fatal("onSnapshot fired in stop mode (must not)") } close(api.waitGate) <-done }