test(07-10): add debounced search unit test for UserAccessAuditViewModel
- Extended CreateViewModel helper to return (vm, auditMock, graphMock) 3-tuple - Updated all 8 existing tests to use _ discard for the new graphMock slot - Added Test 9: SearchQuery_debounced_calls_SearchUsersAsync verifying that setting SearchQuery to "Ali" calls SearchUsersAsync after 300ms debounce - All 9 ViewModel tests pass; full suite 177 passed / 22 skipped
This commit is contained in:
@@ -41,8 +41,8 @@ public class UserAccessAuditViewModelTests
|
||||
string upn = "alice@contoso.com") =>
|
||||
new(display, upn, upn);
|
||||
|
||||
/// <summary>Creates a ViewModel wired with a mock IUserAccessAuditService.</summary>
|
||||
private static (UserAccessAuditViewModel vm, Mock<IUserAccessAuditService> auditMock)
|
||||
/// <summary>Creates a ViewModel wired with mock services.</summary>
|
||||
private static (UserAccessAuditViewModel vm, Mock<IUserAccessAuditService> auditMock, Mock<IGraphUserSearchService> graphMock)
|
||||
CreateViewModel(IReadOnlyList<UserAccessEntry>? auditResult = null)
|
||||
{
|
||||
var mockAudit = new Mock<IUserAccessAuditService>();
|
||||
@@ -65,7 +65,7 @@ public class UserAccessAuditViewModelTests
|
||||
mockSession.Object,
|
||||
NullLogger<FeatureViewModelBase>.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<UserAccessEntry> { 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<SiteInfo>
|
||||
{
|
||||
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<UserAccessEntry> { 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<GraphUserResult>
|
||||
{
|
||||
new("Alice Smith", "alice@contoso.com", "alice@contoso.com")
|
||||
};
|
||||
|
||||
var (vm, _, graphMock) = CreateViewModel();
|
||||
|
||||
graphMock
|
||||
.Setup(s => s.SearchUsersAsync(
|
||||
It.IsAny<string>(),
|
||||
It.Is<string>(q => q == "Ali"),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.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<string>(),
|
||||
"Ali",
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user