diff --git a/SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs b/SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs
new file mode 100644
index 0000000..ac6f035
--- /dev/null
+++ b/SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs
@@ -0,0 +1,87 @@
+using SharepointToolbox.Core.Helpers;
+using SharepointToolbox.Core.Models;
+
+namespace SharepointToolbox.Tests.Helpers;
+
+///
+/// Unit tests for PermissionLevelMapping static helper.
+/// SIMP-01: Validates mapping correctness for known roles, unknown fallback,
+/// case insensitivity, semicolon splitting, risk ranking, and label generation.
+///
+public class PermissionLevelMappingTests
+{
+ [Theory]
+ [InlineData("Full Control", RiskLevel.High)]
+ [InlineData("Site Collection Administrator", RiskLevel.High)]
+ [InlineData("Contribute", RiskLevel.Medium)]
+ [InlineData("Edit", RiskLevel.Medium)]
+ [InlineData("Design", RiskLevel.Medium)]
+ [InlineData("Approve", RiskLevel.Medium)]
+ [InlineData("Manage Hierarchy", RiskLevel.Medium)]
+ [InlineData("Read", RiskLevel.Low)]
+ [InlineData("Restricted Read", RiskLevel.Low)]
+ [InlineData("View Only", RiskLevel.ReadOnly)]
+ [InlineData("Restricted View", RiskLevel.ReadOnly)]
+ public void GetMapping_KnownRoles_ReturnsCorrectRiskLevel(string roleName, RiskLevel expected)
+ {
+ var result = PermissionLevelMapping.GetMapping(roleName);
+ Assert.Equal(expected, result.RiskLevel);
+ Assert.NotEmpty(result.Label);
+ }
+
+ [Fact]
+ public void GetMapping_UnknownRole_ReturnsMediumRiskWithRawName()
+ {
+ var result = PermissionLevelMapping.GetMapping("Custom Permission Level");
+ Assert.Equal(RiskLevel.Medium, result.RiskLevel);
+ Assert.Equal("Custom Permission Level", result.Label);
+ }
+
+ [Fact]
+ public void GetMapping_CaseInsensitive()
+ {
+ var lower = PermissionLevelMapping.GetMapping("full control");
+ var upper = PermissionLevelMapping.GetMapping("FULL CONTROL");
+ Assert.Equal(RiskLevel.High, lower.RiskLevel);
+ Assert.Equal(RiskLevel.High, upper.RiskLevel);
+ }
+
+ [Fact]
+ public void GetMappings_SemicolonDelimited_SplitsAndMaps()
+ {
+ var results = PermissionLevelMapping.GetMappings("Full Control; Read");
+ Assert.Equal(2, results.Count);
+ Assert.Equal(RiskLevel.High, results[0].RiskLevel);
+ Assert.Equal(RiskLevel.Low, results[1].RiskLevel);
+ }
+
+ [Fact]
+ public void GetMappings_EmptyString_ReturnsEmpty()
+ {
+ var results = PermissionLevelMapping.GetMappings("");
+ Assert.Empty(results);
+ }
+
+ [Fact]
+ public void GetHighestRisk_MultipleLevels_ReturnsHighest()
+ {
+ // Full Control (High) + Read (Low) => High
+ var risk = PermissionLevelMapping.GetHighestRisk("Full Control; Read");
+ Assert.Equal(RiskLevel.High, risk);
+ }
+
+ [Fact]
+ public void GetHighestRisk_SingleReadOnly_ReturnsReadOnly()
+ {
+ var risk = PermissionLevelMapping.GetHighestRisk("View Only");
+ Assert.Equal(RiskLevel.ReadOnly, risk);
+ }
+
+ [Fact]
+ public void GetSimplifiedLabels_JoinsLabels()
+ {
+ var labels = PermissionLevelMapping.GetSimplifiedLabels("Contribute; Read");
+ Assert.Contains("Can edit files and list items", labels);
+ Assert.Contains("Can view files and pages", labels);
+ }
+}
diff --git a/SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs b/SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs
new file mode 100644
index 0000000..6c4724b
--- /dev/null
+++ b/SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs
@@ -0,0 +1,82 @@
+using SharepointToolbox.Core.Models;
+
+namespace SharepointToolbox.Tests.Models;
+
+///
+/// Unit tests for PermissionSummaryBuilder and SimplifiedPermissionEntry.
+/// SIMP-02: Validates summary aggregation, risk-level grouping, distinct user counting,
+/// and SimplifiedPermissionEntry wrapping behavior.
+///
+public class PermissionSummaryBuilderTests
+{
+ private static PermissionEntry MakeEntry(string permLevels, string users = "User1", string logins = "user1@test.com") =>
+ new PermissionEntry(
+ ObjectType: "Site",
+ Title: "Test",
+ Url: "https://test.sharepoint.com",
+ HasUniquePermissions: true,
+ Users: users,
+ UserLogins: logins,
+ PermissionLevels: permLevels,
+ GrantedThrough: "Direct Permissions",
+ PrincipalType: "User");
+
+ [Fact]
+ public void Build_ReturnsAllFourRiskLevels()
+ {
+ var entries = SimplifiedPermissionEntry.WrapAll(new[]
+ {
+ MakeEntry("Full Control"),
+ MakeEntry("Contribute"),
+ MakeEntry("Read"),
+ MakeEntry("View Only")
+ });
+
+ var summaries = PermissionSummaryBuilder.Build(entries);
+
+ Assert.Equal(4, summaries.Count);
+ Assert.Contains(summaries, s => s.RiskLevel == RiskLevel.High && s.Count == 1);
+ Assert.Contains(summaries, s => s.RiskLevel == RiskLevel.Medium && s.Count == 1);
+ Assert.Contains(summaries, s => s.RiskLevel == RiskLevel.Low && s.Count == 1);
+ Assert.Contains(summaries, s => s.RiskLevel == RiskLevel.ReadOnly && s.Count == 1);
+ }
+
+ [Fact]
+ public void Build_EmptyCollection_ReturnsZeroCounts()
+ {
+ var summaries = PermissionSummaryBuilder.Build(Array.Empty());
+
+ Assert.Equal(4, summaries.Count);
+ Assert.All(summaries, s => Assert.Equal(0, s.Count));
+ }
+
+ [Fact]
+ public void Build_CountsDistinctUsers()
+ {
+ var entries = SimplifiedPermissionEntry.WrapAll(new[]
+ {
+ MakeEntry("Full Control", "Alice", "alice@test.com"),
+ MakeEntry("Full Control", "Bob", "bob@test.com"),
+ MakeEntry("Full Control", "Alice", "alice@test.com"), // duplicate user
+ });
+
+ var summaries = PermissionSummaryBuilder.Build(entries);
+ var high = summaries.Single(s => s.RiskLevel == RiskLevel.High);
+
+ Assert.Equal(3, high.Count); // 3 entries
+ Assert.Equal(2, high.DistinctUsers); // 2 distinct users
+ }
+
+ [Fact]
+ public void SimplifiedPermissionEntry_WrapAll_PreservesInner()
+ {
+ var original = MakeEntry("Contribute");
+ var wrapped = SimplifiedPermissionEntry.WrapAll(new[] { original });
+
+ Assert.Single(wrapped);
+ Assert.Same(original, wrapped[0].Inner);
+ Assert.Equal("Contribute", wrapped[0].PermissionLevels);
+ Assert.Equal(RiskLevel.Medium, wrapped[0].RiskLevel);
+ Assert.Contains("Can edit", wrapped[0].SimplifiedLabels);
+ }
+}