Pulse/internal/hosted/reaper_additional_test.go
2026-03-18 16:06:30 +00:00

141 lines
3.7 KiB
Go

package hosted
import (
"errors"
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
)
func TestNewReaperDefaultsToMinuteInterval(t *testing.T) {
r := NewReaper(&mockOrgLister{}, &mockOrgDeleter{}, 0, false)
if r.scanInterval != time.Minute {
t.Fatalf("expected default scan interval %v, got %v", time.Minute, r.scanInterval)
}
}
func TestReaperScanOnceHandlesNilReceiver(t *testing.T) {
var r *Reaper
if got := r.ScanOnce(); got != nil {
t.Fatalf("expected nil result for nil receiver, got %v", got)
}
}
func TestReaperScanOnceReturnsDryRunResult(t *testing.T) {
fixedTime := time.Date(2026, 2, 11, 10, 0, 0, 0, time.UTC)
requestedAt := fixedTime.Add(-31 * 24 * time.Hour)
lister := &mockOrgLister{
orgs: []*models.Organization{
{
ID: "org-scan-once",
Status: models.OrgStatusPendingDeletion,
DeletionRequestedAt: &requestedAt,
RetentionDays: 30,
},
},
}
r := NewReaper(lister, &mockOrgDeleter{}, time.Minute, false)
r.now = func() time.Time { return fixedTime }
results := r.ScanOnce()
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
if results[0].Action != "dry_run" {
t.Fatalf("expected dry_run action, got %q", results[0].Action)
}
}
func TestReaperScanReturnsNilForMissingDependenciesOrListError(t *testing.T) {
failingLister := &mockOrgLister{err: errors.New("list failed")}
testCases := []struct {
name string
reaper *Reaper
}{
{
name: "missing lister",
reaper: &Reaper{now: time.Now},
},
{
name: "missing clock",
reaper: &Reaper{lister: &mockOrgLister{}},
},
{
name: "lister error",
reaper: &Reaper{lister: failingLister, now: time.Now},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := tc.reaper.scan(); got != nil {
t.Fatalf("expected nil scan result, got %v", got)
}
})
}
}
func TestReaperLiveModeReportsNilDeleterError(t *testing.T) {
fixedTime := time.Date(2026, 2, 11, 10, 0, 0, 0, time.UTC)
requestedAt := fixedTime.Add(-31 * 24 * time.Hour)
lister := &mockOrgLister{
orgs: []*models.Organization{
{
ID: "org-nil-deleter",
Status: models.OrgStatusPendingDeletion,
DeletionRequestedAt: &requestedAt,
RetentionDays: 30,
},
},
}
r := NewReaper(lister, nil, time.Minute, true)
r.now = func() time.Time { return fixedTime }
results := r.scan()
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
if results[0].Action != "deleted" {
t.Fatalf("expected deleted action, got %q", results[0].Action)
}
if results[0].Error == nil || results[0].Error.Error() != "org deleter is nil" {
t.Fatalf("expected org deleter is nil error, got %v", results[0].Error)
}
}
func TestReaperLiveModePropagatesDeleteError(t *testing.T) {
fixedTime := time.Date(2026, 2, 11, 10, 0, 0, 0, time.UTC)
requestedAt := fixedTime.Add(-31 * 24 * time.Hour)
deleteErr := errors.New("delete failed")
lister := &mockOrgLister{
orgs: []*models.Organization{
{
ID: "org-delete-error",
Status: models.OrgStatusPendingDeletion,
DeletionRequestedAt: &requestedAt,
RetentionDays: 30,
},
},
}
deleter := &mockOrgDeleter{err: deleteErr}
r := NewReaper(lister, deleter, time.Minute, true)
r.now = func() time.Time { return fixedTime }
results := r.scan()
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
if !errors.Is(results[0].Error, deleteErr) {
t.Fatalf("expected delete error %v, got %v", deleteErr, results[0].Error)
}
if deleter.calls != 1 {
t.Fatalf("expected one delete call, got %d", deleter.calls)
}
}