Skip to content

Commit 0ad1944

Browse files
committed
More UserManager tests
1 parent d31efd5 commit 0ad1944

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

src/Identity/Extensions.Core/src/UserManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,7 @@ public virtual Task<IList<UserPasskeyInfo>> GetPasskeysAsync(TUser user)
21882188
{
21892189
ThrowIfDisposed();
21902190
var passkeyStore = GetUserPasskeyStore();
2191+
ArgumentNullThrowHelper.ThrowIfNull(user);
21912192
ArgumentNullThrowHelper.ThrowIfNull(credentialId);
21922193

21932194
return passkeyStore.FindPasskeyAsync(user, credentialId, CancellationToken);

src/Identity/test/Identity.Test/DefaultPasskeyHandlerTest.Helpers.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ private readonly struct AttestationObjectArgs()
145145
public required ReadOnlyMemory<byte>? AuthenticatorData { get; init; }
146146
}
147147

148+
// Represents a test scenario for a passkey operation (attestation or assertion)
148149
private abstract class PasskeyTestBase<TResult>
149150
{
150151
private bool _hasStarted;
@@ -163,6 +164,10 @@ public Task<TResult> RunAsync()
163164
protected abstract Task<TResult> RunCoreAsync();
164165
}
165166

167+
// While some test configuration can be set directly on scenario classes (AttestationTest and AssertionTest),
168+
// individual tests may need to modify values computed during execution (e.g., JSON payloads, hashes).
169+
// This helper enables trivial customization of test scenarios by allowing injection of custom logic to
170+
// transform runtime values.
166171
private class ComputedValue<TValue>
167172
{
168173
private bool _isComputed;

src/Identity/test/Identity.Test/UserManagerTest.cs

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,81 @@ public async Task RemoveClaimCallsStore()
668668
store.VerifyAll();
669669
}
670670

