From 9b29a11d8a767b18979fbd9167ac16413e6f66f9 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 9 Jan 2026 19:26:46 +0800 Subject: [PATCH 1/4] Use `AbpIdentityErrorDescriber` to localize error message instead of `AbpIdentityResultExtensions`. --- .../Identity/AbpIdentityResultExtensions.cs | 113 ----- .../Abp/Identity/AbpIdentityErrorDescriber.cs | 205 +++++++- .../Identity/AbpIdentityResultException.cs | 25 +- .../Abp/Identity/AbpIdentityUserValidator.cs | 18 +- .../Volo/Abp/Identity/IdentityUserManager.cs | 2 +- .../Identity/IdentityUserAppService_Tests.cs | 2 +- .../AbpIdentityErrorDescriber_Tests.cs | 468 ++++++++++++++++++ .../AbpIdentityResultException_Tests.cs | 51 -- 8 files changed, 670 insertions(+), 214 deletions(-) create mode 100644 modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs delete mode 100644 modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs index e851cbc6cc..0c1c13c08c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs @@ -1,58 +1,10 @@ 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.Text.Formatting; namespace Microsoft.AspNetCore.Identity; public static class AbpIdentityResultExtensions { - private static readonly Dictionary IdentityStrings = new Dictionary(); - - 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) { if (identityResult.Succeeded) @@ -68,71 +20,6 @@ public static class AbpIdentityResultExtensions 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(); - } - - if (FormattedStringValueExtracter.IsMatch(error.Description, englishString, out var values)) - { - return values; - } - - return Array.Empty(); - } - - 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().ToArray()); - } - } - } - - return localizer["Volo.Abp.Identity:DefaultError"]; - } - public static string GetResultAsString(this SignInResult signInResult) { if (signInResult.Succeeded) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityErrorDescriber.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityErrorDescriber.cs index 3dea11faea..64ee9e58d7 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityErrorDescriber.cs +++ b/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.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity.Localization; -using Volo.Abp.Localization; namespace Volo.Abp.Identity; @@ -19,15 +17,202 @@ public class AbpIdentityErrorDescriber : IdentityErrorDescriber 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(InvalidUserName), - Description = Localizer["Volo.Abp.Identity:InvalidUserName", userName ?? ""] - }; - } + Code = nameof(DefaultError), + Description = Localizer["Volo.Abp.Identity:DefaultError"] + }; + } + + 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"] + }; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs index 03a8c6f881..a5b4156d2b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs @@ -2,14 +2,10 @@ using System.Linq; using JetBrains.Annotations; 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; -public class AbpIdentityResultException : BusinessException, ILocalizeErrorMessage +public class AbpIdentityResultException : BusinessException { public IdentityResult IdentityResult { get; } @@ -20,23 +16,4 @@ public class AbpIdentityResultException : BusinessException, ILocalizeErrorMessa { IdentityResult = Check.NotNull(identityResult, nameof(identityResult)); } - - public virtual string LocalizeMessage(LocalizationContext context) - { - var localizer = context.LocalizerFactory.Create(); - - 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]; - } - } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs index 84d1a09c25..05c733c0c0 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityUserValidator.cs @@ -17,8 +17,6 @@ namespace Volo.Abp.Identity public virtual async Task ValidateAsync(UserManager manager, IdentityUser user) { - var describer = new IdentityErrorDescriber(); - Check.NotNull(manager, nameof(manager)); Check.NotNull(user, nameof(user)); @@ -27,36 +25,28 @@ namespace Volo.Abp.Identity var userName = await manager.GetUserNameAsync(user); if (userName == null) { - errors.Add(describer.InvalidUserName(null)); + errors.Add(manager.ErrorDescriber.InvalidUserName(null)); } else { var owner = await manager.FindByEmailAsync(userName); if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) { - errors.Add(new IdentityError - { - Code = "InvalidUserName", - Description = Localizer["Volo.Abp.Identity:InvalidUserName", userName] - }); + errors.Add(manager.ErrorDescriber.InvalidUserName(userName)); } } var email = await manager.GetEmailAsync(user); if (email == null) { - errors.Add(describer.InvalidEmail(null)); + errors.Add(manager.ErrorDescriber.InvalidEmail(null)); } else { var owner = await manager.FindByNameAsync(email); if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) { - errors.Add(new IdentityError - { - Code = "InvalidEmail", - Description = Localizer["Volo.Abp.Identity:InvalidEmail", email] - }); + errors.Add(manager.ErrorDescriber.InvalidEmail(email)); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index a4fc897bf4..41da1e5c22 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -553,6 +553,6 @@ public class IdentityUserManager : UserManager, IDomainService } 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))); } } diff --git a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs index 3272929c3e..acd50b7947 100644 --- a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs +++ b/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(async () => { 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] diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs new file mode 100644 index 0000000000..174bae0dbc --- /dev/null +++ b/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(); + + 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().Begin()) + { + var user = new IdentityUser(Guid.NewGuid(), "um_localize_user", "um_localize_user@abp.io"); + + using (CultureHelper.Use("en")) + { + var userManager = GetRequiredService(); + 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(); + 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>().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(); + 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(); + 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>().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(); + 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(); + 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>().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}' 已存在。"); + } + } + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs deleted file mode 100644 index 4a7860563e..0000000000 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs +++ /dev/null @@ -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."); - } - } -} From 2ef4fdef20c39e4eebcbf8c3753332f50b92121c Mon Sep 17 00:00:00 2001 From: Ma Liming Date: Fri, 9 Jan 2026 20:57:30 +0800 Subject: [PATCH 2/4] Update modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs index 174bae0dbc..86200286fb 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs @@ -446,7 +446,7 @@ public class AbpIdentityErrorDescriber_Tests : AbpIdentityDomainTestBase 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); + ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true); (await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue(); var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code"); From 289087c2825161717435e47c597564c5aba4a117 Mon Sep 17 00:00:00 2001 From: Ma Liming Date: Fri, 9 Jan 2026 20:57:36 +0800 Subject: [PATCH 3/4] Update modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs index 86200286fb..77554a3881 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs @@ -326,7 +326,7 @@ public class AbpIdentityErrorDescriber_Tests : AbpIdentityDomainTestBase 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); + ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true); (await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue(); var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code"); From 73cf4792e10dbc7631ba3ca628d6d6409266d367 Mon Sep 17 00:00:00 2001 From: Ma Liming Date: Fri, 9 Jan 2026 20:57:44 +0800 Subject: [PATCH 4/4] Update modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs index 77554a3881..a7f68ec0b2 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityErrorDescriber_Tests.cs @@ -205,7 +205,7 @@ public class AbpIdentityErrorDescriber_Tests : AbpIdentityDomainTestBase 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); + ObjectHelper.TrySetProperty(recoveryUser, x => x.TwoFactorEnabled, () => true); (await userManager.CreateAsync(recoveryUser, "Abp123!")).Succeeded.ShouldBeTrue(); var recoveryResult = await userManager.RedeemTwoFactorRecoveryCodeAsync(recoveryUser, "invalid-code");