Browse Source

Merge pull request #24595 from abpframework/AbpIdentityErrorDescriber

Use `AbpIdentityErrorDescriber` to localize error message instead of `AbpIdentityResultExtensions`.
pull/24642/head
Engincan VESKE 3 weeks ago
committed by GitHub
parent
commit
98678e7978
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 113
      modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs
  2. 205
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityErrorDescriber.cs
  3. 25
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs
  4. 18
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs
  5. 2
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs
  6. 2
      modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs
  7. 468
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs
  8. 51
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs

113
modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs

@ -1,58 +1,10 @@
using System; using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Resources;
using Microsoft.Extensions.Localization;
using Volo.Abp;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Text.Formatting;
namespace Microsoft.AspNetCore.Identity; namespace Microsoft.AspNetCore.Identity;
public static class AbpIdentityResultExtensions public static class AbpIdentityResultExtensions
{ {
private static readonly Dictionary<string, string> IdentityStrings = new Dictionary<string, string>();
static AbpIdentityResultExtensions()
{
var identityResourceManager = new ResourceManager("Microsoft.Extensions.Identity.Core.Resources", typeof(UserManager<>).Assembly);
var resourceSet = identityResourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, false);
if (resourceSet == null)
{
throw new AbpException("Can't get the ResourceSet of Identity.");
}
var iterator = resourceSet.GetEnumerator();
while (true)
{
if (!iterator.MoveNext())
{
break;
}
var key = iterator.Key?.ToString();
var value = iterator.Value?.ToString();
if (key != null && value != null)
{
IdentityStrings.Add(key, value);
}
}
if (IdentityStrings.ContainsKey("InvalidUserName"))
{
// The default text of `InvalidUserName` is `Username '{0}' is invalid, can only contain letters or digits.`
IdentityStrings["InvalidUserName"] = "Username '{0}' is invalid.";
}
IdentityStrings["PasswordInHistory"] = "Passwords must not match your last {0} passwords.";
if (!IdentityStrings.Any())
{
throw new AbpException("ResourceSet values of Identity is empty.");
}
}
public static void CheckErrors(this IdentityResult identityResult) public static void CheckErrors(this IdentityResult identityResult)
{ {
if (identityResult.Succeeded) if (identityResult.Succeeded)
@ -68,71 +20,6 @@ public static class AbpIdentityResultExtensions
throw new AbpIdentityResultException(identityResult); throw new AbpIdentityResultException(identityResult);
} }
public static string[] GetValuesFromErrorMessage(this IdentityResult identityResult, IStringLocalizer localizer)
{
if (identityResult.Succeeded)
{
throw new ArgumentException(
"identityResult.Succeeded should be false in order to get values from error.");
}
if (identityResult.Errors == null)
{
throw new ArgumentException("identityResult.Errors should not be null.");
}
var error = identityResult.Errors.First();
var englishString = IdentityStrings.GetOrDefault(error.Code);
if (englishString == null)
{
return Array.Empty<string>();
}
if (FormattedStringValueExtracter.IsMatch(error.Description, englishString, out var values))
{
return values;
}
return Array.Empty<string>();
}
public static string LocalizeErrors(this IdentityResult identityResult, IStringLocalizer localizer)
{
if (identityResult.Succeeded)
{
throw new ArgumentException("identityResult.Succeeded should be false in order to localize errors.");
}
if (identityResult.Errors == null)
{
throw new ArgumentException("identityResult.Errors should not be null.");
}
return identityResult.Errors.Select(err => LocalizeErrorMessage(err, localizer)).JoinAsString(", ");
}
public static string LocalizeErrorMessage(this IdentityError error, IStringLocalizer localizer)
{
var key = $"Volo.Abp.Identity:{error.Code}";
var localizedString = localizer[key];
if (!localizedString.ResourceNotFound)
{
var englishString = IdentityStrings.GetOrDefault(error.Code);
if (englishString != null)
{
if (FormattedStringValueExtracter.IsMatch(error.Description, englishString, out var values))
{
return string.Format(localizedString.Value, values.Cast<object>().ToArray());
}
}
}
return localizer["Volo.Abp.Identity:DefaultError"];
}
public static string GetResultAsString(this SignInResult signInResult) public static string GetResultAsString(this SignInResult signInResult)
{ {
if (signInResult.Succeeded) if (signInResult.Succeeded)

205
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityErrorDescriber.cs

@ -1,10 +1,8 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity.Localization; using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
@ -19,15 +17,202 @@ public class AbpIdentityErrorDescriber : IdentityErrorDescriber
Localizer = localizer; Localizer = localizer;
} }
public override IdentityError InvalidUserName([CanBeNull] string userName) public override IdentityError DefaultError()
{ {
using (CultureHelper.Use("en")) return new IdentityError
{ {
return new IdentityError Code = nameof(DefaultError),
{ Description = Localizer["Volo.Abp.Identity:DefaultError"]
Code = nameof(InvalidUserName), };
Description = Localizer["Volo.Abp.Identity:InvalidUserName", userName ?? ""] }
};
} public override IdentityError ConcurrencyFailure()
{
return new IdentityError
{
Code = nameof(ConcurrencyFailure),
Description = Localizer["Volo.Abp.Identity:ConcurrencyFailure"]
};
}
public override IdentityError PasswordMismatch()
{
return new IdentityError
{
Code = nameof(PasswordMismatch),
Description = Localizer["Volo.Abp.Identity:PasswordMismatch"]
};
}
public override IdentityError InvalidToken()
{
return new IdentityError
{
Code = nameof(InvalidToken),
Description = Localizer["Volo.Abp.Identity:InvalidToken"]
};
}
public override IdentityError RecoveryCodeRedemptionFailed()
{
return new IdentityError
{
Code = nameof(RecoveryCodeRedemptionFailed),
Description = Localizer["Volo.Abp.Identity:RecoveryCodeRedemptionFailed"]
};
}
public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError
{
Code = nameof(LoginAlreadyAssociated),
Description = Localizer["Volo.Abp.Identity:LoginAlreadyAssociated"]
};
}
public override IdentityError InvalidUserName(string? userName)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = Localizer["Volo.Abp.Identity:InvalidUserName", userName ?? ""]
};
}
public override IdentityError InvalidEmail(string? email)
{
return new IdentityError
{
Code = nameof(InvalidEmail),
Description = Localizer["Volo.Abp.Identity:InvalidEmail", email ?? ""]
};
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = Localizer["Volo.Abp.Identity:DuplicateUserName", userName]
};
}
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError
{
Code = nameof(DuplicateEmail),
Description = Localizer["Volo.Abp.Identity:DuplicateEmail", email]
};
}
public override IdentityError InvalidRoleName(string? role)
{
return new IdentityError
{
Code = nameof(InvalidRoleName),
Description = Localizer["Volo.Abp.Identity:InvalidRoleName", role ?? ""]
};
}
public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError
{
Code = nameof(DuplicateRoleName),
Description = Localizer["Volo.Abp.Identity:DuplicateRoleName", role]
};
}
public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError
{
Code = nameof(UserAlreadyHasPassword),
Description = Localizer["Volo.Abp.Identity:UserAlreadyHasPassword"]
};
}
public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError
{
Code = nameof(UserLockoutNotEnabled),
Description = Localizer["Volo.Abp.Identity:UserLockoutNotEnabled"]
};
}
public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError
{
Code = nameof(UserAlreadyInRole),
Description = Localizer["Volo.Abp.Identity:UserAlreadyInRole", role]
};
}
public override IdentityError UserNotInRole(string role)
{
return new IdentityError
{
Code = nameof(UserNotInRole),
Description = Localizer["Volo.Abp.Identity:UserNotInRole", role]
};
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = Localizer["Volo.Abp.Identity:PasswordTooShort", length]
};
}
public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
{
return new IdentityError
{
Code = nameof(PasswordRequiresUniqueChars),
Description = Localizer["Volo.Abp.Identity:PasswordRequiresUniqueChars", uniqueChars]
};
}
public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonAlphanumeric),
Description = Localizer["Volo.Abp.Identity:PasswordRequiresNonAlphanumeric"]
};
}
public override IdentityError PasswordRequiresDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresDigit),
Description = Localizer["Volo.Abp.Identity:PasswordRequiresDigit"]
};
}
public override IdentityError PasswordRequiresLower()
{
return new IdentityError
{
Code = nameof(PasswordRequiresLower),
Description = Localizer["Volo.Abp.Identity:PasswordRequiresLower"]
};
}
public override IdentityError PasswordRequiresUpper()
{
return new IdentityError
{
Code = nameof(PasswordRequiresUpper),
Description = Localizer["Volo.Abp.Identity:PasswordRequiresUpper"]
};
} }
} }

