package publishpush import ( "testing" "time" "galaxy/notification/internal/api/intentstream" "galaxy/notification/internal/service/acceptintent" "galaxy/transcoder" "github.com/stretchr/testify/require" ) func TestEncoderEncodesSupportedPushNotificationTypes(t *testing.T) { t.Parallel() now := time.UnixMilli(1775121700000).UTC() tests := []struct { name string notificationType intentstream.NotificationType payloadJSON string assertPayload func(*testing.T, []byte) }{ { name: "game turn ready", notificationType: intentstream.NotificationTypeGameTurnReady, payloadJSON: `{"game_id":"game-1","game_name":"Nebula Clash","turn_number":54}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToGameTurnReadyEvent(payload) require.NoError(t, err) require.Equal(t, "game-1", event.GameID) require.Equal(t, int64(54), event.TurnNumber) }, }, { name: "game finished", notificationType: intentstream.NotificationTypeGameFinished, payloadJSON: `{"final_turn_number":81,"game_id":"game-2","game_name":"Nova"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToGameFinishedEvent(payload) require.NoError(t, err) require.Equal(t, "game-2", event.GameID) require.Equal(t, int64(81), event.FinalTurnNumber) }, }, { name: "lobby application submitted", notificationType: intentstream.NotificationTypeLobbyApplicationSubmitted, payloadJSON: `{"applicant_name":"Nova Pilot","applicant_user_id":"user-2","game_id":"game-3","game_name":"Orion Front"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyApplicationSubmittedEvent(payload) require.NoError(t, err) require.Equal(t, "game-3", event.GameID) require.Equal(t, "user-2", event.ApplicantUserID) }, }, { name: "lobby membership approved", notificationType: intentstream.NotificationTypeLobbyMembershipApproved, payloadJSON: `{"game_id":"game-4","game_name":"Ares"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyMembershipApprovedEvent(payload) require.NoError(t, err) require.Equal(t, "game-4", event.GameID) }, }, { name: "lobby membership rejected", notificationType: intentstream.NotificationTypeLobbyMembershipRejected, payloadJSON: `{"game_id":"game-5","game_name":"Atlas"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyMembershipRejectedEvent(payload) require.NoError(t, err) require.Equal(t, "game-5", event.GameID) }, }, { name: "lobby invite created", notificationType: intentstream.NotificationTypeLobbyInviteCreated, payloadJSON: `{"game_id":"game-6","game_name":"Vega","inviter_name":"Nova Pilot","inviter_user_id":"user-9"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyInviteCreatedEvent(payload) require.NoError(t, err) require.Equal(t, "game-6", event.GameID) require.Equal(t, "user-9", event.InviterUserID) }, }, { name: "lobby invite redeemed", notificationType: intentstream.NotificationTypeLobbyInviteRedeemed, payloadJSON: `{"game_id":"game-7","game_name":"Lyra","invitee_name":"Skipper","invitee_user_id":"user-10"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyInviteRedeemedEvent(payload) require.NoError(t, err) require.Equal(t, "game-7", event.GameID) require.Equal(t, "user-10", event.InviteeUserID) }, }, { name: "lobby race name registration eligible", notificationType: intentstream.NotificationTypeLobbyRaceNameRegistrationEligible, payloadJSON: `{"eligible_until_ms":1775208100000,"game_id":"game-8","game_name":"Aurora","race_name":"Skylancer"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyRaceNameRegistrationEligibleEvent(payload) require.NoError(t, err) require.Equal(t, "game-8", event.GameID) require.Equal(t, "Skylancer", event.RaceName) require.Equal(t, int64(1775208100000), event.EligibleUntilMs) }, }, { name: "lobby race name registered", notificationType: intentstream.NotificationTypeLobbyRaceNameRegistered, payloadJSON: `{"race_name":"Skylancer"}`, assertPayload: func(t *testing.T, payload []byte) { t.Helper() event, err := transcoder.PayloadToLobbyRaceNameRegisteredEvent(payload) require.NoError(t, err) require.Equal(t, "Skylancer", event.RaceName) }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() event, err := Encoder{}.Encode( acceptintent.NotificationRecord{ NotificationID: "1775121700000-0", NotificationType: tt.notificationType, Producer: tt.notificationType.ExpectedProducer(), AudienceKind: intentstream.AudienceKindUser, RecipientUserIDs: []string{"user-1"}, PayloadJSON: tt.payloadJSON, IdempotencyKey: "idem-1", RequestFingerprint: "sha256:deadbeef", RequestID: "request-1", TraceID: "trace-1", OccurredAt: now, AcceptedAt: now, UpdatedAt: now, }, acceptintent.NotificationRoute{ NotificationID: "1775121700000-0", RouteID: "push:user:user-1", Channel: intentstream.ChannelPush, RecipientRef: "user:user-1", Status: acceptintent.RouteStatusPending, MaxAttempts: 3, NextAttemptAt: now, CreatedAt: now, UpdatedAt: now, }, ) require.NoError(t, err) require.Equal(t, "user-1", event.UserID) require.Equal(t, string(tt.notificationType), event.EventType) require.Equal(t, "1775121700000-0/push:user:user-1", event.EventID) require.Equal(t, "request-1", event.RequestID) require.Equal(t, "trace-1", event.TraceID) require.NotEmpty(t, event.PayloadBytes) tt.assertPayload(t, event.PayloadBytes) }) } } func TestEncoderRejectsInvalidStoredPayload(t *testing.T) { t.Parallel() now := time.UnixMilli(1775121700000).UTC() _, err := Encoder{}.Encode( acceptintent.NotificationRecord{ NotificationID: "1775121700000-0", NotificationType: intentstream.NotificationTypeGameTurnReady, Producer: intentstream.ProducerGameMaster, AudienceKind: intentstream.AudienceKindUser, RecipientUserIDs: []string{"user-1"}, PayloadJSON: `{"game_id":"","game_name":"Nebula Clash","turn_number":0}`, IdempotencyKey: "idem-1", RequestFingerprint: "sha256:deadbeef", OccurredAt: now, AcceptedAt: now, UpdatedAt: now, }, acceptintent.NotificationRoute{ NotificationID: "1775121700000-0", RouteID: "push:user:user-1", Channel: intentstream.ChannelPush, RecipientRef: "user:user-1", Status: acceptintent.RouteStatusPending, MaxAttempts: 3, NextAttemptAt: now, CreatedAt: now, UpdatedAt: now, }, ) require.Error(t, err) require.ErrorContains(t, err, "payload_encoding_failed") }