using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.SharePoint.Client;
using Moq;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services;
using SharepointToolbox.ViewModels;
using SharepointToolbox.ViewModels.Tabs;
namespace SharepointToolbox.Tests.ViewModels;
///
/// Unit tests for PermissionsViewModel.
/// PERM-02: multi-site scan loop invokes ScanSiteAsync once per URL.
///
public class PermissionsViewModelTests
{
[Fact]
public async Task StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl()
{
// Arrange
var mockPermissionsService = new Mock();
mockPermissionsService
.Setup(s => s.ScanSiteAsync(
It.IsAny(),
It.IsAny(),
It.IsAny>(),
It.IsAny()))
.ReturnsAsync(new List());
var mockSiteListService = new Mock();
var mockSessionManager = new Mock();
mockSessionManager
.Setup(s => s.GetOrCreateContextAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync((ClientContext)null!);
var vm = new PermissionsViewModel(
mockPermissionsService.Object,
mockSiteListService.Object,
mockSessionManager.Object,
new NullLogger());
// Set up two site URLs via SelectedSites
vm.SelectedSites.Add(new SiteInfo("https://tenant1.sharepoint.com/sites/alpha", "Alpha"));
vm.SelectedSites.Add(new SiteInfo("https://tenant1.sharepoint.com/sites/beta", "Beta"));
vm.SetCurrentProfile(new TenantProfile
{
Name = "Test",
TenantUrl = "https://tenant1.sharepoint.com",
ClientId = "client-id"
});
// Act
await vm.TestRunOperationAsync(CancellationToken.None, new Progress());
// Assert: ScanSiteAsync called exactly twice (once per URL)
mockPermissionsService.Verify(
s => s.ScanSiteAsync(
It.IsAny(),
It.IsAny(),
It.IsAny>(),
It.IsAny()),
Times.Exactly(2));
}
///
/// Creates a PermissionsViewModel with mocked services where ScanSiteAsync returns the given results.
///
private static PermissionsViewModel CreateViewModelWithResults(IReadOnlyList results)
{
var mockPermissionsService = new Mock();
mockPermissionsService
.Setup(s => s.ScanSiteAsync(
It.IsAny(),
It.IsAny(),
It.IsAny>(),
It.IsAny()))
.ReturnsAsync(results.ToList());
var mockSiteListService = new Mock();
var mockSessionManager = new Mock();
mockSessionManager
.Setup(s => s.GetOrCreateContextAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync((ClientContext)null!);
var vm = new PermissionsViewModel(
mockPermissionsService.Object,
mockSiteListService.Object,
mockSessionManager.Object,
new NullLogger());
return vm;
}
[Fact]
public void IsSimplifiedMode_Default_IsFalse()
{
WeakReferenceMessenger.Default.Reset();
var vm = CreateViewModelWithResults(Array.Empty());
Assert.False(vm.IsSimplifiedMode);
}
[Fact]
public async Task IsSimplifiedMode_WhenToggled_RebuildsSimplifiedResults()
{
WeakReferenceMessenger.Default.Reset();
var entries = new List
{
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());
// 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
{
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());
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
{
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());
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);
}
}