using SIGCM2.Application.Common; namespace SIGCM2.Application.Tests.Common; /// /// SUITE-B-RESOLVER — R-01 a R-09 (UDT-009) /// Unit tests for PermisoResolver.Resolve static helper. /// Pure unit: no DB, no mocks. /// public sealed class PermisoResolverTests { // R-01: Override vacío → effective = solo rol sin cambios [Fact] public void Resolve_EmptyOverride_ReturnsRolPermisosUnchanged() { var result = PermisoResolver.Resolve(["A", "B"], PermisosOverride.Empty); Assert.Contains("A", result); Assert.Contains("B", result); Assert.Equal(2, result.Count); } // R-02: Grant nuevo permiso → se agrega al set [Fact] public void Resolve_GrantNewPermiso_AddsToEffective() { var overrides = new PermisosOverride(Grant: ["C"], Deny: []); var result = PermisoResolver.Resolve(["A", "B"], overrides); Assert.Contains("A", result); Assert.Contains("B", result); Assert.Contains("C", result); Assert.Equal(3, result.Count); } // R-03: Deny permiso del rol → se quita del set [Fact] public void Resolve_DenyRolPermiso_RemovesFromEffective() { var overrides = new PermisosOverride(Grant: [], Deny: ["A"]); var result = PermisoResolver.Resolve(["A", "B"], overrides); Assert.DoesNotContain("A", result); Assert.Contains("B", result); Assert.Equal(1, result.Count); } // R-04: Grant duplicado (ya en rol) → idempotente, no duplicados [Fact] public void Resolve_GrantDuplicated_Idempotent() { var overrides = new PermisosOverride(Grant: ["B"], Deny: []); var result = PermisoResolver.Resolve(["A", "B"], overrides); Assert.Contains("A", result); Assert.Contains("B", result); Assert.Equal(2, result.Count); // no duplicates } // R-05: Deny código inexistente en rol → no-op [Fact] public void Resolve_DenyNonExistentCode_NoOp() { var overrides = new PermisosOverride(Grant: [], Deny: ["X"]); var result = PermisoResolver.Resolve(["A", "B"], overrides); Assert.Contains("A", result); Assert.Contains("B", result); Assert.Equal(2, result.Count); } // R-06: Grant + Deny combinados [Fact] public void Resolve_GrantAndDeny_Combined() { var overrides = new PermisosOverride(Grant: ["C"], Deny: ["A"]); var result = PermisoResolver.Resolve(["A", "B"], overrides); Assert.DoesNotContain("A", result); Assert.Contains("B", result); Assert.Contains("C", result); Assert.Equal(2, result.Count); } // R-07: PermisosOverride.Empty literal → mismo que rol [Fact] public void Resolve_EmptyLiteral_ReturnsRolPermisosOnly() { var result = PermisoResolver.Resolve(["A", "B"], PermisosOverride.Empty); Assert.Contains("A", result); Assert.Contains("B", result); Assert.Equal(2, result.Count); } // R-08: Rol vacío + grant → effective = solo el grant [Fact] public void Resolve_EmptyRol_WithGrant_ReturnsGrant() { var overrides = new PermisosOverride(Grant: ["C"], Deny: []); var result = PermisoResolver.Resolve([], overrides); Assert.Single(result); Assert.Contains("C", result); } // R-09: Rol vacío + sin overrides → effective vacío [Fact] public void Resolve_EmptyRol_EmptyOverrides_ReturnsEmpty() { var result = PermisoResolver.Resolve([], PermisosOverride.Empty); Assert.Empty(result); } // Extra: Deny gana sobre grant explícito (defense en runtime — validator lo bloquea antes) [Fact] public void Resolve_DenyWinsOver_ExplicitGrant() { // Mismo código en grant y deny → deny gana (algoritmo: grant primero, deny al final) var overrides = new PermisosOverride(Grant: ["C"], Deny: ["C"]); var result = PermisoResolver.Resolve(["A"], overrides); Assert.DoesNotContain("C", result); Assert.Contains("A", result); } }