Files
galaxy-game/gamemaster/notificationintent_audit_test.go
T
2026-05-03 07:59:03 +02:00

148 lines
5.8 KiB
Go

package gamemaster
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"galaxy/notificationintent"
)
// TestNotificationIntentConstructorsForGameMaster freezes the producer-side
// surface of the three GM-owned notification types against
// `pkg/notificationintent`. It complements the YAML-level catalog freeze in
// `notification/contract_asyncapi_test.go` by binding the contract at compile
// time: any rename of a constant, constructor, payload struct, or struct field
// breaks this file's build before it can reach a YAML edit.
//
// The three types frozen here are documented in `gamemaster/README.md`
// §Notification Contracts as the GM-owned producer catalog.
func TestNotificationIntentConstructorsForGameMaster(t *testing.T) {
t.Parallel()
metadata := notificationintent.Metadata{
IdempotencyKey: "gm-stage07-freeze",
OccurredAt: time.UnixMilli(1775121700000).UTC(),
}
recipientUserIDs := []string{"user-1", "user-2"}
t.Run("game.turn.ready", func(t *testing.T) {
t.Parallel()
intent, err := notificationintent.NewGameTurnReadyIntent(
metadata,
recipientUserIDs,
notificationintent.GameTurnReadyPayload{
GameID: "game-1",
GameName: "Nebula Clash",
TurnNumber: 7,
},
)
require.NoError(t, err)
require.Equal(t, notificationintent.NotificationTypeGameTurnReady, intent.NotificationType)
require.Equal(t, "game.turn.ready", intent.NotificationType.String())
require.Equal(t, notificationintent.ProducerGameMaster, intent.Producer)
require.Equal(t, notificationintent.AudienceKindUser, intent.AudienceKind)
require.Equal(t, []string{"user-1", "user-2"}, intent.RecipientUserIDs)
require.Equal(t, metadata.IdempotencyKey, intent.IdempotencyKey)
require.True(t, intent.OccurredAt.Equal(metadata.OccurredAt))
require.NoError(t, intent.Validate())
require.Contains(t, intent.PayloadJSON, `"game_id":"game-1"`)
require.Contains(t, intent.PayloadJSON, `"game_name":"Nebula Clash"`)
require.Contains(t, intent.PayloadJSON, `"turn_number":7`)
})
t.Run("game.finished", func(t *testing.T) {
t.Parallel()
intent, err := notificationintent.NewGameFinishedIntent(
metadata,
recipientUserIDs,
notificationintent.GameFinishedPayload{
GameID: "game-1",
GameName: "Nebula Clash",
FinalTurnNumber: 7,
},
)
require.NoError(t, err)
require.Equal(t, notificationintent.NotificationTypeGameFinished, intent.NotificationType)
require.Equal(t, "game.finished", intent.NotificationType.String())
require.Equal(t, notificationintent.ProducerGameMaster, intent.Producer)
require.Equal(t, notificationintent.AudienceKindUser, intent.AudienceKind)
require.Equal(t, []string{"user-1", "user-2"}, intent.RecipientUserIDs)
require.NoError(t, intent.Validate())
require.Contains(t, intent.PayloadJSON, `"game_id":"game-1"`)
require.Contains(t, intent.PayloadJSON, `"game_name":"Nebula Clash"`)
require.Contains(t, intent.PayloadJSON, `"final_turn_number":7`)
})
t.Run("game.generation_failed", func(t *testing.T) {
t.Parallel()
intent, err := notificationintent.NewGameGenerationFailedIntent(
metadata,
notificationintent.GameGenerationFailedPayload{
GameID: "game-1",
GameName: "Nebula Clash",
FailureReason: "engine_timeout",
},
)
require.NoError(t, err)
require.Equal(t, notificationintent.NotificationTypeGameGenerationFailed, intent.NotificationType)
require.Equal(t, "game.generation_failed", intent.NotificationType.String())
require.Equal(t, notificationintent.ProducerGameMaster, intent.Producer)
require.Equal(t, notificationintent.AudienceKindAdminEmail, intent.AudienceKind)
require.Empty(t, intent.RecipientUserIDs)
require.NoError(t, intent.Validate())
require.Contains(t, intent.PayloadJSON, `"game_id":"game-1"`)
require.Contains(t, intent.PayloadJSON, `"game_name":"Nebula Clash"`)
require.Contains(t, intent.PayloadJSON, `"failure_reason":"engine_timeout"`)
})
t.Run("audience and channel matrix", func(t *testing.T) {
t.Parallel()
userTypes := []notificationintent.NotificationType{
notificationintent.NotificationTypeGameTurnReady,
notificationintent.NotificationTypeGameFinished,
}
for _, notificationType := range userTypes {
notificationType := notificationType
t.Run(notificationType.String(), func(t *testing.T) {
t.Parallel()
require.Equal(t, notificationintent.ProducerGameMaster, notificationType.ExpectedProducer())
require.True(t, notificationType.SupportsAudience(notificationintent.AudienceKindUser))
require.False(t, notificationType.SupportsAudience(notificationintent.AudienceKindAdminEmail))
require.True(t, notificationType.SupportsChannel(notificationintent.AudienceKindUser, notificationintent.ChannelPush))
require.True(t, notificationType.SupportsChannel(notificationintent.AudienceKindUser, notificationintent.ChannelEmail))
require.False(t, notificationType.SupportsChannel(notificationintent.AudienceKindAdminEmail, notificationintent.ChannelEmail))
})
}
t.Run("game.generation_failed", func(t *testing.T) {
t.Parallel()
notificationType := notificationintent.NotificationTypeGameGenerationFailed
require.Equal(t, notificationintent.ProducerGameMaster, notificationType.ExpectedProducer())
require.True(t, notificationType.SupportsAudience(notificationintent.AudienceKindAdminEmail))
require.False(t, notificationType.SupportsAudience(notificationintent.AudienceKindUser))
require.True(t, notificationType.SupportsChannel(notificationintent.AudienceKindAdminEmail, notificationintent.ChannelEmail))
require.False(t, notificationType.SupportsChannel(notificationintent.AudienceKindAdminEmail, notificationintent.ChannelPush))
require.False(t, notificationType.SupportsChannel(notificationintent.AudienceKindUser, notificationintent.ChannelEmail))
})
})
}