---
phase: 08-simplified-permissions
plan: 06
type: execute
wave: 5
depends_on: ["08-05"]
files_modified:
  - SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs
  - SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs
  - SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs
autonomous: true
requirements:
  - SIMP-01
  - SIMP-02
  - SIMP-03
must_haves:
  truths:
    - "PermissionLevelMapping maps all known role names correctly and handles unknown roles"
    - "PermissionSummaryBuilder produces 4 risk-level groups with correct counts"
    - "PermissionsViewModel toggle behavior is verified: IsSimplifiedMode rebuilds data, IsDetailView switches without re-scan"
    - "SimplifiedPermissionEntry wraps PermissionEntry correctly with computed labels and risk levels"
  artifacts:
    - path: "SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs"
      provides: "Unit tests for permission level mapping"
      contains: "class PermissionLevelMappingTests"
    - path: "SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs"
      provides: "Unit tests for summary aggregation"
      contains: "class PermissionSummaryBuilderTests"
    - path: "SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs"
      provides: "Extended ViewModel tests for simplified mode"
      contains: "IsSimplifiedMode"
  key_links:
    - from: "SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs"
      to: "SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs"
      via: "Direct static method calls"
      pattern: "PermissionLevelMapping\\.Get"
    - from: "SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs"
      to: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
      via: "Test constructor + property assertions"
      pattern: "IsSimplifiedMode"
---

<objective>
Add unit tests for the simplified permissions feature: PermissionLevelMapping, PermissionSummaryBuilder, SimplifiedPermissionEntry wrapping, and PermissionsViewModel toggle behavior.

Purpose: Validates the core logic of all three SIMP requirements. Mapping correctness (SIMP-01), summary aggregation (SIMP-02), and toggle-without-rescan behavior (SIMP-03).
Output: PermissionLevelMappingTests.cs, PermissionSummaryBuilderTests.cs, updated PermissionsViewModelTests.cs
</objective>

<execution_context>
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/08-simplified-permissions/08-01-SUMMARY.md
@.planning/phases/08-simplified-permissions/08-02-SUMMARY.md

<interfaces>
<!-- Types under test -->
From PermissionLevelMapping:
```csharp
public static class PermissionLevelMapping
{
    public record MappingResult(string Label, RiskLevel RiskLevel);
    public static MappingResult GetMapping(string roleName);
    public static IReadOnlyList<MappingResult> GetMappings(string permissionLevels);
    public static RiskLevel GetHighestRisk(string permissionLevels);
    public static string GetSimplifiedLabels(string permissionLevels);
}
```

From PermissionSummaryBuilder:
```csharp
public static class PermissionSummaryBuilder
{
    public static IReadOnlyList<PermissionSummary> Build(IEnumerable<SimplifiedPermissionEntry> entries);
}
```

From SimplifiedPermissionEntry:
```csharp
public class SimplifiedPermissionEntry
{
    public PermissionEntry Inner { get; }
    public string SimplifiedLabels { get; }
    public RiskLevel RiskLevel { get; }
    public static IReadOnlyList<SimplifiedPermissionEntry> WrapAll(IEnumerable<PermissionEntry> entries);
}
```

