package outboxpartition import ( "context" "errors" "reflect" "testing" "time" ) func TestConfiguredPartitionsNormalizeAndSkipDiscovery(t *testing.T) { partitions := New([]string{" LALU ", "fami", "HUWAA", "fami", ""}, time.Hour) discoverCalls := 0 apps, err := partitions.Resolve(context.Background(), func(context.Context) ([]string, error) { discoverCalls++ return nil, errors.New("must not run") }) if err != nil { t.Fatalf("resolve configured partitions: %v", err) } if want := []string{"lalu", "fami", "huwaa"}; !reflect.DeepEqual(apps, want) { t.Fatalf("configured apps = %v, want %v", apps, want) } if discoverCalls != 0 { t.Fatalf("configured allowlist unexpectedly queried discovery %d times", discoverCalls) } } func TestDiscoveredPartitionsAreCachedAndStaleListSurvivesRefreshFailure(t *testing.T) { partitions := New(nil, time.Hour) discoverCalls := 0 discover := func(context.Context) ([]string, error) { discoverCalls++ if discoverCalls == 1 { return []string{"huwaa", " LALU ", "fami"}, nil } return nil, errors.New("mysql unavailable") } first, err := partitions.Resolve(context.Background(), discover) if err != nil || !reflect.DeepEqual(first, []string{"fami", "huwaa", "lalu"}) { t.Fatalf("initial discovery apps=%v err=%v", first, err) } second, err := partitions.Resolve(context.Background(), discover) if err != nil || !reflect.DeepEqual(second, first) || discoverCalls != 1 { t.Fatalf("cached discovery apps=%v err=%v calls=%d", second, err, discoverCalls) } // Force only the refresh boundary; production reaches this state after the // five-minute interval without exposing a public cache mutation API. partitions.mu.Lock() partitions.nextRefresh = time.Time{} partitions.mu.Unlock() stale, err := partitions.Resolve(context.Background(), discover) if err == nil || !reflect.DeepEqual(stale, first) { t.Fatalf("failed refresh must retain stale apps: apps=%v err=%v", stale, err) } } func TestClaimFairRotatesLaluFamiHuwaaAndSkipsEmptyQueue(t *testing.T) { partitions := New([]string{"lalu", "fami", "huwaa"}, time.Hour) queues := map[string][]string{ "lalu": {"l1", "l2", "l3"}, "fami": nil, "huwaa": {"h1", "h2", "h3"}, } claim := func(_ context.Context, app string, limit int) ([]string, error) { queue := queues[app] if len(queue) < limit { limit = len(queue) } result := append([]string(nil), queue[:limit]...) queues[app] = queue[limit:] return result, nil } apps, err := partitions.Resolve(context.Background(), nil) if err != nil { t.Fatalf("resolve configured apps: %v", err) } first, err := ClaimFair(context.Background(), partitions.Order(apps), 3, claim) if err != nil || !reflect.DeepEqual(first, []string{"l1", "h1", "l2"}) { t.Fatalf("first fair claim records=%v err=%v", first, err) } second, err := ClaimFair(context.Background(), partitions.Order(apps), 3, claim) if err != nil || !reflect.DeepEqual(second, []string{"h2", "l3", "h3"}) { t.Fatalf("rotated fair claim records=%v err=%v", second, err) } emptyCalls := 0 empty, err := ClaimFair[string](context.Background(), partitions.Order(apps), 3, func(context.Context, string, int) ([]string, error) { emptyCalls++ return nil, nil }) if err != nil || len(empty) != 0 || emptyCalls != 3 { t.Fatalf("empty queues records=%v err=%v calls=%d", empty, err, emptyCalls) } }