package account import ( "context" "errors" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" ) // fakeExpiryQuerier records the `since` bound of each call and replays a scripted // result/error per call, so the sweeper's window and dispatch logic is testable // without a database. type fakeExpiryQuerier struct { results [][]uuid.UUID errs []error sinces []time.Time idx int } func (f *fakeExpiryQuerier) SuspensionsExpiredBetween(_ context.Context, since, _ time.Time) ([]uuid.UUID, error) { f.sinces = append(f.sinces, since) i := f.idx f.idx++ if i < len(f.errs) && f.errs[i] != nil { return nil, f.errs[i] } if i < len(f.results) { return f.results[i], nil } return nil, nil } func newSweeper(store suspensionExpiryQuerier, onExpire func(uuid.UUID)) *SuspensionSweeper { return &SuspensionSweeper{ store: store, onExpire: onExpire, log: zap.NewNop(), since: time.Now().Add(-time.Minute).UTC(), } } func TestSuspensionSweeperDispatchesAndAdvances(t *testing.T) { id1, id2 := uuid.New(), uuid.New() fake := &fakeExpiryQuerier{results: [][]uuid.UUID{{id1, id2}, nil}} var got []uuid.UUID w := newSweeper(fake, func(id uuid.UUID) { got = append(got, id) }) first := w.since w.sweep(context.Background()) assert.Equal(t, []uuid.UUID{id1, id2}, got, "every expired account is dispatched") assert.True(t, w.since.After(first), "the window advances on success") // A second sweep opens the next window at the previous upper bound. prev := w.since w.sweep(context.Background()) require.Len(t, fake.sinces, 2) assert.True(t, fake.sinces[1].After(fake.sinces[0]), "consecutive windows are contiguous and forward") assert.True(t, fake.sinces[1].Equal(prev), "the next window starts at the previous upper bound") } func TestSuspensionSweeperKeepsWindowOnError(t *testing.T) { fake := &fakeExpiryQuerier{errs: []error{errors.New("db down")}} w := newSweeper(fake, func(uuid.UUID) { t.Fatal("onExpire must not run when the query fails") }) before := w.since w.sweep(context.Background()) assert.True(t, w.since.Equal(before), "the window is retained on error so the next tick retries it") } func TestNewSuspensionSweeperDefaults(t *testing.T) { w := NewSuspensionSweeper(nil, func(uuid.UUID) {}, nil) assert.Equal(t, time.Minute, w.Interval()) assert.NotNil(t, w.log, "a nil logger is tolerated") assert.WithinDuration(t, time.Now().UTC(), w.since, time.Second, "the first window opens at construction time") }