<!-- Existing test pattern (from PermissionsViewModelTests.cs) -->
```csharp
public class PermissionsViewModelTests
{
    [Fact]
    public async Task StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl()
    {
        var vm = new PermissionsViewModel(
            mockPermissionsService.Object,
            mockSiteListService.Object,
            mockSessionManager.Object,
            new NullLogger<FeatureViewModelBase>());
        // ... test ...
    }
}
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create PermissionLevelMapping and PermissionSummaryBuilder tests</name>
  <files>SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs, SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs</files>
  <action>
    Create `SharepointToolbox.Tests/Helpers/PermissionLevelMappingTests.cs`:

    ```csharp
    using SharepointToolbox.Core.Helpers;
    using SharepointToolbox.Core.Models;

    namespace SharepointToolbox.Tests.Helpers;

    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);
        }
    }
    ```

    Create `SharepointToolbox.Tests/Models/PermissionSummaryBuilderTests.cs`:

    ```csharp
    using SharepointToolbox.Core.Models;

    namespace SharepointToolbox.Tests.Models;

    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<SimplifiedPermissionEntry>());

            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);
        }
    }
    ```

    Create the `SharepointToolbox.Tests/Helpers/` and `SharepointToolbox.Tests/Models/` directories if they don't exist.
  </action>
  <verify>
    <automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet test SharepointToolbox.Tests/ --filter "PermissionLevelMappingTests|PermissionSummaryBuilderTests" --no-restore 2>&1 | tail -10</automated>
  </verify>
  <done>PermissionLevelMappingTests covers: all 11 known roles, unknown role fallback, case insensitivity, semicolon splitting, highest risk, simplified labels. PermissionSummaryBuilderTests covers: 4 risk levels, empty input, distinct user counting, SimplifiedPermissionEntry wrapping. All tests pass.</done>
</task>

<task type="auto">
  <name>Task 2: Add simplified mode tests to PermissionsViewModelTests</name>
  <files>SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs</files>
  <action>
    Add the following test methods to the existing `PermissionsViewModelTests` class in `SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs`. Add any needed using statements at the top:

    ```csharp
    using CommunityToolkit.Mvvm.Messaging;
    ```

    Add a helper method and new tests after the existing test:

    ```csharp
    /// <summary>
    /// Creates a PermissionsViewModel with mocked services and pre-populated results.
    /// </summary>
    private static PermissionsViewModel CreateViewModelWithResults(IReadOnlyList<PermissionEntry> results)
    {
        var mockPermissionsService = new Mock<IPermissionsService>();
        mockPermissionsService
            .Setup(s => s.ScanSiteAsync(
                It.IsAny<ClientContext>(),
                It.IsAny<ScanOptions>(),
                It.IsAny<IProgress<OperationProgress>>(),
                It.IsAny<CancellationToken>()))
            .ReturnsAsync(results.ToList());

        var mockSiteListService = new Mock<ISiteListService>();

        var mockSessionManager = new Mock<ISessionManager>();
        mockSessionManager
            .Setup(s => s.GetOrCreateContextAsync(It.IsAny<TenantProfile>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((ClientContext)null!);

        var vm = new PermissionsViewModel(
            mockPermissionsService.Object,
            mockSiteListService.Object,
            mockSessionManager.Object,
            new NullLogger<FeatureViewModelBase>());

        return vm;
    }

    [Fact]
    public void IsSimplifiedMode_Default_IsFalse()
    {
        WeakReferenceMessenger.Default.Reset();
        var vm = CreateViewModelWithResults(Array.Empty<PermissionEntry>());
        Assert.False(vm.IsSimplifiedMode);
    }

    [Fact]
    public async Task IsSimplifiedMode_WhenToggled_RebuildSimplifiedResults()
    {
        WeakReferenceMessenger.Default.Reset();
        var entries = new List<PermissionEntry>
        {
            new("Site", "Test", "https://test.sharepoint.com", true, "User1", "user1@test.com", "Full Control", "Direct Permissions", "User"),
            new("List", "Docs", "https://test.sharepoint.com/docs", false, "User2", "user2@test.com", "Read", "Direct Permissions", "User"),
        };

        var vm = CreateViewModelWithResults(entries);

        // Simulate scan completing
        vm.SelectedSites.Add(new SiteInfo("https://test.sharepoint.com", "Test"));
        vm.SetCurrentProfile(new TenantProfile { Name = "Test", TenantUrl = "https://test.sharepoint.com", ClientId = "cid" });
        await vm.TestRunOperationAsync(CancellationToken.None, new Progress<OperationProgress>());

        // Before toggle: simplified results empty
        Assert.Empty(vm.SimplifiedResults);

        // Toggle on
        vm.IsSimplifiedMode = true;

        // After toggle: simplified results populated
        Assert.Equal(2, vm.SimplifiedResults.Count);
        Assert.Equal(4, vm.Summaries.Count);
    }

    [Fact]
    public async Task IsDetailView_Toggle_DoesNotChangeCounts()
    {
        WeakReferenceMessenger.Default.Reset();
        var entries = new List<PermissionEntry>
        {
            new("Site", "Test", "https://test.sharepoint.com", true, "User1", "user1@test.com", "Contribute", "Direct Permissions", "User"),
        };

        var vm = CreateViewModelWithResults(entries);
        vm.SelectedSites.Add(new SiteInfo("https://test.sharepoint.com", "Test"));
        vm.SetCurrentProfile(new TenantProfile { Name = "Test", TenantUrl = "https://test.sharepoint.com", ClientId = "cid" });
        await vm.TestRunOperationAsync(CancellationToken.None, new Progress<OperationProgress>());

        vm.IsSimplifiedMode = true;
        var countBefore = vm.SimplifiedResults.Count;

        vm.IsDetailView = false;
        Assert.Equal(countBefore, vm.SimplifiedResults.Count);  // No re-computation

        vm.IsDetailView = true;
        Assert.Equal(countBefore, vm.SimplifiedResults.Count);  // Still the same
    }

    [Fact]
    public async Task Summaries_ContainsCorrectRiskBreakdown()
    {
        WeakReferenceMessenger.Default.Reset();
        var entries = new List<PermissionEntry>
        {
            new("Site", "S1", "https://s1", true, "Admin", "admin@t.com", "Full Control", "Direct", "User"),
            new("Site", "S2", "https://s2", true, "Editor", "ed@t.com", "Contribute", "Direct", "User"),
            new("List", "L1", "https://l1", false, "Reader", "read@t.com", "Read", "Direct", "User"),
        };

        var vm = CreateViewModelWithResults(entries);
        vm.SelectedSites.Add(new SiteInfo("https://s1", "S1"));
        vm.SetCurrentProfile(new TenantProfile { Name = "Test", TenantUrl = "https://s1", ClientId = "cid" });
        await vm.TestRunOperationAsync(CancellationToken.None, new Progress<OperationProgress>());

        vm.IsSimplifiedMode = true;

        var high = vm.Summaries.Single(s => s.RiskLevel == RiskLevel.High);
        var medium = vm.Summaries.Single(s => s.RiskLevel == RiskLevel.Medium);
        var low = vm.Summaries.Single(s => s.RiskLevel == RiskLevel.Low);

        Assert.Equal(1, high.Count);
        Assert.Equal(1, medium.Count);
        Assert.Equal(1, low.Count);
    }
    ```

    Add the RiskLevel using statement:
    ```csharp
    using SharepointToolbox.Core.Models;  // Already present (for PermissionEntry)
    ```
  </action>
  <verify>
    <automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet test SharepointToolbox.Tests/ --filter "PermissionsViewModelTests" --no-restore 2>&1 | tail -10</automated>
  </verify>
  <done>PermissionsViewModelTests has 5 tests total (1 existing + 4 new). Tests verify: IsSimplifiedMode default false, toggle rebuilds SimplifiedResults, IsDetailView toggle doesn't re-compute, Summaries has correct risk breakdown. All tests pass.</done>
</task>

</tasks>

<verification>
- `dotnet test SharepointToolbox.Tests/ --no-restore` passes all tests
- PermissionLevelMappingTests: 9 test methods covering known roles, unknown fallback, case insensitivity, splitting, risk ranking
- PermissionSummaryBuilderTests: 4 test methods covering risk levels, empty input, distinct users, wrapping
- PermissionsViewModelTests: 5 test methods (1 existing + 4 new) covering simplified mode toggle, detail toggle, summary breakdown
</verification>

<success_criteria>
All simplified permissions logic is covered by automated tests. Mapping correctness (SIMP-01), summary aggregation (SIMP-02), and toggle-without-rescan behavior (SIMP-03) are all verified. The test suite catches regressions in the core mapping layer and ViewModel behavior.
</success_criteria>

<output>
After completion, create `.planning/phases/08-simplified-permissions/08-06-SUMMARY.md`
</output>
