using SharepointToolbox.Core.Helpers; using SharepointToolbox.Core.Models; namespace SharepointToolbox.Tests.Helpers; /// /// Unit tests for PermissionConsolidator static helper. /// RPT-04: Validates consolidation logic for empty input, single entry, merging, /// case-insensitivity, MakeKey format, the 10-row/7-row scenario, LocationCount, /// and preservation of IsHighPrivilege / IsExternalUser flags. /// public class PermissionConsolidatorTests { // --------------------------------------------------------------------------- // Helper factory — reduces boilerplate across all test methods // --------------------------------------------------------------------------- private static UserAccessEntry MakeEntry( string userLogin = "alice@contoso.com", string siteUrl = "https://contoso.sharepoint.com/sites/hr", string siteTitle = "HR Site", string objectType = "List", string objectTitle = "Documents", string objectUrl = "https://contoso.sharepoint.com/sites/hr/Documents", string permissionLevel = "Contribute", AccessType accessType = AccessType.Direct, string grantedThrough = "Direct Permissions", string userDisplayName = "Alice Smith", bool isHighPrivilege = false, bool isExternalUser = false) { return new UserAccessEntry( userDisplayName, userLogin, siteUrl, siteTitle, objectType, objectTitle, objectUrl, permissionLevel, accessType, grantedThrough, isHighPrivilege, isExternalUser); } // --------------------------------------------------------------------------- // RPT-04-a: Empty input returns empty list // --------------------------------------------------------------------------- [Fact] public void Consolidate_EmptyInput_ReturnsEmptyList() { var result = PermissionConsolidator.Consolidate(Array.Empty()); Assert.Empty(result); } // --------------------------------------------------------------------------- // RPT-04-b: Single entry produces 1 consolidated row with 1 location // --------------------------------------------------------------------------- [Fact] public void Consolidate_SingleEntry_ReturnsOneRowWithOneLocation() { var entry = MakeEntry(); var result = PermissionConsolidator.Consolidate(new[] { entry }); var row = Assert.Single(result); Assert.Single(row.Locations); Assert.Equal("alice@contoso.com", row.UserLogin); } // --------------------------------------------------------------------------- // RPT-04-c: 3 entries with same key (different sites) merge to 1 row with 3 locations // --------------------------------------------------------------------------- [Fact] public void Consolidate_ThreeEntriesSameKey_ReturnsOneRowWithThreeLocations() { var entries = new[] { MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR Site"), MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/fin", siteTitle: "Finance Site"), MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/mkt", siteTitle: "Marketing Site"), }; var result = PermissionConsolidator.Consolidate(entries); Assert.Single(result); Assert.Equal(3, result[0].Locations.Count); } // --------------------------------------------------------------------------- // RPT-04-d: Entries with different keys remain as separate rows // --------------------------------------------------------------------------- [Fact] public void Consolidate_DifferentKeys_RemainSeparateRows() { var entries = new[] { MakeEntry(permissionLevel: "Contribute"), MakeEntry(permissionLevel: "Full Control"), }; var result = PermissionConsolidator.Consolidate(entries); Assert.Equal(2, result.Count); } // --------------------------------------------------------------------------- // RPT-04-e: Case-insensitive key — "ALICE@CONTOSO.COM" and "alice@contoso.com" merge // --------------------------------------------------------------------------- [Fact] public void Consolidate_CaseInsensitiveKey_MergesCorrectly() { var entries = new[] { MakeEntry(userLogin: "ALICE@CONTOSO.COM", siteUrl: "https://contoso.sharepoint.com/sites/hr"), MakeEntry(userLogin: "alice@contoso.com", siteUrl: "https://contoso.sharepoint.com/sites/fin"), }; var result = PermissionConsolidator.Consolidate(entries); Assert.Single(result); Assert.Equal(2, result[0].Locations.Count); } // --------------------------------------------------------------------------- // RPT-04-f: MakeKey produces pipe-delimited lowercase format // --------------------------------------------------------------------------- [Fact] public void MakeKey_ProducesPipeDelimitedLowercaseFormat() { var entry = MakeEntry( userLogin: "Alice@Contoso.com", permissionLevel: "Full Control", accessType: AccessType.Direct, grantedThrough: "Direct Permissions"); var key = PermissionConsolidator.MakeKey(entry); // AccessType.ToString() preserves casing ("Direct"); all string fields are lowercased Assert.Equal("alice@contoso.com|full control|Direct|direct permissions", key); } // --------------------------------------------------------------------------- // RPT-04-g: 10-row input with 3 duplicate pairs produces 7 consolidated rows // --------------------------------------------------------------------------- [Fact] public void Consolidate_TenRowsWithThreeDuplicatePairs_ReturnsSevenRows() { var entries = new[] { // alice / Contribute / Direct — 3 entries -> merges to 1 MakeEntry(userLogin: "alice@contoso.com", permissionLevel: "Contribute", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), MakeEntry(userLogin: "alice@contoso.com", permissionLevel: "Contribute", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/fin", siteTitle: "Finance"), MakeEntry(userLogin: "alice@contoso.com", permissionLevel: "Contribute", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/mkt", siteTitle: "Marketing"), // bob / Full Control / Group — 2 entries -> merges to 1 MakeEntry(userLogin: "bob@contoso.com", userDisplayName: "Bob Jones", permissionLevel: "Full Control", accessType: AccessType.Group, grantedThrough: "SharePoint Group: Owners", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), MakeEntry(userLogin: "bob@contoso.com", userDisplayName: "Bob Jones", permissionLevel: "Full Control", accessType: AccessType.Group, grantedThrough: "SharePoint Group: Owners", siteUrl: "https://contoso.sharepoint.com/sites/fin", siteTitle: "Finance"), // carol / Read / Inherited — 2 entries -> merges to 1 MakeEntry(userLogin: "carol@contoso.com", userDisplayName: "Carol White", permissionLevel: "Read", accessType: AccessType.Inherited, grantedThrough: "Inherited Permissions", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), MakeEntry(userLogin: "carol@contoso.com", userDisplayName: "Carol White", permissionLevel: "Read", accessType: AccessType.Inherited, grantedThrough: "Inherited Permissions", siteUrl: "https://contoso.sharepoint.com/sites/fin", siteTitle: "Finance"), // alice / Full Control / Direct — different key from alice's Contribute -> unique row MakeEntry(userLogin: "alice@contoso.com", permissionLevel: "Full Control", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), // dave — unique MakeEntry(userLogin: "dave@contoso.com", userDisplayName: "Dave Brown", permissionLevel: "Contribute", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), // eve — unique MakeEntry(userLogin: "eve@contoso.com", userDisplayName: "Eve Green", permissionLevel: "Read", accessType: AccessType.Direct, grantedThrough: "Direct Permissions", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), // frank — unique (4th unique row) MakeEntry(userLogin: "frank@contoso.com", userDisplayName: "Frank Black", permissionLevel: "Contribute", accessType: AccessType.Group, grantedThrough: "SharePoint Group: Members", siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), }; var result = PermissionConsolidator.Consolidate(entries); // 3 merged groups (alice-Contribute 3->1, bob 2->1, carol 2->1) + 4 unique rows // (alice-FullControl, dave, eve, frank) = 7 total Assert.Equal(7, result.Count); } // --------------------------------------------------------------------------- // RPT-04-h: LocationCount property equals Locations.Count for a merged entry // --------------------------------------------------------------------------- [Fact] public void Consolidate_MergedEntry_LocationCountMatchesLocationsCount() { var entries = new[] { MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/hr", siteTitle: "HR"), MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/fin", siteTitle: "Finance"), MakeEntry(siteUrl: "https://contoso.sharepoint.com/sites/mkt", siteTitle: "Marketing"), }; var result = PermissionConsolidator.Consolidate(entries); Assert.Single(result); Assert.Equal(result[0].Locations.Count, result[0].LocationCount); Assert.Equal(3, result[0].LocationCount); } // --------------------------------------------------------------------------- // RPT-04-i: IsHighPrivilege and IsExternalUser from first entry are preserved // --------------------------------------------------------------------------- [Fact] public void Consolidate_PreservesIsHighPrivilegeAndIsExternalUser() { var entries = new[] { MakeEntry(isHighPrivilege: true, isExternalUser: true, siteUrl: "https://contoso.sharepoint.com/sites/hr"), MakeEntry(isHighPrivilege: false, isExternalUser: false, siteUrl: "https://contoso.sharepoint.com/sites/fin"), }; var result = PermissionConsolidator.Consolidate(entries); Assert.Single(result); Assert.True(result[0].IsHighPrivilege); Assert.True(result[0].IsExternalUser); } }