25
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs

@ -2,14 +2,10 @@
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
public class AbpIdentityResultException : BusinessException, ILocalizeErrorMessage public class AbpIdentityResultException : BusinessException
{ {
public IdentityResult IdentityResult { get; } public IdentityResult IdentityResult { get; }
@ -20,23 +16,4 @@ public class AbpIdentityResultException : BusinessException, ILocalizeErrorMessa
{ {
IdentityResult = Check.NotNull(identityResult, nameof(identityResult)); IdentityResult = Check.NotNull(identityResult, nameof(identityResult));
} }
public virtual string LocalizeMessage(LocalizationContext context)
{
var localizer = context.LocalizerFactory.Create<IdentityResource>();
SetData(localizer);
return IdentityResult.LocalizeErrors(localizer);
}
protected virtual void SetData(IStringLocalizer localizer)
{
var values = IdentityResult.GetValuesFromErrorMessage(localizer);
for (var index = 0; index < values.Length; index++)
{
Data[index.ToString()] = values[index];
}
}
} }

18
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs

@ -17,8 +17,6 @@ namespace Volo.Abp.Identity
public virtual async Task<IdentityResult> ValidateAsync(UserManager<IdentityUser> manager, IdentityUser user) public virtual async Task<IdentityResult> ValidateAsync(UserManager<IdentityUser> manager, IdentityUser user)
{ {
var describer = new IdentityErrorDescriber();
Check.NotNull(manager, nameof(manager)); Check.NotNull(manager, nameof(manager));
Check.NotNull(user, nameof(user)); Check.NotNull(user, nameof(user));
@ -27,36 +25,28 @@ namespace Volo.Abp.Identity
var userName = await manager.GetUserNameAsync(user); var userName = await manager.GetUserNameAsync(user);
if (userName == null) if (userName == null)
{ {
errors.Add(describer.InvalidUserName(null)); errors.Add(manager.ErrorDescriber.InvalidUserName(null));
} }
else else
{ {
var owner = await manager.FindByEmailAsync(userName); var owner = await manager.FindByEmailAsync(userName);
if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{ {
errors.Add(new IdentityError errors.Add(manager.ErrorDescriber.InvalidUserName(userName));
{
Code = "InvalidUserName",
Description = Localizer["Volo.Abp.Identity:InvalidUserName", userName]
});
} }
} }
var email = await manager.GetEmailAsync(user); var email = await manager.GetEmailAsync(user);
if (email == null) if (email == null)
{ {
errors.Add(describer.InvalidEmail(null)); errors.Add(manager.ErrorDescriber.InvalidEmail(null));
} }
else else
{ {
var owner = await manager.FindByNameAsync(email); var owner = await manager.FindByNameAsync(email);
if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{ {
errors.Add(new IdentityError errors.Add(manager.ErrorDescriber.InvalidEmail(email));
{
Code = "InvalidEmail",
Description = Localizer["Volo.Abp.Identity:InvalidEmail", email]
});
} }
} }

2
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs

@ -553,6 +553,6 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
} }
Logger.LogError($"Could not get a valid user name for the given email address: {email}, allowed characters: {Options.User.AllowedUserNameCharacters}, tried {maxTryCount} times."); Logger.LogError($"Could not get a valid user name for the given email address: {email}, allowed characters: {Options.User.AllowedUserNameCharacters}, tried {maxTryCount} times.");
throw new AbpIdentityResultException(IdentityResult.Failed(new IdentityErrorDescriber().InvalidUserName(userName))); throw new AbpIdentityResultException(IdentityResult.Failed(ErrorDescriber.InvalidUserName(userName)));
} }
} }

