124 lines
3.8 KiB
Go
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)
|
|
}
|