Files
galaxy-game/mail/internal/adapters/redisstate/index_cleaner_test.go
T
2026-04-17 18:39:16 +02:00

113 lines
4.0 KiB
Go

package redisstate
import (
"context"
"testing"
"time"
"galaxy/mail/internal/domain/attempt"
deliverydomain "galaxy/mail/internal/domain/delivery"
"github.com/alicebob/miniredis/v2"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
)
func TestIndexCleanerRemovesStaleMembersAfterDeliveryExpiry(t *testing.T) {
t.Parallel()
server := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{Addr: server.Addr()})
t.Cleanup(func() { require.NoError(t, client.Close()) })
writer, err := NewAtomicWriter(client)
require.NoError(t, err)
cleaner, err := NewIndexCleaner(client)
require.NoError(t, err)
record := validDelivery(t)
record.Source = deliverydomain.SourceNotification
record.ResendParentDeliveryID = ""
record.Status = deliverydomain.StatusQueued
record.SentAt = nil
record.LocaleFallbackUsed = false
record.UpdatedAt = record.CreatedAt.Add(time.Minute)
require.NoError(t, record.Validate())
input := CreateAcceptanceInput{
Delivery: record,
FirstAttempt: ptr(validScheduledAttempt(t, record.DeliveryID)),
Idempotency: ptr(validIdempotencyRecord(t, record.Source, record.DeliveryID, record.IdempotencyKey)),
}
require.NoError(t, writer.CreateAcceptance(context.Background(), input))
deadLetterEntry := validDeadLetterEntry(t, record.DeliveryID)
deadLetterPayload, err := MarshalDeadLetter(deadLetterEntry)
require.NoError(t, err)
require.NoError(t, client.Set(context.Background(), Keyspace{}.DeadLetter(record.DeliveryID), deadLetterPayload, DeadLetterTTL).Err())
server.FastForward(DeliveryTTL + time.Second)
require.False(t, server.Exists(Keyspace{}.Delivery(record.DeliveryID)))
require.True(t, server.Exists(Keyspace{}.Attempt(record.DeliveryID, input.FirstAttempt.AttemptNo)))
require.True(t, server.Exists(Keyspace{}.DeadLetter(record.DeliveryID)))
report, err := cleaner.CleanDeliveryIndexes(context.Background())
require.NoError(t, err)
require.Positive(t, report.ScannedIndexes)
require.Positive(t, report.ScannedMembers)
require.Positive(t, report.RemovedMembers)
assertZCard := func(key string, want int64) {
t.Helper()
got, err := client.ZCard(context.Background(), key).Result()
require.NoError(t, err)
require.Equal(t, want, got)
}
assertZCard(Keyspace{}.CreatedAtIndex(), 0)
assertZCard(Keyspace{}.SourceIndex(record.Source), 0)
assertZCard(Keyspace{}.StatusIndex(record.Status), 0)
assertZCard(Keyspace{}.RecipientIndex(record.Envelope.To[0]), 0)
assertZCard(Keyspace{}.RecipientIndex(record.Envelope.Cc[0]), 0)
assertZCard(Keyspace{}.RecipientIndex(record.Envelope.Bcc[0]), 0)
assertZCard(Keyspace{}.TemplateIndex(record.TemplateID), 0)
assertZCard(Keyspace{}.IdempotencyIndex(record.Source, record.IdempotencyKey), 0)
require.True(t, server.Exists(Keyspace{}.Attempt(record.DeliveryID, input.FirstAttempt.AttemptNo)))
require.True(t, server.Exists(Keyspace{}.DeadLetter(record.DeliveryID)))
scheduleCard, err := client.ZCard(context.Background(), Keyspace{}.AttemptSchedule()).Result()
require.NoError(t, err)
require.EqualValues(t, 1, scheduleCard)
}
func TestIndexCleanerSkipsMalformedCommandIndex(t *testing.T) {
t.Parallel()
server := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{Addr: server.Addr()})
t.Cleanup(func() { require.NoError(t, client.Close()) })
cleaner, err := NewIndexCleaner(client)
require.NoError(t, err)
entry := validMalformedCommandEntry(t)
require.NoError(t, client.ZAdd(context.Background(), Keyspace{}.MalformedCommandCreatedAtIndex(), redis.Z{
Score: float64(entry.RecordedAt.UTC().UnixMilli()),
Member: entry.StreamEntryID,
}).Err())
report, err := cleaner.CleanDeliveryIndexes(context.Background())
require.NoError(t, err)
require.Zero(t, report.ScannedIndexes)
require.Zero(t, report.ScannedMembers)
require.Zero(t, report.RemovedMembers)
indexMembers, err := client.ZRange(context.Background(), Keyspace{}.MalformedCommandCreatedAtIndex(), 0, -1).Result()
require.NoError(t, err)
require.Equal(t, []string{entry.StreamEntryID}, indexMembers)
}
var _ = attempt.Attempt{}