2
modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs

@ -162,7 +162,7 @@ public class IdentityUserAppService_Tests : AbpIdentityApplicationTestBase
(await Assert.ThrowsAsync<AbpIdentityResultException>(async () => (await Assert.ThrowsAsync<AbpIdentityResultException>(async () =>
{ {
await _userAppService.UpdateAsync(johnNash.Id, input); await _userAppService.UpdateAsync(johnNash.Id, input);
})).Message.ShouldContain("Optimistic concurrency failure"); })).Message.ShouldContain("Optimistic concurrency check has been failed. The entity you're working on has modified by another user. Please discard your changes and try again.");
} }
[Fact] [Fact]

468
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs

@ -0,0 +1,468 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp;
using Volo.Abp.Localization;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.Identity;
public class AbpIdentityErrorDescriber_Tests : AbpIdentityDomainTestBase
{
[Fact]
public void Should_Localize_Error_Messages()
{
var describer = GetRequiredService<IdentityErrorDescriber>();
using (CultureHelper.Use("en"))
{
describer.DefaultError().Description.ShouldBe("An unknown failure has occurred.");
describer.ConcurrencyFailure().Description.ShouldBe("Optimistic concurrency check has been failed. The entity you're working on has modified by another user. Please discard your changes and try again.");
describer.PasswordMismatch().Description.ShouldBe("Incorrect password.");
describer.InvalidToken().Description.ShouldBe("Invalid token.");
describer.RecoveryCodeRedemptionFailed().Description.ShouldBe("Recovery code redemption failed.");
describer.LoginAlreadyAssociated().Description.ShouldBe("A user with this login already exists.");
describer.InvalidUserName("john").Description.ShouldBe("Username 'john' is invalid.");
describer.InvalidEmail("john@abp.io").Description.ShouldBe("Email 'john@abp.io' is invalid.");
describer.DuplicateUserName("john").Description.ShouldBe("Username 'john' is already taken.");
describer.DuplicateEmail("john@abp.io").Description.ShouldBe("Email 'john@abp.io' is already taken.");
describer.InvalidRoleName("admin").Description.ShouldBe("Role name 'admin' is invalid.");
describer.DuplicateRoleName("admin").Description.ShouldBe("Role name 'admin' is already taken.");
describer.UserAlreadyHasPassword().Description.ShouldBe("User already has a password set.");
describer.UserLockoutNotEnabled().Description.ShouldBe("Lockout is not enabled for this user.");
describer.UserAlreadyInRole("admin").Description.ShouldBe("User already in role 'admin'.");
describer.UserNotInRole("admin").Description.ShouldBe("User is not in role 'admin'.");
describer.PasswordTooShort(6).Description.ShouldBe("Password length must be greater than 6 characters.");
describer.PasswordRequiresUniqueChars(3).Description.ShouldBe("Passwords must use at least 3 different characters.");
describer.PasswordRequiresNonAlphanumeric().Description.ShouldBe("Password must contain at least one non-alphanumeric character.");
describer.PasswordRequiresDigit().Description.ShouldBe("Passwords must have at least one digit ('0'-'9').");
describer.PasswordRequiresLower().Description.ShouldBe("Passwords must have at least one lowercase ('a'-'z').");
describer.PasswordRequiresUpper().Description.ShouldBe("Passwords must have at least one uppercase ('A'-'Z').");
}
using (CultureHelper.Use("tr"))
{
describer.DefaultError().Description.ShouldBe("Bilinmeyen bir hata oluştu.");
describer.ConcurrencyFailure().Description.ShouldBe("İyimser eşzamanlılık denetimi başarısız oldu. Üzerinde çalıştığınız varlık başka bir kullanıcı tarafından değiştirildi. Lütfen değişikliklerinizi geri alın ve tekrar deneyin.");
describer.PasswordMismatch().Description.ShouldBe("Hatalı şifre.");
describer.InvalidToken().Description.ShouldBe("Geçersiz token.");
describer.RecoveryCodeRedemptionFailed().Description.ShouldBe("Kurtarma kodu kullanılamadı.");
describer.LoginAlreadyAssociated().Description.ShouldBe("Bu giriş bilgilerine sahip bir kullanıcı zaten var.");
describer.InvalidUserName("john").Description.ShouldBe("'john' kullanıcı adı geçersiz.");
describer.InvalidEmail("john@abp.io").Description.ShouldBe("'john@abp.io' email adresi hatalı.");
describer.DuplicateUserName("john").Description.ShouldBe("'john' kullanıcı adı zaten alınmış.");
describer.DuplicateEmail("john@abp.io").Description.ShouldBe("'john@abp.io' email adresi zaten alınmış.");
describer.InvalidRoleName("admin").Description.ShouldBe("'admin' rol ismi geçersizdir.");
describer.DuplicateRoleName("admin").Description.ShouldBe("'admin' rol ismi zaten alınmış.");
describer.UserAlreadyHasPassword().Description.ShouldBe("Kullanıcının zaten bir şifresi var.");
describer.UserLockoutNotEnabled().Description.ShouldBe("Bu kullanıcı için hesap kilitleme etkin değil.");
describer.UserAlreadyInRole("admin").Description.ShouldBe("Kullanıcı zaten 'admin' rolünde.");
describer.UserNotInRole("admin").Description.ShouldBe("Kullanıcı 'admin' rolünde değil.");
describer.PasswordTooShort(6).Description.ShouldBe("Şifre uzunluğu 6 karakterden uzun olmalıdır.");
describer.PasswordRequiresUniqueChars(3).Description.ShouldBe("Şifre en az 3 farklı karakter içermeli.");
describer.PasswordRequiresNonAlphanumeric().Description.ShouldBe("Parola en az bir alfasayısal olmayan karakter içermelidir.");
describer.PasswordRequiresDigit().Description.ShouldBe("Şifre en az bir sayı içermeli ('0'-'9').");
describer.PasswordRequiresLower().Description.ShouldBe("Şifre en az bir küçük harf içermeli ('a'-'z').");
describer.PasswordRequiresUpper().Description.ShouldBe("Şifre en az bir büyük harf içermeli ('A'-'Z').");
}
using (CultureHelper.Use("zh-Hans"))
{
describer.DefaultError().Description.ShouldBe("发生了一个未知错误。");
describer.ConcurrencyFailure().Description.ShouldBe("乐观并发检查失败. 你正在处理的对象已被其他用户修改. 请放弃你的更改, 然后重试。");
describer.PasswordMismatch().Description.ShouldBe("密码错误。");
describer.InvalidToken().Description.ShouldBe("token无效。");
describer.RecoveryCodeRedemptionFailed().Description.ShouldBe("恢复代码兑换失败。");
describer.LoginAlreadyAssociated().Description.ShouldBe("此登录名的用户已存在。");
describer.InvalidUserName("john").Description.ShouldBe("用户名 'john' 无效。");
describer.InvalidEmail("john@abp.io").Description.ShouldBe("邮箱 'john@abp.io' 无效。");
describer.DuplicateUserName("john").Description.ShouldBe("用户名 'john' 已存在。");
describer.DuplicateEmail("john@abp.io").Description.ShouldBe("邮箱 'john@abp.io' 已存在。");
describer.InvalidRoleName("admin").Description.ShouldBe("角色名 'admin' 无效。");
describer.DuplicateRoleName("admin").Description.ShouldBe("角色名 'admin' 已存在。");
describer.UserAlreadyHasPassword().Description.ShouldBe("用户已设置密码。");
describer.UserLockoutNotEnabled().Description.ShouldBe("该用户未启用锁定。");
describer.UserAlreadyInRole("admin").Description.ShouldBe("用户已具有角色 'admin'。");
describer.UserNotInRole("admin").Description.ShouldBe("用户不具有 'admin' 角色。");
describer.PasswordTooShort(6).Description.ShouldBe("密码长度必须大于 6 字符。");
describer.PasswordRequiresUniqueChars(3).Description.ShouldBe("密码至少包含3个唯一字符。");
describer.PasswordRequiresNonAlphanumeric().Description.ShouldBe("密码必须至少包含一个非字母数字字符。");
describer.PasswordRequiresDigit().Description.ShouldBe("密码至少包含一位数字 ('0'-'9')。");
describer.PasswordRequiresLower().Description.ShouldBe("密码至少包含一位小写字母 ('a'-'z')。");
describer.PasswordRequiresUpper().Description.ShouldBe("密码至少包含一位大写字母 ('A'-'Z')。");
}
}
[Fact]
public async Task Should_Localize_UserManager_Errors()
{
using (GetRequiredService<IUnitOfWorkManager>().Begin())
{
var user = new IdentityUser(Guid.NewGuid(), "um_localize_user", "um_localize_user@abp.io");
using (CultureHelper.Use("en"))
{
var userManager = GetRequiredService<IdentityUserManager>();
var result = await userManager.CreateAsync(user, "abc", validatePassword: true);
result.Succeeded.ShouldBeFalse();
var message = string.Join("; ", result.Errors.Select(e => e.Description));
message.ShouldContain("Password length must be greater than 6 characters.");
message.ShouldContain("Password must contain at least one non-alphanumeric character.");
message.ShouldContain("Passwords must have at least one digit ('0'-'9').");
message.ShouldContain("Passwords must have at least one uppercase ('A'-'Z').");
var invalidUser = new IdentityUser(Guid.NewGuid(), "invalid user?", "not-an-email");
var invalidResult = await userManager.CreateAsync(invalidUser, "Abp123!");
invalidResult.Succeeded.ShouldBeFalse();
var invalidMessage = string.Join("; ", invalidResult.Errors.Select(e => e.Description));
invalidMessage.ShouldContain("Username 'invalid user?' is invalid.");
invalidMessage.ShouldContain("Email 'not-an-email' is invalid.");
var firstUser = new IdentityUser(Guid.NewGuid(), "dup_user_en", "dup_user_en@abp.io");
(await userManager.CreateAsync(firstUser, "Abp123!")).Succeeded.ShouldBeTrue();
var duplicateUser = new IdentityUser(Guid.NewGuid(), "dup_user_en", "dup_user_en@abp.io");
var duplicateResult = await userManager.CreateAsync(duplicateUser, "Abp123!");
duplicateResult.Succeeded.ShouldBeFalse();
var duplicateMessage = string.Join("; ", duplicateResult.Errors.Select(e => e.Description));
duplicateMessage.ShouldContain("Username 'dup_user_en' is already taken.");
duplicateMessage.ShouldContain("Email 'dup_user_en@abp.io' is already taken.");
var persistedUser = await userManager.FindByIdAsync(firstUser.Id.ToString());
var addPasswordResult = await userManager.AddPasswordAsync(persistedUser, "Another123!");
addPasswordResult.Succeeded.ShouldBeFalse();
addPasswordResult.Errors.ShouldContain(e => e.Description == "User already has a password set.");
var roleManager = GetRequiredService<IdentityRoleManager>();
var roleName = "localized_role_en";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), roleName))).Succeeded.ShouldBeTrue();
var roleUser = new IdentityUser(Guid.NewGuid(), "role_user_en", "role_user_en@abp.io");
(await userManager.CreateAsync(roleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
(await userManager.AddToRoleAsync(roleUser, roleName)).Succeeded.ShouldBeTrue();
var alreadyInRoleResult = await userManager.AddToRoleAsync(roleUser, roleName);
alreadyInRoleResult.Succeeded.ShouldBeFalse();
alreadyInRoleResult.Errors.ShouldContain(e => e.Description == $"User already in role '{roleName}'.");
var notInRoleUser = new IdentityUser(Guid.NewGuid(), "notinrole_user_en", "notinrole_user_en@abp.io");
(await userManager.CreateAsync(notInRoleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
var notInRoleResult = await userManager.RemoveFromRoleAsync(notInRoleUser, roleName);
notInRoleResult.Succeeded.ShouldBeFalse();
notInRoleResult.Errors.ShouldContain(e => e.Description == $"User is not in role '{roleName}'.");
var loginUser1 = new IdentityUser(Guid.NewGuid(), "login_user1_en", "login_user1_en@abp.io");
(await userManager.CreateAsync(loginUser1, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginInfo = new UserLoginInfo("Google", "assoc_key_en", "Google");
(await userManager.AddLoginAsync(loginUser1, loginInfo)).Succeeded.ShouldBeTrue();
var loginUser2 = new IdentityUser(Guid.NewGuid(), "login_user2_en", "login_user2_en@abp.io");
(await userManager.CreateAsync(loginUser2, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginConflict = await userManager.AddLoginAsync(loginUser2, loginInfo);
loginConflict.Succeeded.ShouldBeFalse();
loginConflict.Errors.ShouldContain(e => e.Description == "A user with this login already exists.");
var noLowerUser = new IdentityUser(Guid.NewGuid(), "nolower_user_en", "nolower_user_en@abp.io");
var noLowerResult = await userManager.CreateAsync(noLowerUser, "ABC123!");
noLowerResult.Succeeded.ShouldBeFalse();
var noLowerMessage = string.Join("; ", noLowerResult.Errors.Select(e => e.Description));
noLowerMessage.ShouldContain("Passwords must have at least one lowercase ('a'-'z').");
var options = GetRequiredService<IOptions<IdentityOptions>>().Value;
var originalUniqueChars = options.Password.RequiredUniqueChars;
options.Password.RequiredUniqueChars = 3;
var uniqueUser = new IdentityUser(Guid.NewGuid(), "unique_user_en", "unique_user_en@abp.io");
var uniqueResult = await userManager.CreateAsync(uniqueUser, "aaaaaa!");
uniqueResult.Succeeded.ShouldBeFalse();
var uniqueMessage = string.Join("; ", uniqueResult.Errors.Select(e => e.Description));
uniqueMessage.ShouldContain("Passwords must use at least 3 different characters.");
options.Password.RequiredUniqueChars = originalUniqueChars;
var mismatchUser = new IdentityUser(Guid.NewGuid(), "mismatch_user_en", "mismatch_user_en@abp.io");
(await userManager.CreateAsync(mismatchUser, "Abp123!")).Succeeded.ShouldBeTrue();
var mismatchResult = await userManager.ChangePasswordAsync(mismatchUser, "WrongOld123!", "NewAbp123!");
mismatchResult.Succeeded.ShouldBeFalse();
mismatchResult.Errors.ShouldContain(e => e.Description == "Incorrect password.");
var recoveryUser = new IdentityUser(Guid.NewGuid(), "recovery_user_en", "recovery_user_en@abp.io");
ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true);
(await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue();
var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code");
recoveryResult.Succeeded.ShouldBeFalse();
recoveryResult.Errors.ShouldContain(e => e.Description == "Recovery code redemption failed.");
var invalidRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), ""));
invalidRoleResult.Succeeded.ShouldBeFalse();
invalidRoleResult.Errors.ShouldContain(e => e.Description == "Role name '' is invalid.");
var duplicateRoleName = "duplicate_role_en";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName))).Succeeded.ShouldBeTrue();
var duplicateRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName));
duplicateRoleResult.Succeeded.ShouldBeFalse();
duplicateRoleResult.Errors.ShouldContain(e => e.Description == $"Role name '{duplicateRoleName}' is already taken.");
}
// Recreate a fresh user per culture to avoid state issues
user = new IdentityUser(Guid.NewGuid(), "um_localize_user_tr", "um_localize_user_tr@abp.io");
using (CultureHelper.Use("tr"))
{
var userManager = GetRequiredService<IdentityUserManager>();
var result = await userManager.CreateAsync(user, "abc", validatePassword: true);
result.Succeeded.ShouldBeFalse();
var message = string.Join("; ", result.Errors.Select(e => e.Description));
message.ShouldContain("Şifre uzunluğu 6 karakterden uzun olmalıdır.");
message.ShouldContain("Parola en az bir alfasayısal olmayan karakter içermelidir.");
message.ShouldContain("Şifre en az bir sayı içermeli ('0'-'9').");
message.ShouldContain("Şifre en az bir büyük harf içermeli ('A'-'Z').");
var invalidUser = new IdentityUser(Guid.NewGuid(), "invalid user?", "not-an-email");
var invalidResult = await userManager.CreateAsync(invalidUser, "Abp123!");
invalidResult.Succeeded.ShouldBeFalse();
var invalidMessage = string.Join("; ", invalidResult.Errors.Select(e => e.Description));
invalidMessage.ShouldContain("'invalid user?' kullanıcı adı geçersiz.");
invalidMessage.ShouldContain("'not-an-email' email adresi hatalı.");
var firstUser = new IdentityUser(Guid.NewGuid(), "dup_user_tr", "dup_user_tr@abp.io");
(await userManager.CreateAsync(firstUser, "Abp123!")).Succeeded.ShouldBeTrue();
var duplicateUser = new IdentityUser(Guid.NewGuid(), "dup_user_tr", "dup_user_tr@abp.io");
var duplicateResult = await userManager.CreateAsync(duplicateUser, "Abp123!");
duplicateResult.Succeeded.ShouldBeFalse();
var duplicateMessage = string.Join("; ", duplicateResult.Errors.Select(e => e.Description));
duplicateMessage.ShouldContain("'dup_user_tr' kullanıcı adı zaten alınmış.");
duplicateMessage.ShouldContain("'dup_user_tr@abp.io' email adresi zaten alınmış.");
var persistedUser = await userManager.FindByIdAsync(firstUser.Id.ToString());
var addPasswordResult = await userManager.AddPasswordAsync(persistedUser, "Another123!");
addPasswordResult.Succeeded.ShouldBeFalse();
addPasswordResult.Errors.ShouldContain(e => e.Description == "Kullanıcının zaten bir şifresi var.");
var roleManager = GetRequiredService<IdentityRoleManager>();
var roleName = "localized_role_tr";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), roleName))).Succeeded.ShouldBeTrue();
var roleUser = new IdentityUser(Guid.NewGuid(), "role_user_tr", "role_user_tr@abp.io");
(await userManager.CreateAsync(roleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
(await userManager.AddToRoleAsync(roleUser, roleName)).Succeeded.ShouldBeTrue();
var alreadyInRoleResult = await userManager.AddToRoleAsync(roleUser, roleName);
alreadyInRoleResult.Succeeded.ShouldBeFalse();
alreadyInRoleResult.Errors.ShouldContain(e => e.Description == $"Kullanıcı zaten '{roleName}' rolünde.");
var notInRoleUser = new IdentityUser(Guid.NewGuid(), "notinrole_user_tr", "notinrole_user_tr@abp.io");
(await userManager.CreateAsync(notInRoleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
var notInRoleResult = await userManager.RemoveFromRoleAsync(notInRoleUser, roleName);
notInRoleResult.Succeeded.ShouldBeFalse();
notInRoleResult.Errors.ShouldContain(e => e.Description == $"Kullanıcı '{roleName}' rolünde değil.");
var loginUser1 = new IdentityUser(Guid.NewGuid(), "login_user1_tr", "login_user1_tr@abp.io");
(await userManager.CreateAsync(loginUser1, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginInfo = new UserLoginInfo("Google", "assoc_key_tr", "Google");
(await userManager.AddLoginAsync(loginUser1, loginInfo)).Succeeded.ShouldBeTrue();
var loginUser2 = new IdentityUser(Guid.NewGuid(), "login_user2_tr", "login_user2_tr@abp.io");
(await userManager.CreateAsync(loginUser2, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginConflict = await userManager.AddLoginAsync(loginUser2, loginInfo);
loginConflict.Succeeded.ShouldBeFalse();
loginConflict.Errors.ShouldContain(e => e.Description == "Bu giriş bilgilerine sahip bir kullanıcı zaten var.");
var noLowerUser = new IdentityUser(Guid.NewGuid(), "nolower_user_tr", "nolower_user_tr@abp.io");
var noLowerResult = await userManager.CreateAsync(noLowerUser, "ABC123!");
noLowerResult.Succeeded.ShouldBeFalse();
var noLowerMessage = string.Join("; ", noLowerResult.Errors.Select(e => e.Description));
noLowerMessage.ShouldContain("Şifre en az bir küçük harf içermeli ('a'-'z').");
var options = GetRequiredService<IOptions<IdentityOptions>>().Value;
var originalUniqueChars = options.Password.RequiredUniqueChars;
options.Password.RequiredUniqueChars = 3;
var uniqueUser = new IdentityUser(Guid.NewGuid(), "unique_user_tr", "unique_user_tr@abp.io");
var uniqueResult = await userManager.CreateAsync(uniqueUser, "aaaaaa!");
uniqueResult.Succeeded.ShouldBeFalse();
var uniqueMessage = string.Join("; ", uniqueResult.Errors.Select(e => e.Description));
uniqueMessage.ShouldContain("Şifre en az 3 farklı karakter içermeli.");
options.Password.RequiredUniqueChars = originalUniqueChars;
var mismatchUser = new IdentityUser(Guid.NewGuid(), "mismatch_user_tr", "mismatch_user_tr@abp.io");
(await userManager.CreateAsync(mismatchUser, "Abp123!")).Succeeded.ShouldBeTrue();
var mismatchResult = await userManager.ChangePasswordAsync(mismatchUser, "WrongOld123!", "NewAbp123!");
mismatchResult.Succeeded.ShouldBeFalse();
mismatchResult.Errors.ShouldContain(e => e.Description == "Hatalı şifre.");
var recoveryUser = new IdentityUser(Guid.NewGuid(), "recovery_user_tr", "recovery_user_tr@abp.io");
ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true);
(await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue();
var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code");
recoveryResult.Succeeded.ShouldBeFalse();
recoveryResult.Errors.ShouldContain(e => e.Description == "Kurtarma kodu kullanılamadı.");
var invalidRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), ""));
invalidRoleResult.Succeeded.ShouldBeFalse();
invalidRoleResult.Errors.ShouldContain(e => e.Description == "'' rol ismi geçersizdir.");
var duplicateRoleName = "duplicate_role_tr";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName))).Succeeded.ShouldBeTrue();
var duplicateRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName));
duplicateRoleResult.Succeeded.ShouldBeFalse();
duplicateRoleResult.Errors.ShouldContain(e => e.Description == $"'{duplicateRoleName}' rol ismi zaten alınmış.");
}
user = new IdentityUser(Guid.NewGuid(), "um_localize_user_zh", "um_localize_user_zh@abp.io");
using (CultureHelper.Use("zh-Hans"))
{
var userManager = GetRequiredService<IdentityUserManager>();
var result = await userManager.CreateAsync(user, "abc", validatePassword: true);
result.Succeeded.ShouldBeFalse();
var message = string.Join("; ", result.Errors.Select(e => e.Description));
message.ShouldContain("密码长度必须大于 6 字符。");
message.ShouldContain("密码必须至少包含一个非字母数字字符。");
message.ShouldContain("密码至少包含一位数字 ('0'-'9')。");
message.ShouldContain("密码至少包含一位大写字母 ('A'-'Z')。");
var invalidUser = new IdentityUser(Guid.NewGuid(), "invalid user?", "not-an-email");
var invalidResult = await userManager.CreateAsync(invalidUser, "Abp123!");
invalidResult.Succeeded.ShouldBeFalse();
var invalidMessage = string.Join("; ", invalidResult.Errors.Select(e => e.Description));
invalidMessage.ShouldContain("用户名 'invalid user?' 无效。");
invalidMessage.ShouldContain("邮箱 'not-an-email' 无效。");
var firstUser = new IdentityUser(Guid.NewGuid(), "dup_user_zh", "dup_user_zh@abp.io");
(await userManager.CreateAsync(firstUser, "Abp123!")).Succeeded.ShouldBeTrue();
var duplicateUser = new IdentityUser(Guid.NewGuid(), "dup_user_zh", "dup_user_zh@abp.io");
var duplicateResult = await userManager.CreateAsync(duplicateUser, "Abp123!");
duplicateResult.Succeeded.ShouldBeFalse();
var duplicateMessage = string.Join("; ", duplicateResult.Errors.Select(e => e.Description));
duplicateMessage.ShouldContain("用户名 'dup_user_zh' 已存在。");
duplicateMessage.ShouldContain("邮箱 'dup_user_zh@abp.io' 已存在。");
var persistedUser = await userManager.FindByIdAsync(firstUser.Id.ToString());
var addPasswordResult = await userManager.AddPasswordAsync(persistedUser, "Another123!");
addPasswordResult.Succeeded.ShouldBeFalse();
addPasswordResult.Errors.ShouldContain(e => e.Description == "用户已设置密码。");
var roleManager = GetRequiredService<IdentityRoleManager>();
var roleName = "localized_role_zh";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), roleName))).Succeeded.ShouldBeTrue();
var roleUser = new IdentityUser(Guid.NewGuid(), "role_user_zh", "role_user_zh@abp.io");
(await userManager.CreateAsync(roleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
(await userManager.AddToRoleAsync(roleUser, roleName)).Succeeded.ShouldBeTrue();
var alreadyInRoleResult = await userManager.AddToRoleAsync(roleUser, roleName);
alreadyInRoleResult.Succeeded.ShouldBeFalse();
alreadyInRoleResult.Errors.ShouldContain(e => e.Description == $"用户已具有角色 '{roleName}'。");
var notInRoleUser = new IdentityUser(Guid.NewGuid(), "notinrole_user_zh", "notinrole_user_zh@abp.io");
(await userManager.CreateAsync(notInRoleUser, "Abp123!"))
.Succeeded.ShouldBeTrue();
var notInRoleResult = await userManager.RemoveFromRoleAsync(notInRoleUser, roleName);
notInRoleResult.Succeeded.ShouldBeFalse();
notInRoleResult.Errors.ShouldContain(e => e.Description == $"用户不具有 '{roleName}' 角色。");
var loginUser1 = new IdentityUser(Guid.NewGuid(), "login_user1_zh", "login_user1_zh@abp.io");
(await userManager.CreateAsync(loginUser1, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginInfo = new UserLoginInfo("Google", "assoc_key_zh", "Google");
(await userManager.AddLoginAsync(loginUser1, loginInfo)).Succeeded.ShouldBeTrue();
var loginUser2 = new IdentityUser(Guid.NewGuid(), "login_user2_zh", "login_user2_zh@abp.io");
(await userManager.CreateAsync(loginUser2, "Abp123!"))
.Succeeded.ShouldBeTrue();
var loginConflict = await userManager.AddLoginAsync(loginUser2, loginInfo);
loginConflict.Succeeded.ShouldBeFalse();
loginConflict.Errors.ShouldContain(e => e.Description == "此登录名的用户已存在。");
var noLowerUser = new IdentityUser(Guid.NewGuid(), "nolower_user_zh", "nolower_user_zh@abp.io");
var noLowerResult = await userManager.CreateAsync(noLowerUser, "ABC123!");
noLowerResult.Succeeded.ShouldBeFalse();
var noLowerMessage = string.Join("; ", noLowerResult.Errors.Select(e => e.Description));
noLowerMessage.ShouldContain("密码至少包含一位小写字母 ('a'-'z')。");
var options = GetRequiredService<IOptions<IdentityOptions>>().Value;
var originalUniqueChars = options.Password.RequiredUniqueChars;
options.Password.RequiredUniqueChars = 3;
var uniqueUser = new IdentityUser(Guid.NewGuid(), "unique_user_zh", "unique_user_zh@abp.io");
var uniqueResult = await userManager.CreateAsync(uniqueUser, "aaaaaa!");
uniqueResult.Succeeded.ShouldBeFalse();
var uniqueMessage = string.Join("; ", uniqueResult.Errors.Select(e => e.Description));
uniqueMessage.ShouldContain("密码至少包含3个唯一字符。");
options.Password.RequiredUniqueChars = originalUniqueChars;
var mismatchUser = new IdentityUser(Guid.NewGuid(), "mismatch_user_zh", "mismatch_user_zh@abp.io");
(await userManager.CreateAsync(mismatchUser, "Abp123!")).Succeeded.ShouldBeTrue();
var mismatchResult = await userManager.ChangePasswordAsync(mismatchUser, "WrongOld123!", "NewAbp123!");
mismatchResult.Succeeded.ShouldBeFalse();
mismatchResult.Errors.ShouldContain(e => e.Description == "密码错误。");
var recoveryUser = new IdentityUser(Guid.NewGuid(), "recovery_user_zh", "recovery_user_zh@abp.io");
ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true);
(await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue();
var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code");
recoveryResult.Succeeded.ShouldBeFalse();
recoveryResult.Errors.ShouldContain(e => e.Description == "恢复代码兑换失败。");
var invalidRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), ""));
invalidRoleResult.Succeeded.ShouldBeFalse();
invalidRoleResult.Errors.ShouldContain(e => e.Description == "角色名 '' 无效。");
var duplicateRoleName = "duplicate_role_zh";
(await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName))).Succeeded.ShouldBeTrue();
var duplicateRoleResult = await roleManager.CreateAsync(new IdentityRole(Guid.NewGuid(), duplicateRoleName));
duplicateRoleResult.Succeeded.ShouldBeFalse();
duplicateRoleResult.Errors.ShouldContain(e => e.Description == $"角色名 '{duplicateRoleName}' 已存在。");
}
}
}
}

