package policysvc import ( "context" "testing" "time" "galaxy/user/internal/domain/common" "galaxy/user/internal/domain/policy" "galaxy/user/internal/ports" "github.com/stretchr/testify/require" ) func TestApplySanctionServiceExecutePublishesEvent(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() userID := common.UserID("user-123") sanctionStore := newFakeSanctionStore() limitStore := newFakeLimitStore() publisher := &recordingPolicyPublisher{} service, err := NewApplySanctionServiceWithObservability( fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}}, sanctionStore, limitStore, &fakePolicyLifecycleStore{sanctions: sanctionStore, limits: limitStore}, fixedClock{now: now}, fixedIDGenerator{sanctionRecordID: policy.SanctionRecordID("sanction-1")}, nil, nil, publisher, ) require.NoError(t, err) _, err = service.Execute(context.Background(), ApplySanctionInput{ UserID: userID.String(), SanctionCode: string(policy.SanctionCodeLoginBlock), Scope: "auth", ReasonCode: "policy_blocked", Actor: ActorInput{Type: "admin", ID: "admin-1"}, AppliedAt: now.Add(-time.Minute).Format(time.RFC3339Nano), ExpiresAt: now.Add(time.Hour).Format(time.RFC3339Nano), }) require.NoError(t, err) require.Len(t, publisher.sanctionEvents, 1) require.Equal(t, ports.SanctionChangedOperationApplied, publisher.sanctionEvents[0].Operation) require.Equal(t, common.Source("admin_internal_api"), publisher.sanctionEvents[0].Source) } func TestRemoveSanctionServiceExecuteMissingDoesNotPublishEvent(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() userID := common.UserID("user-123") sanctionStore := newFakeSanctionStore() limitStore := newFakeLimitStore() publisher := &recordingPolicyPublisher{} service, err := NewRemoveSanctionServiceWithObservability( fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}}, sanctionStore, limitStore, &fakePolicyLifecycleStore{sanctions: sanctionStore, limits: limitStore}, fixedClock{now: now}, fixedIDGenerator{}, nil, nil, publisher, ) require.NoError(t, err) _, err = service.Execute(context.Background(), RemoveSanctionInput{ UserID: userID.String(), SanctionCode: string(policy.SanctionCodeLoginBlock), ReasonCode: "manual_remove", Actor: ActorInput{Type: "admin", ID: "admin-1"}, }) require.NoError(t, err) require.Empty(t, publisher.sanctionEvents) } func TestSetLimitServiceExecutePublishesEvent(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() userID := common.UserID("user-123") sanctionStore := newFakeSanctionStore() limitStore := newFakeLimitStore() publisher := &recordingPolicyPublisher{} service, err := NewSetLimitServiceWithObservability( fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}}, sanctionStore, limitStore, &fakePolicyLifecycleStore{sanctions: sanctionStore, limits: limitStore}, fixedClock{now: now}, fixedIDGenerator{limitRecordID: policy.LimitRecordID("limit-1")}, nil, nil, publisher, ) require.NoError(t, err) _, err = service.Execute(context.Background(), SetLimitInput{ UserID: userID.String(), LimitCode: string(policy.LimitCodeMaxOwnedPrivateGames), Value: 5, ReasonCode: "manual_override", Actor: ActorInput{Type: "admin", ID: "admin-1"}, AppliedAt: now.Add(-time.Minute).Format(time.RFC3339Nano), ExpiresAt: now.Add(time.Hour).Format(time.RFC3339Nano), }) require.NoError(t, err) require.Len(t, publisher.limitEvents, 1) require.Equal(t, ports.LimitChangedOperationSet, publisher.limitEvents[0].Operation) require.NotNil(t, publisher.limitEvents[0].Value) require.Equal(t, 5, *publisher.limitEvents[0].Value) } func TestRemoveLimitServiceExecuteMissingDoesNotPublishEvent(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() userID := common.UserID("user-123") sanctionStore := newFakeSanctionStore() limitStore := newFakeLimitStore() publisher := &recordingPolicyPublisher{} service, err := NewRemoveLimitServiceWithObservability( fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}}, sanctionStore, limitStore, &fakePolicyLifecycleStore{sanctions: sanctionStore, limits: limitStore}, fixedClock{now: now}, fixedIDGenerator{}, nil, nil, publisher, ) require.NoError(t, err) _, err = service.Execute(context.Background(), RemoveLimitInput{ UserID: userID.String(), LimitCode: string(policy.LimitCodeMaxOwnedPrivateGames), ReasonCode: "manual_remove", Actor: ActorInput{Type: "admin", ID: "admin-1"}, }) require.NoError(t, err) require.Empty(t, publisher.limitEvents) } type recordingPolicyPublisher struct { sanctionEvents []ports.SanctionChangedEvent limitEvents []ports.LimitChangedEvent } func (publisher *recordingPolicyPublisher) PublishSanctionChanged(_ context.Context, event ports.SanctionChangedEvent) error { if err := event.Validate(); err != nil { return err } publisher.sanctionEvents = append(publisher.sanctionEvents, event) return nil } func (publisher *recordingPolicyPublisher) PublishLimitChanged(_ context.Context, event ports.LimitChangedEvent) error { if err := event.Validate(); err != nil { return err } publisher.limitEvents = append(publisher.limitEvents, event) return nil } var ( _ ports.SanctionChangedPublisher = (*recordingPolicyPublisher)(nil) _ ports.LimitChangedPublisher = (*recordingPolicyPublisher)(nil) )