package opstate import ( "sync/atomic" "testing" "time" ) type fakeOp struct { id string done atomic.Bool updatedAt time.Time canceled atomic.Bool } func (f *fakeOp) ID() string { return f.id } func (f *fakeOp) IsDone() bool { return f.done.Load() } func (f *fakeOp) UpdatedAt() time.Time { return f.updatedAt } func (f *fakeOp) Cancel() { f.canceled.Store(true) } func TestRegistryInsertAndGet(t *testing.T) { var r Registry[*fakeOp] op := &fakeOp{id: "op-1", updatedAt: time.Now()} r.Insert(op) got, ok := r.Get("op-1") if !ok { t.Fatal("Get after Insert missed") } if got.ID() != "op-1" { t.Fatalf("Get().ID = %q", got.ID()) } _, ok = r.Get("missing") if ok { t.Fatal("Get on missing key should miss") } } func TestRegistryPruneDropsCompletedOldOps(t *testing.T) { var r Registry[*fakeOp] now := time.Now() recent := &fakeOp{id: "recent", updatedAt: now} recent.done.Store(true) stale := &fakeOp{id: "stale", updatedAt: now.Add(-time.Hour)} stale.done.Store(true) pending := &fakeOp{id: "pending", updatedAt: now.Add(-time.Hour)} // NOT done → stays even though old. r.Insert(recent) r.Insert(stale) r.Insert(pending) cutoff := now.Add(-time.Minute) r.Prune(cutoff) if _, ok := r.Get("stale"); ok { t.Error("stale op should have been pruned") } if _, ok := r.Get("recent"); !ok { t.Error("recent op should survive (newer than cutoff)") } if _, ok := r.Get("pending"); !ok { t.Error("pending op should survive (not done)") } } func TestRegistryPruneNoOpOnEmpty(t *testing.T) { var r Registry[*fakeOp] // Just shouldn't panic. r.Prune(time.Now()) }