using KArtSell.Modules.ModelOperations.Domain; using Xunit; namespace KArtSell.ModelOperations.UnitTests; public class VS02_SecurityMasterPolicyTests { [Fact] public void ResolveSyncConflict_LocalVersionAhead_ReturnsIdempotent() { var state = new SyncState( LocalVersion: 5, RemoteVersion: 3, LocalRules: new(), RemoteRules: new(), IdempotencyKey: "key-123", CorrelationId: "corr-456"); var result = SecurityMasterPolicy.ResolveSyncConflict(state); Assert.True(result.IsSuccess); Assert.Equal(5, result.NewVersion); Assert.Empty(result.AppliedRules); } [Fact] public void ResolveSyncConflict_VersionsMatch_ReturnsIdempotent() { var state = new SyncState( LocalVersion: 5, RemoteVersion: 5, LocalRules: new(), RemoteRules: new(), IdempotencyKey: "key-123", CorrelationId: "corr-456"); var result = SecurityMasterPolicy.ResolveSyncConflict(state); Assert.True(result.IsSuccess); Assert.Equal(5, result.NewVersion); } [Fact] public void ResolveSyncConflict_RemoteAhead_AppliesNewRules() { var remoteRule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow, ExpiresAt: null, PublishedAt: DateTime.UtcNow, CorrelationId: "corr-456"); var state = new SyncState( LocalVersion: 1, RemoteVersion: 2, LocalRules: new(), RemoteRules: new() { remoteRule }, IdempotencyKey: "key-123", CorrelationId: "corr-456"); var result = SecurityMasterPolicy.ResolveSyncConflict(state); Assert.True(result.IsSuccess); Assert.Equal(2, result.NewVersion); Assert.Single(result.AppliedRules); Assert.Equal(remoteRule.RuleId, result.AppliedRules[0].RuleId); } [Fact] public void ResolveSyncConflict_LastWriteWins_UsesNewerTimestamp() { var ruleId = Guid.NewGuid(); var olderTime = DateTime.UtcNow.AddMinutes(-5); var newerTime = DateTime.UtcNow; var localRule = new SecurityRule( RuleId: ruleId, ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow, ExpiresAt: null, PublishedAt: olderTime, CorrelationId: "corr-456"); var remoteRule = new SecurityRule( RuleId: ruleId, ResourceName: "api/users", Action: "write", Version: 2, EffectiveAt: DateTime.UtcNow, ExpiresAt: null, PublishedAt: newerTime, CorrelationId: "corr-456"); var state = new SyncState( LocalVersion: 1, RemoteVersion: 2, LocalRules: new() { localRule }, RemoteRules: new() { remoteRule }, IdempotencyKey: "key-123", CorrelationId: "corr-456"); var result = SecurityMasterPolicy.ResolveSyncConflict(state); Assert.True(result.IsSuccess); Assert.Single(result.AppliedRules); Assert.Equal("write", result.AppliedRules[0].Action); } [Fact] public void ValidateRule_ValidRule_ReturnsTrue() { var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow, ExpiresAt: DateTime.UtcNow.AddDays(30), PublishedAt: DateTime.UtcNow, CorrelationId: "corr-456"); var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule); Assert.True(isValid); Assert.Empty(errors); } [Fact] public void ValidateRule_InvalidAction_ReturnsFalse() { var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "DELETE", Version: 1, EffectiveAt: DateTime.UtcNow, ExpiresAt: null, PublishedAt: DateTime.UtcNow, CorrelationId: "corr-456"); var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule); Assert.False(isValid); Assert.Contains("Action must be one of", errors[0]); } [Fact] public void ValidateRule_ExpiresBeforeEffective_ReturnsFalse() { var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow.AddDays(10), ExpiresAt: DateTime.UtcNow.AddDays(5), PublishedAt: DateTime.UtcNow, CorrelationId: "corr-456"); var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule); Assert.False(isValid); Assert.Contains("EffectiveAt must be before", errors[0]); } [Fact] public void IsRuleActive_BeforeEffectiveTime_ReturnsFalse() { var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow.AddDays(1), ExpiresAt: null, PublishedAt: DateTime.UtcNow, CorrelationId: "corr-456"); var isActive = SecurityMasterPolicy.IsRuleActive(rule, DateTime.UtcNow); Assert.False(isActive); } [Fact] public void IsRuleActive_AfterExpiryTime_ReturnsFalse() { var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: DateTime.UtcNow.AddDays(-1), ExpiresAt: DateTime.UtcNow.AddMinutes(-1), PublishedAt: DateTime.UtcNow.AddDays(-1), CorrelationId: "corr-456"); var isActive = SecurityMasterPolicy.IsRuleActive(rule, DateTime.UtcNow); Assert.False(isActive); } [Fact] public void IsRuleActive_WithinWindow_ReturnsTrue() { var now = DateTime.UtcNow; var rule = new SecurityRule( RuleId: Guid.NewGuid(), ResourceName: "api/users", Action: "read", Version: 1, EffectiveAt: now.AddHours(-1), ExpiresAt: now.AddHours(1), PublishedAt: now.AddDays(-1), CorrelationId: "corr-456"); var isActive = SecurityMasterPolicy.IsRuleActive(rule, now); Assert.True(isActive); } [Fact] public void CreateIdempotencyKey_FormatsCorrectly() { var key = SecurityMasterPolicy.CreateIdempotencyKey(5, "corr-123"); Assert.Equal("sync-5-corr-123", key); } [Fact] public void RequiresRollback_NoRulesAppliedButVersionIncremented_ReturnsTrue() { var requiresRollback = SecurityMasterPolicy.RequiresRollback(0, 1); Assert.True(requiresRollback); } [Fact] public void RequiresRollback_RulesApplied_ReturnsFalse() { var requiresRollback = SecurityMasterPolicy.RequiresRollback(5, 1); Assert.False(requiresRollback); } }