package policy import ( "testing" "time" "galaxy/user/internal/domain/common" "github.com/stretchr/testify/require" ) func TestSanctionRecordValidateAt(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() expiresAt := now.Add(time.Hour) removedAt := now.Add(30 * time.Minute) tests := []struct { name string record SanctionRecord wantErr bool wantActive bool }{ { name: "active", record: SanctionRecord{ RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy_blocked"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-time.Minute), ExpiresAt: &expiresAt, }, wantActive: true, }, { name: "expired", record: SanctionRecord{ RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy_blocked"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-2 * time.Hour), ExpiresAt: ptrTime(now.Add(-time.Minute)), }, }, { name: "removed", record: SanctionRecord{ RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy_blocked"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-time.Hour), RemovedAt: &removedAt, RemovedBy: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-2")}, RemovedReasonCode: common.ReasonCode("manual_remove"), }, }, { name: "future applied at", record: SanctionRecord{ RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy_blocked"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(time.Minute), }, wantErr: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() err := tt.record.ValidateAt(now) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tt.wantActive, tt.record.IsActiveAt(now)) }) } } func TestActiveSanctionsAt(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() records := []SanctionRecord{ { RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeProfileUpdateBlock, Scope: common.Scope("profile"), ReasonCode: common.ReasonCode("moderation"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-time.Hour), }, { RecordID: SanctionRecordID("sanction-2"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-2")}, AppliedAt: now.Add(-2 * time.Hour), ExpiresAt: ptrTime(now.Add(-time.Minute)), }, } active, err := ActiveSanctionsAt(records, now) require.NoError(t, err) require.Len(t, active, 1) require.Equal(t, SanctionCodeProfileUpdateBlock, active[0].SanctionCode) } func TestSanctionCodeCatalog(t *testing.T) { t.Parallel() require.True(t, SanctionCodeLoginBlock.IsKnown()) require.True(t, SanctionCodePrivateGameCreateBlock.IsKnown()) require.True(t, SanctionCodePrivateGameManageBlock.IsKnown()) require.True(t, SanctionCodeGameJoinBlock.IsKnown()) require.True(t, SanctionCodeProfileUpdateBlock.IsKnown()) require.True(t, SanctionCodePermanentBlock.IsKnown()) require.False(t, SanctionCode("unknown_code").IsKnown()) } func TestActiveSanctionsAtPermanentBlockCoexistsWithOtherCodes(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() records := []SanctionRecord{ { RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodePermanentBlock, Scope: common.Scope("platform"), ReasonCode: common.ReasonCode("terminal_policy_violation"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-time.Hour), }, { RecordID: SanctionRecordID("sanction-2"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-2 * time.Hour), }, } active, err := ActiveSanctionsAt(records, now) require.NoError(t, err) require.Len(t, active, 2) require.Equal(t, SanctionCodeLoginBlock, active[0].SanctionCode) require.Equal(t, SanctionCodePermanentBlock, active[1].SanctionCode) } func TestActiveSanctionsAtDuplicateActiveCode(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() _, err := ActiveSanctionsAt([]SanctionRecord{ { RecordID: SanctionRecordID("sanction-1"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-time.Hour), }, { RecordID: SanctionRecordID("sanction-2"), UserID: common.UserID("user-123"), SanctionCode: SanctionCodeLoginBlock, Scope: common.Scope("auth"), ReasonCode: common.ReasonCode("policy"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-2 * time.Hour), }, }, now) require.Error(t, err) } func TestLimitRecordValidateAtAndActiveLimits(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() record := LimitRecord{ RecordID: LimitRecordID("limit-1"), UserID: common.UserID("user-123"), LimitCode: LimitCodeMaxOwnedPrivateGames, Value: 3, ReasonCode: common.ReasonCode("manual_override"), Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")}, AppliedAt: now.Add(-time.Minute), } require.NoError(t, record.ValidateAt(now)) require.True(t, record.IsActiveAt(now)) active, err := ActiveLimitsAt([]LimitRecord{ record, { RecordID: LimitRecordID("limit-2"), UserID: common.UserID("user-123"), LimitCode: LimitCodeMaxActivePrivateGames, Value: 7, ReasonCode: common.ReasonCode("manual_override"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-time.Hour), }, }, now) require.NoError(t, err) require.Len(t, active, 1) require.Equal(t, LimitCodeMaxOwnedPrivateGames, active[0].LimitCode) } func TestLimitCodeSupportAndRetiredRecognition(t *testing.T) { t.Parallel() require.True(t, LimitCodeMaxOwnedPrivateGames.IsSupported()) require.True(t, LimitCodeMaxPendingPublicApplications.IsSupported()) require.True(t, LimitCodeMaxActiveGameMemberships.IsSupported()) require.True(t, LimitCodeMaxActivePrivateGames.IsRetired()) require.True(t, LimitCodeMaxPendingPrivateJoinRequests.IsRetired()) require.True(t, LimitCodeMaxPendingPrivateInvitesSent.IsRetired()) require.True(t, LimitCodeMaxActivePrivateGames.IsRecognized()) require.False(t, LimitCode("unknown_limit").IsRecognized()) require.False(t, LimitCodeMaxActivePrivateGames.IsKnown()) } func TestActiveLimitsAtDuplicateActiveCode(t *testing.T) { t.Parallel() now := time.Unix(1_775_240_000, 0).UTC() _, err := ActiveLimitsAt([]LimitRecord{ { RecordID: LimitRecordID("limit-1"), UserID: common.UserID("user-123"), LimitCode: LimitCodeMaxOwnedPrivateGames, Value: 2, ReasonCode: common.ReasonCode("manual_override"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-time.Hour), }, { RecordID: LimitRecordID("limit-2"), UserID: common.UserID("user-123"), LimitCode: LimitCodeMaxOwnedPrivateGames, Value: 5, ReasonCode: common.ReasonCode("manual_override"), Actor: common.ActorRef{Type: common.ActorType("admin")}, AppliedAt: now.Add(-2 * time.Hour), }, }, now) require.Error(t, err) } func ptrTime(value time.Time) *time.Time { return &value }