51
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs

@ -1,51 +0,0 @@
using Microsoft.AspNetCore.Identity;
using Shouldly;
using Volo.Abp.Localization;
using Xunit;
namespace Volo.Abp.Identity;
public class AbpIdentityResultException_Tests : AbpIdentityDomainTestBase
{
[Fact]
public void Should_Localize_Messages()
{
var exception = new AbpIdentityResultException(
IdentityResult.Failed(
new IdentityError
{
Code = "PasswordTooShort",
Description = "Passwords must be at least 6 characters."
},
new IdentityError
{
Code = "PasswordRequiresNonAlphanumeric",
Description = "Passwords must have at least one non alphanumeric character."
},
new IdentityError
{
Code = "UnknownError",
Description = "Unknown error"
}
)
);
using (CultureHelper.Use("tr"))
{
var localizeMessage = exception.LocalizeMessage(new LocalizationContext(ServiceProvider));
localizeMessage.ShouldContain("Şifre uzunluğu 6 karakterden uzun olmalıdır.");
localizeMessage.ShouldContain("Parola en az bir alfasayısal olmayan karakter içermeli");
localizeMessage.ShouldContain("Bilinmeyen bir hata oluştu.");
}
using (CultureHelper.Use("en"))
{
var localizeMessage = exception.LocalizeMessage(new LocalizationContext(ServiceProvider));
localizeMessage.ShouldContain("Password length must be greater than 6 characters.");
localizeMessage.ShouldContain("Password must contain at least one non-alphanumeric character.");
localizeMessage.ShouldContain("An unknown failure has occurred.");
}
}
}
Loading…
Cancel
Save