671+
[Fact]
672+
public async Task SetPasskeyAsyncCallsStore()
673+
{
674+
// Setup
675+
var store = new Mock<IUserPasskeyStore<PocoUser>>();
676+
var user = new PocoUser { UserName = "Foo" };
677+
var passkey = new UserPasskeyInfo(null, null, null, default, 0, null, false, false, false, null, null);
678+
store.Setup(s => s.SetPasskeyAsync(user, passkey, CancellationToken.None)).Returns(Task.CompletedTask).Verifiable();
679+
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
680+
var userManager = MockHelpers.TestUserManager<PocoUser>(store.Object);
681+
682+
// Act
683+
var result = await userManager.SetPasskeyAsync(user, passkey);
684+
685+
// Assert
686+
Assert.True(result.Succeeded);
687+
store.VerifyAll();
688+
}
689+
690+
[Fact]
691+
public async Task GetPasskeysAsyncCallsStore()
692+
{
693+
// Setup
694+
var store = new Mock<IUserPasskeyStore<PocoUser>>();
695+
var user = new PocoUser { UserName = "Foo" };
696+
var passkey = new UserPasskeyInfo(null, null, null, default, 0, null, false, false, false, null, null);
697+
var passkeys = (IList<UserPasskeyInfo>)[passkey];
698+
store.Setup(s => s.GetPasskeysAsync(user, CancellationToken.None)).Returns(Task.FromResult(passkeys)).Verifiable();
699+
var userManager = MockHelpers.TestUserManager<PocoUser>(store.Object);
700+
701+
// Act
702+
var result = await userManager.GetPasskeysAsync(user);
703+
704+
// Assert
705+
Assert.Same(passkeys, result);
706+
store.VerifyAll();
707+
}
708+
709+
[Fact]
710+
public async Task FindByPasskeyIdCallsStore()
711+
{
712+
// Setup
713+
var store = new Mock<IUserPasskeyStore<PocoUser>>();
714+
var user = new PocoUser { UserName = "Foo" };
715+
var credentialId = (byte[])[1, 2, 3, 4, 5, 6, 7, 8];
716+
store.Setup(s => s.FindByPasskeyIdAsync(credentialId, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
717+
var userManager = MockHelpers.TestUserManager<PocoUser>(store.Object);
718+
719+
// Act
720+
var result = await userManager.FindByPasskeyIdAsync(credentialId);
721+
722+
// Assert
723+
Assert.Equal(user, result);
724+
store.VerifyAll();
725+
}
726+
727+
[Fact]
728+
public async Task RemovePasskeyAsyncCallsStore()
729+
{
730+
// Setup
731+
var store = new Mock<IUserPasskeyStore<PocoUser>>();
732+
var user = new PocoUser { UserName = "Foo" };
733+
var credentialId = (byte[])[1, 2, 3, 4, 5, 6, 7, 8];
734+
store.Setup(s => s.RemovePasskeyAsync(user, credentialId, CancellationToken.None)).Returns(Task.CompletedTask).Verifiable();
735+
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
736+
var userManager = MockHelpers.TestUserManager<PocoUser>(store.Object);
737+
738+
// Act
739+
var result = await userManager.RemovePasskeyAsync(user, credentialId);
740+
741+
// Assert
742+
Assert.True(result.Succeeded);
743+
store.VerifyAll();
744+
}
745+
671746
[Fact]
672747
public async Task CheckPasswordWithNullUserReturnsFalse()
673748
{
@@ -1040,6 +1115,10 @@ await Assert.ThrowsAsync<ArgumentNullException>("providerKey",
10401115
Assert.Throws<ArgumentNullException>("provider", () => manager.RegisterTokenProvider("whatever", null));
10411116
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.AddToRolesAsync(new PocoUser(), null));
10421117
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.RemoveFromRolesAsync(new PocoUser(), null));
1118+
await Assert.ThrowsAsync<ArgumentNullException>("passkey", async () => await manager.SetPasskeyAsync(new PocoUser(), null));
1119+
await Assert.ThrowsAsync<ArgumentNullException>("credentialId", async () => await manager.GetPasskeyAsync(new PocoUser(), null));
1120+
await Assert.ThrowsAsync<ArgumentNullException>("credentialId", async () => await manager.FindByPasskeyIdAsync(null));
1121+
await Assert.ThrowsAsync<ArgumentNullException>("credentialId", async () => await manager.RemovePasskeyAsync(new PocoUser(), null));
10431122
}
10441123

10451124
[Fact]
@@ -1141,6 +1220,14 @@ await Assert.ThrowsAsync<ArgumentNullException>("user",
11411220
async () => await manager.GetLockoutEndDateAsync(null));
11421221
await Assert.ThrowsAsync<ArgumentNullException>("user",
11431222
async () => await manager.IsLockedOutAsync(null));
1223+
await Assert.ThrowsAsync<ArgumentNullException>("user",
1224+
async () => await manager.SetPasskeyAsync(null, null));
1225+
await Assert.ThrowsAsync<ArgumentNullException>("user",
1226+
async () => await manager.GetPasskeysAsync(null));
1227+
await Assert.ThrowsAsync<ArgumentNullException>("user",
1228+
async () => await manager.GetPasskeyAsync(null, null));
1229+
await Assert.ThrowsAsync<ArgumentNullException>("user",
1230+
async () => await manager.RemovePasskeyAsync(null, null));
11441231
}
11451232

11461233
[Fact]
@@ -1180,6 +1267,11 @@ public async Task MethodsThrowWhenDisposedTest()
11801267
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GenerateEmailConfirmationTokenAsync(null));
11811268
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.IsEmailConfirmedAsync(null));
11821269
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ConfirmEmailAsync(null, null));
1270+
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.SetPasskeyAsync(null, null));
1271+
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GetPasskeysAsync(null));
1272+
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GetPasskeyAsync(null, null));
1273+
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.FindByPasskeyIdAsync(null));
1274+
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.RemovePasskeyAsync(null, null));
11831275
}
11841276

