diff --git a/Identity.sln b/Identity.sln index 19134d0bfe..72f841b164 100644 --- a/Identity.sln +++ b/Identity.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}" EndProject @@ -29,6 +29,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identi EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.AspNetCoreCompat", "src\Microsoft.AspNet.Identity.AspNetCoreCompat\Microsoft.AspNet.Identity.AspNetCoreCompat.xproj", "{6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Microsoft.AspNetCore.Identity.Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.xproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -121,6 +123,18 @@ Global {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Mixed Platforms.Build.0 = Release|Any CPU {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.ActiveCfg = Release|Any CPU {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -134,5 +148,6 @@ Global {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} {4490894C-3572-4E63-86F1-EE5105CE8A06} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {5608E828-DD54-4E2A-B73C-FC22268BE797} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs index 2f69a8e2d0..e4febdec32 100644 --- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs @@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The role whose ID should be returned. /// The used to propagate notifications that the operation should be canceled. /// A that contains the ID of the role. - public Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The role whose name should be returned. /// The used to propagate notifications that the operation should be canceled. /// A that contains the name of the role. - public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -255,7 +255,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The name of the role. /// The used to propagate notifications that the operation should be canceled. /// The that represents the asynchronous operation. - public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -383,7 +383,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The role whose claims should be retrieved. /// The used to propagate notifications that the operation should be canceled. /// A that contains the claims granted to a role. - public async Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public async virtual Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) @@ -401,7 +401,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The claim to add to the role. /// The used to propagate notifications that the operation should be canceled. /// The that represents the asynchronous operation. - public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) @@ -424,7 +424,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The claim to remove from the role. /// The used to propagate notifications that the operation should be canceled. /// The that represents the asynchronous operation. - public async Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + public async virtual Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs index 87cf6d2579..b8439aad6c 100644 --- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs @@ -1411,7 +1411,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The name of the token. /// The used to propagate notifications that the operation should be canceled. /// The that represents the asynchronous operation. - public async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + public async virtual Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -1435,7 +1435,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore /// The name of the token. /// The used to propagate notifications that the operation should be canceled. /// The that represents the asynchronous operation. - public async Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + public async virtual Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); diff --git a/test/Shared/IdentityResultAssert.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentityResultAssert.cs similarity index 64% rename from test/Shared/IdentityResultAssert.cs rename to src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentityResultAssert.cs index d7c7983cdf..4246d1712f 100644 --- a/test/Shared/IdentityResultAssert.cs +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentityResultAssert.cs @@ -1,27 +1,39 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Diagnostics; using System.Linq; using Microsoft.Extensions.Logging; using Xunit; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// Helper for tests to validate identity results. + /// public static class IdentityResultAssert { + /// + /// Asserts that the result has Succeeded. + /// + /// public static void IsSuccess(IdentityResult result) { Assert.NotNull(result); Assert.True(result.Succeeded); } + /// + /// Asserts that the result has not Succeeded. + /// public static void IsFailure(IdentityResult result) { Assert.NotNull(result); Assert.False(result.Succeeded); } + /// + /// Asserts that the result has not Succeeded and that error is the first Error's Description. + /// public static void IsFailure(IdentityResult result, string error) { Assert.NotNull(result); @@ -29,6 +41,9 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(error, result.Errors.First().Description); } + /// + /// Asserts that the result has not Succeeded and that first error matches error's code and Description. + /// public static void IsFailure(IdentityResult result, IdentityError error) { Assert.NotNull(result); @@ -37,6 +52,11 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(error.Code, result.Errors.First().Code); } + /// + /// Asserts that the logger contains the expectedLog. + /// + /// The logger to inspect. + /// The expected log message. public static void VerifyLogMessage(ILogger logger, string expectedLog) { var testlogger = logger as ITestLogger; diff --git a/test/Shared/UserManagerTestBase.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs similarity index 86% rename from test/Shared/UserManagerTestBase.cs rename to src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs index 37e080247b..da118b619c 100644 --- a/test/Shared/UserManagerTestBase.cs +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs @@ -17,13 +17,23 @@ using Xunit; namespace Microsoft.AspNetCore.Identity.Test { - // Common functionality tests that all verifies user manager functionality regardless of store implementation - public abstract class UserManagerTestBase : UserManagerTestBase + /// + /// Common functionality tests that all verifies user manager functionality regardless of store implementation + /// + /// The type of the user. + /// The type of the role. + public abstract class IdentitySpecificationTestBase : IdentitySpecificationTestBase where TUser : class where TRole : class { } - public abstract class UserManagerTestBase + /// + /// Base class for tests that exercise basic identity functionality that all stores should support. + /// + /// The type of the user. + /// The type of the role. + /// The primary key type. + public abstract class IdentitySpecificationTestBase where TUser : class where TRole : class where TKey : IEquatable @@ -32,11 +42,20 @@ namespace Microsoft.AspNetCore.Identity.Test private readonly IdentityErrorDescriber _errorDescriber = new IdentityErrorDescriber(); + /// + /// If true, tests that require a database will be skipped. + /// + /// protected virtual bool ShouldSkipDbTests() { return false; } + /// + /// Configure the service collection used for tests. + /// + /// + /// protected virtual void SetupIdentityServices(IServiceCollection services, object context = null) { services.AddSingleton(); @@ -55,6 +74,13 @@ namespace Microsoft.AspNetCore.Identity.Test services.AddSingleton>>(new TestLogger>()); } + /// + /// Creates the user manager used for tests. + /// + /// The context that will be passed into the store, typically a db context. + /// The service collection to use, optional. + /// Delegate used to configure the services, optional. + /// The user manager to use for tests. protected virtual UserManager CreateManager(object context = null, IServiceCollection services = null, Action configureServices = null) { if (services == null) @@ -66,13 +92,16 @@ namespace Microsoft.AspNetCore.Identity.Test context = CreateTestContext(); } SetupIdentityServices(services, context); - if (configureServices != null) - { - configureServices(services); - } + configureServices?.Invoke(services); return services.BuildServiceProvider().GetService>(); } + /// + /// Creates the role manager for tests. + /// + /// The context that will be passed into the store, typically a db context. + /// The service collection to use, optional. + /// protected RoleManager CreateRoleManager(object context = null, IServiceCollection services = null) { if (services == null) @@ -87,24 +116,86 @@ namespace Microsoft.AspNetCore.Identity.Test return services.BuildServiceProvider().GetService>(); } + /// + /// Creates the context object for a test, typically a DbContext. + /// + /// The context object for a test, typically a DbContext. protected abstract object CreateTestContext(); + /// + /// Adds an IUserStore to services for the test. + /// + /// The service collection to add to. + /// The context for the store to use, optional. protected abstract void AddUserStore(IServiceCollection services, object context = null); + + /// + /// Adds an IRoleStore to services for the test. + /// + /// The service collection to add to. + /// The context for the store to use, optional. protected abstract void AddRoleStore(IServiceCollection services, object context = null); + /// + /// Set the user's password hash. + /// + /// The user to set. + /// The password hash to set. protected abstract void SetUserPasswordHash(TUser user, string hashedPassword); + /// + /// Create a new test user instance. + /// + /// Optional name prefix, name will be randomized. + /// Optional email. + /// Optional phone number. + /// Optional lockout enabled. + /// Optional lockout end. + /// If true, the prefix should be used as the username without a random pad. + /// The new test user instance. protected abstract TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "", bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = null, bool useNamePrefixAsUserName = false); + /// + /// Creates a new test role instance. + /// + /// Optional name prefix, name will be randomized. + /// If true, the prefix should be used as the rolename without a random pad. + /// protected abstract TRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false); + /// + /// Query used to do name equality checks. + /// + /// The user name to match. + /// The query to use. protected abstract Expression> UserNameEqualsPredicate(string userName); + + /// + /// Query used to do user name prefix matching. + /// + /// The user name to match. + /// The query to use. protected abstract Expression> UserNameStartsWithPredicate(string userName); + /// + /// Query used to do name equality checks. + /// + /// The role name to match. + /// The query to use. protected abstract Expression> RoleNameEqualsPredicate(string roleName); + + /// + /// Query used to do user name prefix matching. + /// + /// The role name to match. + /// The query to use. protected abstract Expression> RoleNameStartsWithPredicate(string roleName); + /// + /// Test. + /// + /// Task [Fact] public async Task CanDeleteUser() { @@ -120,6 +211,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Null(await manager.FindByIdAsync(userId)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanUpdateUserName() { @@ -139,6 +234,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Null(await manager.FindByNameAsync(name)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CheckSetUserNameValidatesUser() { @@ -167,6 +266,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task SetUserNameUpdatesSecurityStamp() { @@ -185,6 +288,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CreateUpdatesSecurityStamp() { @@ -200,6 +307,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotNull(await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CheckSetEmailValidatesUser() { @@ -223,6 +334,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, ""), _errorDescriber.InvalidEmail("")); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanUpdatePasswordUsingHasher() { @@ -243,6 +358,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(await manager.CheckPasswordAsync(user, "New")); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanFindById() { @@ -256,6 +375,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotNull(await manager.FindByIdAsync(await manager.GetUserIdAsync(user))); } + /// + /// Test. + /// + /// Task [Fact] public async Task UserValidatorCanBlockCreate() { @@ -271,6 +394,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task UserValidatorCanBlockUpdate() { @@ -287,6 +414,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChainUserValidators() { @@ -305,7 +436,11 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(2, result.Errors.Count()); } - [ConditionalTheory] + /// + /// Test. + /// + /// Task + [Theory] [InlineData("")] [InlineData(null)] public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) @@ -321,6 +456,10 @@ namespace Microsoft.AspNetCore.Identity.Test } #if NET451 + /// + /// Test. + /// + /// Task [Theory] [InlineData("@@afd")] [InlineData("bogus")] @@ -337,6 +476,10 @@ namespace Microsoft.AspNetCore.Identity.Test } #endif + /// + /// Test. + /// + /// Task [Fact] public async Task PasswordValidatorCanBlockAddPassword() { @@ -354,6 +497,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChainPasswordValidators() { @@ -372,6 +519,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(2, result.Errors.Count()); } + /// + /// Test. + /// + /// Task [Fact] public async Task PasswordValidatorCanBlockChangePassword() { @@ -389,6 +540,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task PasswordValidatorCanBlockCreateUser() { @@ -404,6 +559,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanCreateUserNoPassword() { @@ -423,6 +582,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, logins.Count()); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanCreateUserAddLogin() { @@ -445,6 +608,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(display, logins.First().ProviderDisplayName); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanCreateUserLoginAndAddPassword() { @@ -468,6 +635,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(await manager.CheckPasswordAsync(user, "password")); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddPasswordFailsIfAlreadyHave() { @@ -484,6 +655,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} already has a password."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanCreateUserAddRemoveLogin() { @@ -515,6 +690,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanRemovePassword() { @@ -535,6 +714,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChangePassword() { @@ -555,6 +738,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanAddRemoveUserClaim() { @@ -584,6 +771,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, userClaims.Count); } + /// + /// Test. + /// + /// Task [Fact] public async Task RemoveClaimOnlyAffectsUser() { @@ -617,6 +808,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(3, userClaims2.Count); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanReplaceUserClaim() { @@ -640,6 +835,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(claim.Value, newClaim.Value); } + /// + /// Test. + /// + /// Task [Fact] public async Task ReplaceUserClaimOnlyAffectsUser() { @@ -673,6 +872,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal("a", oldClaim2.Value); } + /// + /// Test. + /// + /// Task [Fact] public async Task ChangePasswordFallsIfPasswordWrong() { @@ -688,6 +891,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Change password failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddDupeUserNameFails() { @@ -703,6 +910,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateUserName(username)); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddDupeEmailAllowedByDefault() { @@ -718,6 +929,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user2, await manager.GetEmailAsync(user))); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddDupeEmailFailsWhenUniqueEmailRequired() { @@ -733,6 +948,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateEmail("FooUser@yup.com")); } + /// + /// Test. + /// + /// Task [Fact] public async Task UpdateSecurityStampActuallyChanges() { @@ -750,6 +969,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddDupeLoginFails() { @@ -768,6 +991,11 @@ namespace Microsoft.AspNetCore.Identity.Test } // Email tests + + /// + /// Test. + /// + /// Task [Fact] public async Task CanFindByEmail() { @@ -783,6 +1011,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(user, fetch); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanFindUsersViaUserQuerable() { @@ -803,6 +1035,10 @@ namespace Microsoft.AspNetCore.Identity.Test } } + /// + /// Test. + /// + /// Task [Fact] public async Task ConfirmEmailFalseByDefaultTest() { @@ -839,6 +1075,10 @@ namespace Microsoft.AspNetCore.Identity.Test } } + /// + /// Test. + /// + /// Task [Fact] public async Task CanResetPasswordWithStaticTokenProvider() { @@ -864,6 +1104,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() { @@ -890,6 +1134,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() { @@ -912,6 +1160,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() { @@ -940,6 +1192,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user2)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanConfirmEmailWithStaticToken() { @@ -962,6 +1218,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await manager.IsEmailConfirmedAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() { @@ -980,6 +1240,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task ConfirmTokenFailsAfterPasswordChange() { @@ -1001,6 +1265,10 @@ namespace Microsoft.AspNetCore.Identity.Test // Lockout tests + /// + /// Test. + /// + /// Task [Fact] public async Task SingleFailureLockout() { @@ -1023,6 +1291,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task TwoFailureLockout() { @@ -1048,6 +1320,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ResetAccessCountPreventsLockout() { @@ -1076,6 +1352,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanEnableLockoutManuallyAndLockout() { @@ -1104,6 +1384,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() { @@ -1120,6 +1404,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task LockoutFailsIfNotEnabled() { @@ -1138,6 +1426,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await mgr.IsLockedOutAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() { @@ -1152,6 +1444,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await mgr.IsLockedOutAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() { @@ -1167,6 +1463,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await mgr.IsLockedOutAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() { @@ -1182,6 +1482,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(await mgr.IsLockedOutAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() { @@ -1200,7 +1504,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(lockoutEnd, end); } - // Role Tests + /// + /// Test. + /// + /// Task [Fact] public async Task CanCreateRoleTest() { @@ -1237,6 +1544,10 @@ namespace Microsoft.AspNetCore.Identity.Test } } + /// + /// Test. + /// + /// Task [Fact] public async Task BadValidatorBlocksCreateRole() { @@ -1253,6 +1564,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Role {await manager.GetRoleIdAsync(role) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChainRoleValidators() { @@ -1271,6 +1586,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(2, result.Errors.Count()); } + /// + /// Test. + /// + /// Task [Fact] public async Task BadValidatorBlocksRoleUpdate() { @@ -1288,6 +1607,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Role {await manager.GetRoleIdAsync(role) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanDeleteRole() { @@ -1305,6 +1628,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await manager.RoleExistsAsync(roleName)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanAddRemoveRoleClaim() { @@ -1344,6 +1671,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(3, safeRoleClaims.Count); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanRoleFindById() { @@ -1358,6 +1689,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(role, await manager.FindByIdAsync(await manager.GetRoleIdAsync(role))); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanRoleFindByName() { @@ -1374,6 +1709,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(role, await manager.FindByNameAsync(roleName)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanUpdateRoleName() { @@ -1393,6 +1732,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(role, await manager.FindByNameAsync("Changed")); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanQueryableRoles() { @@ -1408,11 +1751,18 @@ namespace Microsoft.AspNetCore.Identity.Test { IdentityResultAssert.IsSuccess(await manager.CreateAsync(r)); } - Assert.Equal(roles.Count, manager.Roles.Count(RoleNameStartsWithPredicate("CanQuerableRolesTest"))); - Assert.Null(manager.Roles.FirstOrDefault(RoleNameEqualsPredicate("bogus"))); + Expression> func = RoleNameStartsWithPredicate("CanQuerableRolesTest"); + Assert.Equal(roles.Count, manager.Roles.Count(func)); + func = RoleNameEqualsPredicate("bogus"); + Assert.Null(manager.Roles.FirstOrDefault(func)); + } } + /// + /// Test. + /// + /// Task [Fact] public async Task CreateRoleFailsIfExists() { @@ -1430,6 +1780,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.IsFailure(await manager.CreateAsync(role2)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanAddUsersToRole() { @@ -1455,8 +1809,11 @@ namespace Microsoft.AspNetCore.Identity.Test } } - [ConditionalFact] - [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Fails due to threading bugs in Mono")] + /// + /// Test. + /// + /// Task + [Fact] public async Task CanGetRolesForUser() { if (ShouldSkipDbTests()) @@ -1495,6 +1852,10 @@ namespace Microsoft.AspNetCore.Identity.Test } } + /// + /// Test. + /// + /// Task [Fact] public async Task RemoveUserFromRoleWithMultipleRoles() { @@ -1518,6 +1879,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.False(await userManager.IsInRoleAsync(user, await roleManager.GetRoleNameAsync(roles[2]))); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanRemoveUsersFromRole() { @@ -1548,6 +1913,10 @@ namespace Microsoft.AspNetCore.Identity.Test } } + /// + /// Test. + /// + /// Task [Fact] public async Task RemoveUserNotInRoleFails() { @@ -1568,6 +1937,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(userMgr.Logger, $"User {await userMgr.GetUserIdAsync(user)} is not in role {roleName}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddUserToRoleFailsIfAlreadyInRole() { @@ -1589,6 +1962,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(userMgr.Logger, $"User {await userMgr.GetUserIdAsync(user)} is already in role {roleName}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task AddUserToRolesIgnoresDuplicates() { @@ -1609,6 +1986,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(await userMgr.IsInRoleAsync(user, roleName)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanFindRoleByNameWithManager() { @@ -1623,6 +2004,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotNull(await roleMgr.FindByNameAsync(roleName)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanFindRoleWithManager() { @@ -1637,6 +2022,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(roleName, await roleMgr.GetRoleNameAsync(await roleMgr.FindByNameAsync(roleName))); } + /// + /// Test. + /// + /// Task [Fact] public async Task SetPhoneNumberTest() { @@ -1654,6 +2043,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChangePhoneNumber() { @@ -1673,6 +2066,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ChangePhoneNumberFailsWithWrongToken() { @@ -1693,6 +2090,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ChangePhoneNumberFailsWithWrongPhoneNumber() { @@ -1713,6 +2114,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanVerifyPhoneNumber() { @@ -1737,6 +2142,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyChangePhoneNumberTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChangeEmail() { @@ -1759,6 +2168,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanChangeEmailWithDifferentTokenProvider() { @@ -1784,6 +2197,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ChangeEmailFailsWithWrongToken() { @@ -1807,6 +2224,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task ChangeEmailFailsWithEmail() { @@ -1831,6 +2252,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task EmailFactorFailsAfterSecurityStampChangeTest() { @@ -1856,6 +2281,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task EnableTwoFactorChangesSecurityStamp() { @@ -1873,6 +2302,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(await manager.GetTwoFactorEnabledAsync(user)); } + /// + /// Test. + /// + /// Task [Fact] public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() { @@ -1891,6 +2324,10 @@ namespace Microsoft.AspNetCore.Identity.Test () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"), error); } + /// + /// Test. + /// + /// Task [Fact] public async Task GetValidTwoFactorTestEmptyWithNoProviders() { @@ -1906,6 +2343,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.True(!factors.Any()); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanGetSetUpdateAndRemoveUserToken() { @@ -1930,6 +2371,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name")); } + /// + /// Test. + /// + /// Task [Fact] public async Task CanGetValidTwoFactor() { @@ -1965,6 +2410,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal("Phone", factors[0]); } + /// + /// Test. + /// + /// Task [Fact] public async Task PhoneFactorFailsAfterSecurityStampChangeTest() { @@ -1985,6 +2434,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task VerifyTokenFromWrongTokenProviderFails() { @@ -2001,6 +2454,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task VerifyWithWrongSmsTokenFails() { @@ -2015,6 +2472,10 @@ namespace Microsoft.AspNetCore.Identity.Test IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); } + /// + /// Test. + /// + /// Task [Fact] public async Task NullableDateTimeOperationTest() { @@ -2037,6 +2498,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(DateTimeOffset.Parse("01/01/2014"), await userMgr.GetLockoutEndDateAsync(user)); } + /// + /// Test. + /// + /// Task [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Fails due to threading bugs in Mono")] public async Task CanGetUsersWithClaims() @@ -2063,6 +2528,10 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, (await manager.GetUsersForClaimAsync(new Claim("123", "456"))).Count); } + /// + /// Test. + /// + /// Task [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Fails due to threading bugs in Mono")] public async Task CanGetUsersInRole() @@ -2102,7 +2571,7 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(0, (await manager.GetUsersInRoleAsync("123456")).Count); } - public List GenerateUsers(string userNamePrefix, int count) + private List GenerateUsers(string userNamePrefix, int count) { var users = new List(count); for (var i = 0; i < count; i++) @@ -2112,7 +2581,7 @@ namespace Microsoft.AspNetCore.Identity.Test return users; } - public List GenerateRoles(string namePrefix, int count) + private List GenerateRoles(string namePrefix, int count) { var roles = new List(count); for (var i = 0; i < count; i++) diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.xproj b/src/Microsoft.AspNetCore.Identity.Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.xproj new file mode 100644 index 0000000000..84132d9ee8 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.xproj @@ -0,0 +1,19 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 5608e828-dd54-4e2a-b73c-fc22268be797 + Microsoft.AspNetCore.Identity.Specification.Tests + .\obj + .\bin\ + + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1a8b56c5a1 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] +[assembly: AssemblyCompany("Microsoft Corporation.")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyProduct("Microsoft ASP.NET Core")] diff --git a/test/Shared/TestLogger.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/TestLogger.cs similarity index 55% rename from test/Shared/TestLogger.cs rename to src/Microsoft.AspNetCore.Identity.Specification.Tests/TestLogger.cs index fd699b1371..7b9ebdece8 100644 --- a/test/Shared/TestLogger.cs +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/TestLogger.cs @@ -7,26 +7,58 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// test logger. + /// public interface ITestLogger { + /// + /// log messages. + /// IList LogMessages { get; } } + /// + /// Test logger. + /// + /// public class TestLogger : ILogger, ITestLogger { + /// + /// log messages. + /// public IList LogMessages { get; } = new List(); + /// + /// + /// + /// + /// public IDisposable BeginScope(TState state) { LogMessages.Add(state?.ToString()); return null; } + /// + /// + /// + /// + /// public bool IsEnabled(LogLevel logLevel) { return true; } + /// + /// + /// + /// + /// + /// + /// + /// + /// public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { if (formatter == null) diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/project.json b/src/Microsoft.AspNetCore.Identity.Specification.Tests/project.json new file mode 100644 index 0000000000..9f07dafbff --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/project.json @@ -0,0 +1,42 @@ +{ + "description": "Shared test suite for Asp.Net Identity Core store implementations.", + "version": "1.2.0-*", + "buildOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk", + "xmlDoc": true + }, + "packOptions": { + "repository": { + "type": "git", + "url": "git://github.com/aspnet/identity" + }, + "tags": [ + "aspnetcore", + "identity", + "membership" + ] + }, + "dependencies": { + "Microsoft.AspNetCore.Identity": { + "target": "project" + }, + "Microsoft.AspNetCore.Testing": "1.2.0-*", + "Microsoft.Extensions.DependencyInjection": "1.2.0-*", + "Microsoft.Extensions.Logging": "1.2.0-*", + "Microsoft.Extensions.TaskCache.Sources": { + "version": "1.2.0-*", + "type": "build" + }, + "NETStandard.Library": "1.6.1-*", + "xunit": "2.2.0-*" + }, + "frameworks": { + "net451": {}, + "netstandard1.3": { + "dependencies": { + "System.Linq.Queryable": "4.3.0-*" + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Identity/Properties/AssemblyInfo.cs index e9f062a492..935f3661c9 100644 --- a/src/Microsoft.AspNetCore.Identity/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNetCore.Identity/Properties/AssemblyInfo.cs @@ -1,10 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Specification.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs index 791f734e38..5a4d0fdc20 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test { - public class InMemoryEFUserStoreTest : UserManagerTestBase + public class InMemoryEFUserStoreTest : IdentitySpecificationTestBase { protected override object CreateTestContext() { diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs index 3b771f3f56..689689728b 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs @@ -15,7 +15,7 @@ using Xunit; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test { - public class InMemoryEFUserStoreTestWithGenerics : UserManagerTestBase, IDisposable + public class InMemoryEFUserStoreTestWithGenerics : IdentitySpecificationTestBase, IDisposable { private readonly InMemoryContextWithGenerics _context; private UserStoreWithGenerics _store; diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/project.json b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/project.json index 9b09367c4f..c0582d7979 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/project.json +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/project.json @@ -12,6 +12,7 @@ "Microsoft.AspNetCore.Http": "1.2.0-*", "Microsoft.AspNetCore.Identity": "1.2.0-*", "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.2.0-*", + "Microsoft.AspNetCore.Identity.Specification.Tests": "1.2.0-*", "Microsoft.AspNetCore.Testing": "1.2.0-*", "Microsoft.EntityFrameworkCore.InMemory": "1.2.0-*", "Moq": "4.6.36-*", diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/ApiConsistencyTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/ApiConsistencyTest.cs new file mode 100644 index 0000000000..47f3a3a06d --- /dev/null +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/ApiConsistencyTest.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using Microsoft.AspNetCore.Identity.Test; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test +{ + public class ApiConsistencyTest : ApiConsistencyTestBase + { + protected override Assembly TargetAssembly => typeof(IdentityUser).GetTypeInfo().Assembly; + } +} diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/SqlStoreTestBase.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/SqlStoreTestBase.cs index 5406487f39..06cc83402c 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/SqlStoreTestBase.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/SqlStoreTestBase.cs @@ -16,7 +16,7 @@ using Xunit; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { - public abstract class SqlStoreTestBase : UserManagerTestBase, IClassFixture + public abstract class SqlStoreTestBase : IdentitySpecificationTestBase, IClassFixture where TUser : IdentityUser, new() where TRole : IdentityRole, new() where TKey : IEquatable diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs index 0d06b7b65a..f5d3cfab18 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs @@ -14,7 +14,7 @@ using Xunit; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { - public class UserStoreTest : UserManagerTestBase, IClassFixture + public class UserStoreTest : IdentitySpecificationTestBase, IClassFixture { private readonly ScratchDatabaseFixture _fixture; diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs index af19cfc35a..e8b0587dfb 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs @@ -15,7 +15,7 @@ using Xunit; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { - public class UserStoreWithGenericsTest : UserManagerTestBase, IClassFixture + public class UserStoreWithGenericsTest : IdentitySpecificationTestBase, IClassFixture { private readonly ScratchDatabaseFixture _fixture; diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/project.json b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/project.json index 0da6498c3d..49c5edb521 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/project.json +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/project.json @@ -20,6 +20,7 @@ "Microsoft.AspNetCore.Http": "1.2.0-*", "Microsoft.AspNetCore.Identity": "1.2.0-*", "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.2.0-*", + "Microsoft.AspNetCore.Identity.Specification.Tests": "1.2.0-*", "Microsoft.AspNetCore.TestHost": "1.2.0-*", "Microsoft.AspNetCore.Testing": "1.2.0-*", "Microsoft.EntityFrameworkCore.InMemory": "1.2.0-*", diff --git a/test/Microsoft.AspNetCore.Identity.InMemory.Test/InMemoryStoreTest.cs b/test/Microsoft.AspNetCore.Identity.InMemory.Test/InMemoryStoreTest.cs index 83384bd4aa..18d897bef9 100644 --- a/test/Microsoft.AspNetCore.Identity.InMemory.Test/InMemoryStoreTest.cs +++ b/test/Microsoft.AspNetCore.Identity.InMemory.Test/InMemoryStoreTest.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Identity.InMemory.Test { - public class InMemoryStoreTest : UserManagerTestBase + public class InMemoryStoreTest : IdentitySpecificationTestBase { protected override object CreateTestContext() { diff --git a/test/Microsoft.AspNetCore.Identity.InMemory.Test/project.json b/test/Microsoft.AspNetCore.Identity.InMemory.Test/project.json index fb0b3dfae3..cfa40aee53 100644 --- a/test/Microsoft.AspNetCore.Identity.InMemory.Test/project.json +++ b/test/Microsoft.AspNetCore.Identity.InMemory.Test/project.json @@ -13,6 +13,7 @@ "Microsoft.AspNetCore.Hosting": "1.2.0-*", "Microsoft.AspNetCore.Http": "1.2.0-*", "Microsoft.AspNetCore.Identity": "1.2.0-*", + "Microsoft.AspNetCore.Identity.Specification.Tests": "1.2.0-*", "Microsoft.AspNetCore.TestHost": "1.2.0-*", "Microsoft.AspNetCore.Testing": "1.2.0-*", "Moq": "4.6.36-*", diff --git a/test/Microsoft.AspNetCore.Identity.Test/PasswordHasherTest.cs b/test/Microsoft.AspNetCore.Identity.Test/PasswordHasherTest.cs index ea16a4a0ab..a2281b52db 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/PasswordHasherTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/PasswordHasherTest.cs @@ -188,5 +188,11 @@ namespace Microsoft.AspNetCore.Identity.Test } } } + + private class PasswordHasherOptionsAccessor : IOptions + { + public PasswordHasherOptions Value { get; } = new PasswordHasherOptions(); + } + } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Identity.Test/project.json b/test/Microsoft.AspNetCore.Identity.Test/project.json index 9ec1efab12..9893c49ad0 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/project.json +++ b/test/Microsoft.AspNetCore.Identity.Test/project.json @@ -11,6 +11,7 @@ "Microsoft.AspNetCore.Hosting": "1.2.0-*", "Microsoft.AspNetCore.Http": "1.2.0-*", "Microsoft.AspNetCore.Identity": "1.2.0-*", + "Microsoft.AspNetCore.Identity.Specification.Tests": "1.2.0-*", "Microsoft.AspNetCore.Testing": "1.2.0-*", "Microsoft.Extensions.Configuration": "1.2.0-*", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.2.0-*", diff --git a/test/Shared/PasswordHasherOptionsAccessor.cs b/test/Shared/PasswordHasherOptionsAccessor.cs deleted file mode 100644 index ef9e9c58ae..0000000000 --- a/test/Shared/PasswordHasherOptionsAccessor.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Identity.Test -{ - internal class PasswordHasherOptionsAccessor : IOptions - { - public PasswordHasherOptions Value { get; } = new PasswordHasherOptions(); - } -} \ No newline at end of file diff --git a/test/Shared/PriorityOrderer.cs b/test/Shared/PriorityOrderer.cs index d12866a2cd..1e43b11408 100644 --- a/test/Shared/PriorityOrderer.cs +++ b/test/Shared/PriorityOrderer.cs @@ -9,19 +9,38 @@ namespace Microsoft.AspNetCore.Identity.Test using Xunit.Abstractions; using Xunit.Sdk; + /// + /// Test priority + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class TestPriorityAttribute : Attribute { + /// + /// ctor + /// + /// public TestPriorityAttribute(int priority) { Priority = priority; } + /// + /// Priority + /// public int Priority { get; private set; } } + /// + /// Used to run tests in order. + /// public class PriorityOrderer : ITestCaseOrderer { + /// + /// Orders tests cases + /// + /// + /// + /// public IEnumerable OrderTestCases(IEnumerable testCases) where XunitTestCase : ITestCase { var sortedMethods = new SortedDictionary>(); diff --git a/test/Shared/TestRole.cs b/test/Shared/TestRole.cs index 21e491120e..ea1dbf0778 100644 --- a/test/Shared/TestRole.cs +++ b/test/Shared/TestRole.cs @@ -35,6 +35,9 @@ namespace Microsoft.AspNetCore.Identity.Test /// public class TestRole where TKey : IEquatable { + /// + /// Constructor + /// public TestRole() { } /// @@ -60,6 +63,10 @@ namespace Microsoft.AspNetCore.Identity.Test /// Role name /// public virtual string Name { get; set; } + + /// + /// Normalized name used for equality + /// public virtual string NormalizedName { get; set; } /// diff --git a/test/Shared/TestRoleClaim.cs b/test/Shared/TestRoleClaim.cs index fbda178714..3f7a3ce6f1 100644 --- a/test/Shared/TestRoleClaim.cs +++ b/test/Shared/TestRoleClaim.cs @@ -5,6 +5,9 @@ using System; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// EntityType that represents one specific role claim + /// public class TestRoleClaim : TestRoleClaim { } /// diff --git a/test/Shared/TestUser.cs b/test/Shared/TestUser.cs index 744fa3d013..d08e747a28 100644 --- a/test/Shared/TestUser.cs +++ b/test/Shared/TestUser.cs @@ -6,30 +6,62 @@ using System.Collections.Generic; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// Test user class + /// public class TestUser : TestUser { + /// + /// Ctor + /// public TestUser() { Id = Guid.NewGuid().ToString(); } + /// + /// Ctor + /// + /// public TestUser(string userName) : this() { UserName = userName; } } + /// + /// Test user + /// + /// public class TestUser where TKey : IEquatable { + /// + /// ctor + /// public TestUser() { } + /// + /// ctor + /// + /// public TestUser(string userName) : this() { UserName = userName; } + /// + /// Id + /// public virtual TKey Id { get; set; } + + /// + /// Name + /// public virtual string UserName { get; set; } + + /// + /// normalized user name + /// public virtual string NormalizedUserName { get; set; } /// @@ -37,6 +69,9 @@ namespace Microsoft.AspNetCore.Identity.Test /// public virtual string Email { get; set; } + /// + /// normalized email + /// public virtual string NormalizedEmail { get; set; } /// @@ -89,9 +124,21 @@ namespace Microsoft.AspNetCore.Identity.Test /// public virtual int AccessFailedCount { get; set; } + /// + /// Navigation property + /// public virtual ICollection> Roles { get; private set; } = new List>(); + /// + /// Navigation property + /// public virtual ICollection> Claims { get; private set; } = new List>(); + /// + /// Navigation property + /// public virtual ICollection> Logins { get; private set; } = new List>(); + /// + /// Navigation property + /// public virtual ICollection> Tokens { get; private set; } = new List>(); } } diff --git a/test/Shared/TestUserClaim.cs b/test/Shared/TestUserClaim.cs index d34e99349b..cac773fbd6 100644 --- a/test/Shared/TestUserClaim.cs +++ b/test/Shared/TestUserClaim.cs @@ -5,6 +5,9 @@ using System; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// EntityType that represents one specific user claim + /// public class TestUserClaim : TestUserClaim { } /// diff --git a/test/Shared/TestUserLogin.cs b/test/Shared/TestUserLogin.cs index 9ad2fd9ff8..43d3b311d1 100644 --- a/test/Shared/TestUserLogin.cs +++ b/test/Shared/TestUserLogin.cs @@ -5,6 +5,9 @@ using System; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// Entity type for a user's login (i.e. facebook, google) + /// public class TestUserLogin : TestUserLogin { } /// diff --git a/test/Shared/TestUserRole.cs b/test/Shared/TestUserRole.cs index 9a3207bdaf..906cff96f9 100644 --- a/test/Shared/TestUserRole.cs +++ b/test/Shared/TestUserRole.cs @@ -5,6 +5,9 @@ using System; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// EntityType that represents a user belonging to a role + /// public class TestUserRole : TestUserRole { } /// diff --git a/test/Shared/TestUserToken.cs b/test/Shared/TestUserToken.cs index 8e9adfa18a..2df5fa5320 100644 --- a/test/Shared/TestUserToken.cs +++ b/test/Shared/TestUserToken.cs @@ -5,10 +5,13 @@ using System; namespace Microsoft.AspNetCore.Identity.Test { + /// + /// Entity type for a user's token + /// public class TestUserToken : TestUserToken { } /// - /// Entity type for a user's login (i.e. facebook, google) + /// Entity type for a user's token /// /// public class TestUserToken where TKey : IEquatable