149 lines
8.2 KiB
Java
149 lines
8.2 KiB
Java
package com.baiyue.auth.service;
|
|
|
|
import com.baiyue.auth.client.IamOnboardingClient;
|
|
import com.baiyue.auth.dto.MerchantRegisterRequest;
|
|
import com.baiyue.auth.entity.AuthAccount;
|
|
import com.baiyue.auth.repository.AuthAccountRepository;
|
|
import com.baiyue.common.core.api.ApiResponse;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import org.springframework.test.context.TestPropertySource;
|
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
|
import org.springframework.transaction.support.TransactionSynchronization;
|
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
|
|
import jakarta.annotation.Resource;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.doThrow;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
import static org.mockito.Mockito.never;
|
|
|
|
@SpringJUnitConfig(MerchantRegistrationService.class)
|
|
@TestPropertySource(properties = "baiyue.security.internal-service-token=internal-token")
|
|
class MerchantRegistrationServiceTest {
|
|
@MockBean private AuthAccountRepository accounts;
|
|
@MockBean private PasswordEncoder encoder;
|
|
@MockBean private IamOnboardingClient onboarding;
|
|
@Resource private MerchantRegistrationService service;
|
|
@Test
|
|
void registeredAuthAccountUsesTheOwnerIdentityReturnedByIamOnboarding() {
|
|
when(encoder.encode("Password123")).thenReturn("encoded-password");
|
|
when(onboarding.register(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
|
new IamOnboardingClient.OnboardingResponse(10001L, 30001L, "PROFILE_REQUIRED", "PENDING")));
|
|
|
|
var result = service.register(new MerchantRegisterRequest(
|
|
"registered_owner", "Password123", "注册人", "测试企业", "张三",
|
|
"13800138000", "owner@example.com", "CN", "深圳市宝安区"));
|
|
|
|
ArgumentCaptor<IamOnboardingClient.OnboardingRequest> onboardingRequest =
|
|
ArgumentCaptor.forClass(IamOnboardingClient.OnboardingRequest.class);
|
|
ArgumentCaptor<AuthAccount> storedAccount = ArgumentCaptor.forClass(AuthAccount.class);
|
|
verify(onboarding).register(eq("internal-token"), onboardingRequest.capture());
|
|
verify(accounts).insert(storedAccount.capture());
|
|
|
|
assertEquals(onboardingRequest.getValue().authUserId(), storedAccount.getValue().getId());
|
|
assertEquals(result.accountId(), storedAccount.getValue().getId());
|
|
assertEquals(10001L, storedAccount.getValue().getDefaultTenantId());
|
|
assertEquals(10001L, storedAccount.getValue().getOperatorTenantId());
|
|
assertEquals(30001L, storedAccount.getValue().getDefaultMembershipId());
|
|
assertEquals("MERCHANT", storedAccount.getValue().getDefaultAppCode());
|
|
assertEquals("ACTIVE", storedAccount.getValue().getAccountStatus());
|
|
assertEquals("13800138000", storedAccount.getValue().getPhone());
|
|
assertEquals("13800138000", onboardingRequest.getValue().administratorPhone());
|
|
assertEquals("encoded-password", storedAccount.getValue().getPasswordHash());
|
|
assertEquals("LOGIN_AND_COMPLETE_QUALIFICATIONS", result.nextStep());
|
|
assertEquals("PENDING", result.dimensionInitializationStatus());
|
|
}
|
|
|
|
@Test
|
|
void exactIncompleteRegistrationRetryReturnsTheExistingRegistration() {
|
|
AuthAccount account = new AuthAccount();
|
|
account.setId(9001L);
|
|
account.setUsername("registered_owner");
|
|
account.setPhone("13800138000");
|
|
account.setPasswordHash("encoded-password");
|
|
account.setAccountStatus("ACTIVE");
|
|
account.setDefaultAppCode("MERCHANT");
|
|
when(accounts.findByUsername("registered_owner")).thenReturn(account);
|
|
when(accounts.findByPhone("13800138000")).thenReturn(account);
|
|
when(encoder.matches("Password123", "encoded-password")).thenReturn(true);
|
|
when(onboarding.registration("internal-token", 9001L)).thenReturn(ApiResponse.success(
|
|
new IamOnboardingClient.OnboardingResponse(10001L, 30001L, "PROFILE_REQUIRED", "READY")));
|
|
|
|
var result = service.register(new MerchantRegisterRequest(
|
|
"registered_owner", "Password123", "注册人", "测试企业", null,
|
|
"13800138000", "owner@example.com", "CN", "深圳市宝安区"));
|
|
|
|
assertEquals(9001L, result.accountId());
|
|
assertEquals(10001L, result.tenantId());
|
|
assertEquals("READY", result.dimensionInitializationStatus());
|
|
verify(onboarding, never()).register(eq("internal-token"), any());
|
|
verify(accounts, never()).insert(any());
|
|
}
|
|
|
|
@Test
|
|
void phoneOwnedByAnotherUsernameDirectsTheUserBackToLogin() {
|
|
AuthAccount account = new AuthAccount();
|
|
account.setId(9001L);
|
|
account.setUsername("registered_owner");
|
|
account.setPhone("13800138000");
|
|
when(accounts.findByPhone("13800138000")).thenReturn(account);
|
|
|
|
var failure = assertThrows(com.baiyue.common.core.exception.BizException.class,
|
|
() -> service.register(new MerchantRegisterRequest(
|
|
"another_owner", "Password123", "注册人", "测试企业", null,
|
|
"13800138000", "owner@example.com", "CN", "深圳市宝安区")));
|
|
|
|
assertEquals("该手机号已注册,请直接登录后继续完善企业资质", failure.getMessage());
|
|
verify(onboarding, never()).register(eq("internal-token"), any());
|
|
}
|
|
|
|
@Test
|
|
void failedAuthInsertCompensatesTheIamRegistration() {
|
|
when(encoder.encode("Password123")).thenReturn("encoded-password");
|
|
when(onboarding.register(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
|
new IamOnboardingClient.OnboardingResponse(10001L, 30001L, "PROFILE_REQUIRED", "PENDING")));
|
|
when(onboarding.compensate(eq("internal-token"), any())).thenReturn(ApiResponse.success(null));
|
|
doThrow(new IllegalStateException("auth database unavailable")).when(accounts).insert(any());
|
|
|
|
assertThrows(IllegalStateException.class, () -> service.register(new MerchantRegisterRequest(
|
|
"failed_owner", "Password123", "注册人", "测试企业", "张三",
|
|
"13800138000", "owner@example.com", "CN", "深圳市宝安区")));
|
|
|
|
ArgumentCaptor<IamOnboardingClient.OnboardingRequest> onboardingRequest =
|
|
ArgumentCaptor.forClass(IamOnboardingClient.OnboardingRequest.class);
|
|
ArgumentCaptor<Long> compensationUserId = ArgumentCaptor.forClass(Long.class);
|
|
verify(onboarding).register(eq("internal-token"), onboardingRequest.capture());
|
|
verify(onboarding).compensate(eq("internal-token"), compensationUserId.capture());
|
|
assertEquals(onboardingRequest.getValue().authUserId(), compensationUserId.getValue());
|
|
}
|
|
|
|
@Test
|
|
void rolledBackAuthTransactionCompensatesAfterTheLocalTransactionCompletes() {
|
|
when(encoder.encode("Password123")).thenReturn("encoded-password");
|
|
when(onboarding.register(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
|
new IamOnboardingClient.OnboardingResponse(10001L, 30001L, "PROFILE_REQUIRED", "PENDING")));
|
|
when(onboarding.compensate(eq("internal-token"), any())).thenReturn(ApiResponse.success(null));
|
|
|
|
TransactionSynchronizationManager.initSynchronization();
|
|
try {
|
|
service.register(new MerchantRegisterRequest(
|
|
"rollback_owner", "Password123", "注册人", "测试企业", "张三",
|
|
"13800138000", "owner@example.com", "CN", "深圳市宝安区"));
|
|
var synchronizations = TransactionSynchronizationManager.getSynchronizations();
|
|
assertEquals(1, synchronizations.size());
|
|
synchronizations.get(0).afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
|
|
verify(onboarding).compensate(eq("internal-token"), any());
|
|
} finally {
|
|
TransactionSynchronizationManager.clearSynchronization();
|
|
}
|
|
}
|
|
}
|