-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add unit tests for wasm auth state provider
- Loading branch information
1 parent
562c6c3
commit 2849247
Showing
4 changed files
with
188 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
test/Duende.Bff.Blazor.Client.UnitTests/BffClientAuthenticationStateProviderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
using System.Security.Claims; | ||
using Duende.Bff.Blazor.Client.Internals; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Time.Testing; | ||
using NSubstitute; | ||
using Shouldly; | ||
|
||
namespace Duende.Bff.Blazor.Client.UnitTests; | ||
|
||
public class BffClientAuthenticationStateProviderTests | ||
{ | ||
[Fact] | ||
public async Task when_UserService_gives_anonymous_user_GetAuthState_returns_anonymous() | ||
{ | ||
var userService = Substitute.For<IGetUserService>(); | ||
userService.GetUserAsync().Returns(new ClaimsPrincipal(new ClaimsIdentity())); | ||
var sut = new BffClientAuthenticationStateProvider( | ||
userService, | ||
new FakeTimeProvider(), | ||
TestMocks.MockOptions(), | ||
Substitute.For<ILogger<BffClientAuthenticationStateProvider>>()); | ||
|
||
var authState = await sut.GetAuthenticationStateAsync(); | ||
authState.User.Identity.IsAuthenticated.ShouldBeFalse(); | ||
} | ||
|
||
[Fact] | ||
public async Task when_UserService_returns_persisted_user_GetAuthState_returns_that_user() | ||
{ | ||
var expectedName = "test-user"; | ||
var userService = Substitute.For<IGetUserService>(); | ||
userService.GetUserAsync().Returns(new ClaimsPrincipal(new ClaimsIdentity( | ||
new []{ new Claim("name", expectedName) }, | ||
"pwd", "name", "role"))); | ||
var sut = new BffClientAuthenticationStateProvider( | ||
userService, | ||
new FakeTimeProvider(), | ||
TestMocks.MockOptions(), | ||
Substitute.For<ILogger<BffClientAuthenticationStateProvider>>()); | ||
|
||
var authState = await sut.GetAuthenticationStateAsync(); | ||
authState.User.Identity.IsAuthenticated.ShouldBeTrue(); | ||
authState.User.Identity.Name.ShouldBe(expectedName); | ||
userService.Received(1).GetUserAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task after_configured_delay_UserService_is_called_again_and_state_notification_is_called() | ||
{ | ||
var expectedName = "test-user"; | ||
var userService = Substitute.For<IGetUserService>(); | ||
var time = new FakeTimeProvider(); | ||
userService.GetUserAsync().Returns(new ClaimsPrincipal(new ClaimsIdentity( | ||
new []{ new Claim("name", expectedName) }, | ||
"pwd", "name", "role"))); | ||
var sut = new BffClientAuthenticationStateProvider( | ||
userService, | ||
time, | ||
TestMocks.MockOptions(new BffBlazorOptions | ||
{ | ||
StateProviderPollingDelay = 2000, | ||
StateProviderPollingInterval = 10000 | ||
|
||
}), | ||
Substitute.For<ILogger<BffClientAuthenticationStateProvider>>()); | ||
|
||
var authState = await sut.GetAuthenticationStateAsync(); | ||
|
||
// Initially, we have called the user service once to initialize | ||
userService.Received(1).GetUserAsync(); | ||
|
||
// Advance time within the polling delay, and note that we still haven't made additional calls | ||
time.Advance(TimeSpan.FromSeconds(1)); // t = 1 | ||
userService.Received(1).GetUserAsync(); | ||
|
||
// Advance time past the polling delay, and note that we make an additional call | ||
time.Advance(TimeSpan.FromSeconds(2)); // t = 3 | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(1).GetUserAsync(false); | ||
|
||
// Advance time within the polling interval, but more than the polling delay | ||
// We don't expect additional calls yet | ||
time.Advance(TimeSpan.FromSeconds(3)); // t = 6 | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(1).GetUserAsync(false); | ||
|
||
// Advance time past the polling interval, and note that we make an additional call | ||
time.Advance(TimeSpan.FromSeconds(10)); // t = 16 | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(2).GetUserAsync(false); | ||
} | ||
|
||
[Fact] | ||
public async Task timer_stops_when_user_logs_out() | ||
{ | ||
var expectedName = "test-user"; | ||
var userService = Substitute.For<IGetUserService>(); | ||
var time = new FakeTimeProvider(); | ||
|
||
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity()); | ||
anonymousUser.Identity.IsAuthenticated.ShouldBeFalse(); | ||
|
||
var cachedUser = new ClaimsPrincipal(new ClaimsIdentity( | ||
[ | ||
new Claim("name", expectedName), | ||
new Claim("source", "cache") | ||
], "pwd", "name", "role")); | ||
|
||
var fetchedUser = new ClaimsPrincipal(new ClaimsIdentity( | ||
[ | ||
new Claim("name", expectedName), | ||
new Claim("source", "fetch") | ||
], "pwd", "name", "role")); | ||
|
||
userService.GetUserAsync(true).Returns(cachedUser); | ||
userService.GetUserAsync(false).Returns(fetchedUser, anonymousUser); | ||
var sut = new BffClientAuthenticationStateProvider( | ||
userService, | ||
time, | ||
TestMocks.MockOptions(new BffBlazorOptions | ||
{ | ||
StateProviderPollingDelay = 2000, | ||
StateProviderPollingInterval = 10000 | ||
|
||
}), | ||
Substitute.For<ILogger<BffClientAuthenticationStateProvider>>()); | ||
|
||
var authState = await sut.GetAuthenticationStateAsync(); | ||
time.Advance(TimeSpan.FromSeconds(5)); | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(1).GetUserAsync(false); | ||
|
||
time.Advance(TimeSpan.FromSeconds(10)); | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(2).GetUserAsync(false); | ||
|
||
|
||
time.Advance(TimeSpan.FromSeconds(50)); | ||
userService.Received(1).GetUserAsync(true); | ||
userService.Received(2).GetUserAsync(false); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
using System.Net; | ||
using Microsoft.Extensions.Options; | ||
using NSubstitute; | ||
|
||
namespace Duende.Bff.Blazor.Client.UnitTests; | ||
|
||
public static class TestMocks | ||
{ | ||
public static IHttpClientFactory MockHttpClientFactory(string response, HttpStatusCode status) | ||
{ | ||
var httpClient = new HttpClient(new MockHttpMessageHandler(response, status)) | ||
{ | ||
// Just have to set something that looks reasonably like a URL so that the HttpClient's internal validation | ||
// doesn't blow up | ||
BaseAddress = new Uri("https://example.com") | ||
}; | ||
var factory = Substitute.For<IHttpClientFactory>(); | ||
factory.CreateClient(BffClientAuthenticationStateProvider.HttpClientName).Returns(httpClient); | ||
return factory; | ||
} | ||
|
||
public static IOptions<BffBlazorOptions> MockOptions(BffBlazorOptions? opt = null) | ||
{ | ||
var result = Substitute.For<IOptions<BffBlazorOptions>>(); | ||
result.Value.Returns(opt ?? new BffBlazorOptions()); | ||
return result; | ||
} | ||
} |