Files
galaxy-game/rtmanager/internal/adapters/notificationpublisher/publisher_test.go
T
2026-04-28 20:39:18 +02:00

124 lines
3.8 KiB
Go

package notificationpublisher
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"galaxy/notificationintent"
)
func newRedis(t *testing.T) (*redis.Client, *miniredis.Miniredis) {
t.Helper()
server := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{Addr: server.Addr()})
t.Cleanup(func() { _ = client.Close() })
return client, server
}
func readStream(t *testing.T, client *redis.Client, stream string) []redis.XMessage {
t.Helper()
messages, err := client.XRange(context.Background(), stream, "-", "+").Result()
require.NoError(t, err)
return messages
}
func TestNewPublisherValidation(t *testing.T) {
t.Run("nil client", func(t *testing.T) {
_, err := NewPublisher(Config{})
require.Error(t, err)
assert.Contains(t, err.Error(), "nil redis client")
})
}
func TestPublisherWritesIntent(t *testing.T) {
client, _ := newRedis(t)
publisher, err := NewPublisher(Config{Client: client, Stream: "notification:intents"})
require.NoError(t, err)
intent, err := notificationintent.NewRuntimeImagePullFailedIntent(
notificationintent.Metadata{
IdempotencyKey: "rtmanager:start:game-1:abc",
OccurredAt: time.UnixMilli(1714200000000).UTC(),
},
notificationintent.RuntimeImagePullFailedPayload{
GameID: "game-1",
ImageRef: "galaxy/game:1.4.2",
ErrorCode: "image_pull_failed",
ErrorMessage: "registry timeout",
AttemptedAtMs: 1714200000000,
},
)
require.NoError(t, err)
require.NoError(t, publisher.Publish(context.Background(), intent))
messages := readStream(t, client, "notification:intents")
require.Len(t, messages, 1)
values := messages[0].Values
assert.Equal(t, "runtime.image_pull_failed", values["notification_type"])
assert.Equal(t, "runtime_manager", values["producer"])
assert.Equal(t, "admin_email", values["audience_kind"])
assert.Equal(t, "rtmanager:start:game-1:abc", values["idempotency_key"])
// recipient_user_ids_json must be absent for admin_email audience.
_, hasRecipients := values["recipient_user_ids_json"]
assert.False(t, hasRecipients)
payloadRaw, ok := values["payload_json"].(string)
require.True(t, ok)
var payload map[string]any
require.NoError(t, json.Unmarshal([]byte(payloadRaw), &payload))
assert.Equal(t, "game-1", payload["game_id"])
assert.Equal(t, "galaxy/game:1.4.2", payload["image_ref"])
}
func TestPublisherForwardsValidationError(t *testing.T) {
client, _ := newRedis(t)
publisher, err := NewPublisher(Config{Client: client})
require.NoError(t, err)
// Intent with a zero OccurredAt fails the shared validator.
bad := notificationintent.Intent{
NotificationType: notificationintent.NotificationTypeRuntimeImagePullFailed,
Producer: notificationintent.ProducerRuntimeManager,
AudienceKind: notificationintent.AudienceKindAdminEmail,
IdempotencyKey: "k",
PayloadJSON: `{"game_id":"g","image_ref":"r","error_code":"c","error_message":"m","attempted_at_ms":1}`,
}
require.Error(t, publisher.Publish(context.Background(), bad))
}
func TestPublisherDefaultsStreamName(t *testing.T) {
client, _ := newRedis(t)
publisher, err := NewPublisher(Config{Client: client, Stream: ""})
require.NoError(t, err)
intent, err := notificationintent.NewRuntimeContainerStartFailedIntent(
notificationintent.Metadata{
IdempotencyKey: "k",
OccurredAt: time.UnixMilli(1714200000000).UTC(),
},
notificationintent.RuntimeContainerStartFailedPayload{
GameID: "g",
ImageRef: "r",
ErrorCode: "container_start_failed",
ErrorMessage: "boom",
AttemptedAtMs: 1714200000000,
},
)
require.NoError(t, err)
require.NoError(t, publisher.Publish(context.Background(), intent))
messages := readStream(t, client, notificationintent.DefaultIntentsStream)
require.Len(t, messages, 1)
}