11851277
private class BadPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
@@ -1213,7 +1305,8 @@ private class EmptyStore :
12131305
IUserLockoutStore<PocoUser>,
12141306
IUserTwoFactorStore<PocoUser>,
12151307
IUserRoleStore<PocoUser>,
1216-
IUserSecurityStampStore<PocoUser>
1308+
IUserSecurityStampStore<PocoUser>,
1309+
IUserPasskeyStore<PocoUser>
12171310
{
12181311
public Task<IList<Claim>> GetClaimsAsync(PocoUser user, CancellationToken cancellationToken = default(CancellationToken))
12191312
{
@@ -1463,6 +1556,31 @@ public void Dispose()
14631556
{
14641557
return Task.FromResult(0);
14651558
}
1559+
1560+
public Task SetPasskeyAsync(PocoUser user, UserPasskeyInfo passkey, CancellationToken cancellationToken)
1561+
{
1562+
return Task.CompletedTask;
1563+
}
1564+
1565+
public Task<IList<UserPasskeyInfo>> GetPasskeysAsync(PocoUser user, CancellationToken cancellationToken)
1566+
{
1567+
return Task.FromResult<IList<UserPasskeyInfo>>([]);
1568+
}
1569+
1570+
public Task<PocoUser> FindByPasskeyIdAsync(byte[] credentialId, CancellationToken cancellationToken)
1571+
{
1572+
return Task.FromResult<PocoUser>(null);
1573+
}
1574+
1575+
public Task<UserPasskeyInfo> FindPasskeyAsync(PocoUser user, byte[] credentialId, CancellationToken cancellationToken)
1576+
{
1577+
return Task.FromResult<UserPasskeyInfo>(null);
1578+
}
1579+
1580+
public Task RemovePasskeyAsync(PocoUser user, byte[] credentialId, CancellationToken cancellationToken)
1581+
{
1582+
return Task.CompletedTask;
1583+
}
14661584
}
14671585

14681586
private class NoOpTokenProvider : IUserTwoFactorTokenProvider<PocoUser>
@@ -1493,7 +1611,8 @@ private class NotImplementedStore :
14931611
IUserEmailStore<PocoUser>,
14941612
IUserPhoneNumberStore<PocoUser>,
14951613
IUserLockoutStore<PocoUser>,
1496-
IUserTwoFactorStore<PocoUser>
1614+
IUserTwoFactorStore<PocoUser>,
1615+
IUserPasskeyStore<PocoUser>
14971616
{
14981617
public Task<IList<Claim>> GetClaimsAsync(PocoUser user, CancellationToken cancellationToken = default(CancellationToken))
14991618
{
@@ -1734,6 +1853,31 @@ Task<IdentityResult> IUserStore<PocoUser>.DeleteAsync(PocoUser user, Cancellatio
17341853
{
17351854
throw new NotImplementedException();
17361855
}
1856+
1857+
public Task SetPasskeyAsync(PocoUser user, UserPasskeyInfo passkey, CancellationToken cancellationToken)
1858+
{
1859+
throw new NotImplementedException();
1860+
}
1861+
1862+
public Task<IList<UserPasskeyInfo>> GetPasskeysAsync(PocoUser user, CancellationToken cancellationToken)
1863+
{
1864+
throw new NotImplementedException();
1865+
}
1866+
1867+
public Task<PocoUser> FindByPasskeyIdAsync(byte[] credentialId, CancellationToken cancellationToken)
1868+
{
1869+
throw new NotImplementedException();
1870+
}
1871+
1872+
public Task<UserPasskeyInfo> FindPasskeyAsync(PocoUser user, byte[] credentialId, CancellationToken cancellationToken)
1873+
{
1874+
throw new NotImplementedException();
1875+
}
1876+
1877+
public Task RemovePasskeyAsync(PocoUser user, byte[] credentialId, CancellationToken cancellationToken)
1878+
{
1879+
throw new NotImplementedException();
1880+
}
17371881
}
17381882

17391883
[Fact]

0 commit comments

Comments
 (0)