diff --git a/SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelTests.cs b/SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelTests.cs
index d105a01..f3bdcad 100644
--- a/SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelTests.cs
+++ b/SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelTests.cs
@@ -41,8 +41,8 @@ public class UserAccessAuditViewModelTests
string upn = "alice@contoso.com") =>
new(display, upn, upn);
- /// Creates a ViewModel wired with a mock IUserAccessAuditService.
- private static (UserAccessAuditViewModel vm, Mock auditMock)
+ /// Creates a ViewModel wired with mock services.
+ private static (UserAccessAuditViewModel vm, Mock auditMock, Mock graphMock)
CreateViewModel(IReadOnlyList? auditResult = null)
{
var mockAudit = new Mock();
@@ -65,7 +65,7 @@ public class UserAccessAuditViewModelTests
mockSession.Object,
NullLogger.Instance);
- return (vm, mockAudit);
+ return (vm, mockAudit, mockGraph);
}
// ── Test 1: RunOperation calls AuditUsersAsync ────────────────────────────
@@ -73,7 +73,7 @@ public class UserAccessAuditViewModelTests
[Fact]
public async Task RunOperation_calls_AuditUsersAsync()
{
- var (vm, auditMock) = CreateViewModel();
+ var (vm, auditMock, _) = CreateViewModel();
vm.SelectedUsers.Add(MakeUser());
vm.SelectedSites.Add(new SiteInfo("https://contoso.sharepoint.com", "Contoso"));
@@ -102,7 +102,7 @@ public class UserAccessAuditViewModelTests
MakeEntry(userLogin: "bob@contoso.com")
};
- var (vm, _) = CreateViewModel(entries);
+ var (vm, _, _) = CreateViewModel(entries);
vm.SelectedUsers.Add(MakeUser());
vm.SelectedSites.Add(new SiteInfo("https://contoso.sharepoint.com", "Contoso"));
@@ -124,7 +124,7 @@ public class UserAccessAuditViewModelTests
MakeEntry(userLogin: "bob@contoso.com", siteUrl: "https://contoso.sharepoint.com/sites/s1", isHighPrivilege: true)
};
- var (vm, _) = CreateViewModel(entries);
+ var (vm, _, _) = CreateViewModel(entries);
vm.SelectedUsers.Add(MakeUser());
vm.SelectedSites.Add(new SiteInfo("https://contoso.sharepoint.com", "Contoso"));
@@ -142,7 +142,7 @@ public class UserAccessAuditViewModelTests
public async Task OnTenantSwitched_resets_state()
{
var entries = new List { MakeEntry() };
- var (vm, _) = CreateViewModel(entries);
+ var (vm, _, _) = CreateViewModel(entries);
// Populate state
vm.SelectedUsers.Add(MakeUser());
@@ -172,7 +172,7 @@ public class UserAccessAuditViewModelTests
[Fact]
public void OnGlobalSitesChanged_updates_selected_sites()
{
- var (vm, _) = CreateViewModel();
+ var (vm, _, _) = CreateViewModel();
var sites = new List
{
new("https://contoso.sharepoint.com/sites/hr", "HR"),
@@ -190,7 +190,7 @@ public class UserAccessAuditViewModelTests
[Fact]
public void OnGlobalSitesChanged_skipped_when_override()
{
- var (vm, _) = CreateViewModel();
+ var (vm, _, _) = CreateViewModel();
// Add a local site and set the override flag via reflection
var localSite = new SiteInfo("https://contoso.sharepoint.com/sites/local", "Local");
@@ -218,7 +218,7 @@ public class UserAccessAuditViewModelTests
[Fact]
public void CanExport_false_when_no_results()
{
- var (vm, _) = CreateViewModel();
+ var (vm, _, _) = CreateViewModel();
// Results is empty by default
Assert.Empty(vm.Results);
@@ -232,7 +232,7 @@ public class UserAccessAuditViewModelTests
public async Task CanExport_true_when_has_results()
{
var entries = new List { MakeEntry() };
- var (vm, _) = CreateViewModel(entries);
+ var (vm, _, _) = CreateViewModel(entries);
vm.SelectedUsers.Add(MakeUser());
vm.SelectedSites.Add(new SiteInfo("https://contoso.sharepoint.com", "Contoso"));
@@ -243,4 +243,49 @@ public class UserAccessAuditViewModelTests
Assert.True(vm.ExportCsvCommand.CanExecute(null));
Assert.True(vm.ExportHtmlCommand.CanExecute(null));
}
+
+ // ── Test 9: Debounced search triggers SearchUsersAsync ───────────────────
+
+ [Fact]
+ public async Task SearchQuery_debounced_calls_SearchUsersAsync()
+ {
+ var graphResults = new List
+ {
+ new("Alice Smith", "alice@contoso.com", "alice@contoso.com")
+ };
+
+ var (vm, _, graphMock) = CreateViewModel();
+
+ graphMock
+ .Setup(s => s.SearchUsersAsync(
+ It.IsAny(),
+ It.Is(q => q == "Ali"),
+ It.IsAny(),
+ It.IsAny()))
+ .ReturnsAsync(graphResults);
+
+ // Set a TenantProfile so _currentProfile is non-null
+ var profile = new TenantProfile
+ {
+ Name = "Test",
+ TenantUrl = "https://contoso.sharepoint.com",
+ ClientId = "test-client-id"
+ };
+ WeakReferenceMessenger.Default.Send(new TenantSwitchedMessage(profile));
+
+ // Act: set SearchQuery which triggers OnSearchQueryChanged → DebounceSearchAsync
+ vm.SearchQuery = "Ali";
+
+ // Wait longer than 300ms debounce to allow async fire-and-forget to complete
+ await Task.Delay(600);
+
+ // Assert: SearchUsersAsync was called with the query
+ graphMock.Verify(
+ s => s.SearchUsersAsync(
+ It.IsAny(),
+ "Ali",
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
}