Files
ccdi/ccdi-lsfx/src/test/java/com/ruoyi/lsfx/controller/CreditParseControllerTest.java

334 lines
17 KiB
Java
Raw Normal View History

2026-03-23 15:54:50 +08:00
package com.ruoyi.lsfx.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
2026-03-23 15:54:50 +08:00
import com.ruoyi.common.core.domain.AjaxResult;
2026-07-20 15:35:54 +08:00
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
2026-03-23 16:01:46 +08:00
import com.ruoyi.lsfx.client.CreditParseClient;
2026-07-20 15:35:54 +08:00
import com.ruoyi.lsfx.domain.CallerContext;
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
2026-03-23 16:01:46 +08:00
import com.ruoyi.lsfx.exception.LsfxApiException;
import com.ruoyi.lsfx.util.HttpUtil;
2026-07-20 15:35:54 +08:00
import org.junit.jupiter.api.AfterEach;
2026-03-23 15:54:50 +08:00
import org.junit.jupiter.api.Test;
2026-03-23 16:01:46 +08:00
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
2026-03-23 16:01:46 +08:00
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
2026-07-20 15:35:54 +08:00
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
2026-03-23 15:54:50 +08:00
import java.util.ArrayList;
2026-07-20 15:35:54 +08:00
import java.util.Collections;
import java.util.List;
import java.util.Map;
2026-07-20 15:35:54 +08:00
import java.util.Set;
2026-03-23 15:54:50 +08:00
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
2026-03-23 16:01:46 +08:00
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
2026-03-23 16:01:46 +08:00
import static org.mockito.ArgumentMatchers.anyString;
2026-07-20 15:35:54 +08:00
import static org.mockito.ArgumentMatchers.any;
2026-03-23 16:01:46 +08:00
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
2026-03-23 16:01:46 +08:00
import static org.mockito.Mockito.when;
2026-03-23 15:54:50 +08:00
2026-03-23 16:01:46 +08:00
@ExtendWith(MockitoExtension.class)
2026-03-23 15:54:50 +08:00
class CreditParseControllerTest {
2026-07-20 15:35:54 +08:00
private static final CallerContext CALLER = CallerContext.of(7L, "tester");
2026-03-23 16:01:46 +08:00
@Mock
private CreditParseClient client;
@InjectMocks
private CreditParseController controller;
2026-03-23 15:54:50 +08:00
2026-07-20 15:35:54 +08:00
@AfterEach
void clearSecurityContext() {
SecurityContextHolder.clearContext();
}
2026-03-23 15:54:50 +08:00
@Test
void parse_shouldRejectBlankRemotePath() {
AjaxResult result = controller.parse(null, null);
2026-03-23 15:54:50 +08:00
assertEquals(500, result.get("code"));
}
@Test
void shouldUseDefaultModelWhenMissing() {
2026-07-20 15:35:54 +08:00
setLoginUser(CALLER.userId(), CALLER.username());
CreditParseInvokeResponse response = new CreditParseInvokeResponse();
response.setSuccess(true);
response.setCode(10000);
2026-03-23 16:01:46 +08:00
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
2026-07-20 15:35:54 +08:00
when(client.parse(eq(CALLER), eq("LXCUSTALL"), eq(remotePath))).thenReturn(response);
2026-03-23 16:01:46 +08:00
AjaxResult result = controller.parse(remotePath, null);
2026-03-23 16:01:46 +08:00
assertEquals(200, result.get("code"));
assertSame(response, result.get("data"));
}
@Test
void shouldReturnAjaxErrorWhenClientThrows() {
2026-07-20 15:35:54 +08:00
setLoginUser(CALLER.userId(), CALLER.username());
when(client.parse(any(CallerContext.class), anyString(), anyString()))
2026-03-23 16:01:46 +08:00
.thenThrow(new LsfxApiException("超时"));
AjaxResult result = controller.parse("http://127.0.0.1:62318/profile/credit-html/a.html", null);
2026-03-23 16:01:46 +08:00
assertEquals(500, result.get("code"));
}
2026-07-20 15:35:54 +08:00
private void setLoginUser(Long userId, String username) {
SysUser user = new SysUser();
user.setUserName(username);
LoginUser loginUser = new LoginUser(user, Set.of("*:*:*"));
loginUser.setUserId(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
void creditParseClient_shouldInitiateAndQueryResultWithSameSerialNum() throws Exception {
HttpUtil httpUtil = mock(HttpUtil.class);
CreditParseClient parseClient = new CreditParseClient();
ObjectMapper objectMapper = new ObjectMapper();
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
ReflectionTestUtils.setField(parseClient, "runType", "1");
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
String payload = "{\"lx_header\":{\"query_cert_no\":\"330101199001010011\",\"query_cust_name\":\"张三\",\"report_time\":\"2026-03-24\"},\"lx_debt\":{\"uncle_bank_house_bal\":\"1\"},\"lx_publictype\":{\"civil_cnt\":1}}";
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(initiateSuccessResponse());
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(resultSuccessResponse(objectMapper, payload, "ERR_SHOULD_IGNORE"));
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
2026-07-20 15:35:54 +08:00
CreditParseInvokeResponse actual = parseClient.parse(CALLER, remotePath);
assertEquals(true, actual.getSuccess());
assertEquals(10000, actual.getCode());
assertEquals("330101199001010011", actual.getData().getMappingOutputFields()
.getPayload().getLxHeader().get("query_cert_no"));
ArgumentCaptor<Map<String, Object>> initiateParamsCaptor = ArgumentCaptor.forClass((Class) Map.class);
verify(httpUtil).postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
initiateParamsCaptor.capture(),
isNull()
);
ArgumentCaptor<Map<String, Object>> resultParamsCaptor = ArgumentCaptor.forClass((Class) Map.class);
verify(httpUtil).postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
resultParamsCaptor.capture(),
isNull()
);
Map<String, Object> initiateParams = initiateParamsCaptor.getValue();
Map<String, Object> resultParams = resultParamsCaptor.getValue();
assertNotNull(initiateParams.get("serialNum"));
assertTrue(initiateParams.get("serialNum").toString().startsWith("CCDI_CREDIT_"));
assertEquals(initiateParams.get("serialNum"), resultParams.get("serialNum"));
assertEquals("999000", initiateParams.get("orgCode"));
assertEquals("999000", resultParams.get("orgCode"));
assertEquals("1", initiateParams.get("runType"));
assertEquals("1", resultParams.get("runType"));
assertEquals(remotePath, initiateParams.get("remotePath"));
assertEquals("LXCUSTALL", initiateParams.get("model"));
assertEquals(false, resultParams.containsKey("remotePath"));
assertEquals(false, resultParams.containsKey("model"));
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
void creditParseClient_shouldRetryEmptyPayloadFiveTimesWithTwoSecondInterval() throws Exception {
HttpUtil httpUtil = mock(HttpUtil.class);
TestableCreditParseClient parseClient = new TestableCreditParseClient();
ObjectMapper objectMapper = new ObjectMapper();
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
ReflectionTestUtils.setField(parseClient, "runType", "1");
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
String emptyPayloadResponse = "{\"success\":true,\"code\":10000,\"data\":{\"status\":1,\"reasonCode\":200,\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"ERR_SHOULD_IGNORE\"}}}";
String payload = "{\"lx_header\":{\"query_cert_no\":\"330101199001010011\",\"query_cust_name\":\"张三\",\"report_time\":\"2026-03-24\"}}";
String resultResponse = resultSuccessResponse(objectMapper, payload, "ERR_SHOULD_IGNORE");
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(initiateSuccessResponse());
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(emptyPayloadResponse, emptyPayloadResponse, emptyPayloadResponse, emptyPayloadResponse, resultResponse);
2026-07-20 15:35:54 +08:00
CreditParseInvokeResponse actual = parseClient.parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/a.html");
assertEquals("330101199001010011", actual.getData().getMappingOutputFields()
.getPayload().getLxHeader().get("query_cert_no"));
verify(httpUtil, times(5)).postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
);
assertEquals(List.of(2000L, 2000L, 2000L, 2000L), parseClient.getSleepIntervals());
}
@Test
void creditParseClient_shouldRejectInvalidOuterCode() throws Exception {
HttpUtil httpUtil = mock(HttpUtil.class);
TestableCreditParseClient parseClient = new TestableCreditParseClient();
ObjectMapper objectMapper = new ObjectMapper();
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
ReflectionTestUtils.setField(parseClient, "runType", "1");
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(initiateSuccessResponse());
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn("{\"success\":true,\"code\":99999,\"data\":{\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"0\"}}}");
LsfxApiException exception = assertThrows(LsfxApiException.class,
2026-07-20 15:35:54 +08:00
() -> parseClient.parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/a.html"));
assertTrue(exception.getMessage().contains("平台状态码异常"));
}
@Test
void creditParseClient_shouldRejectFailedResultStatus() throws Exception {
HttpUtil httpUtil = mock(HttpUtil.class);
TestableCreditParseClient parseClient = new TestableCreditParseClient();
ObjectMapper objectMapper = new ObjectMapper();
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
ReflectionTestUtils.setField(parseClient, "runType", "1");
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn(initiateSuccessResponse());
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn("{\"success\":true,\"code\":10000,\"data\":{\"reasonMessage\":\"解析失败\",\"reasonCode\":500,\"status\":0,\"mappingOutputFields\":{\"message\":\"结果异常\"}}}");
LsfxApiException exception = assertThrows(LsfxApiException.class,
2026-07-20 15:35:54 +08:00
() -> parseClient.parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/a.html"));
assertTrue(exception.getMessage().contains("解析失败"));
}
@Test
void creditParseClient_shouldRejectFailedInitiateStatus() throws Exception {
HttpUtil httpUtil = mock(HttpUtil.class);
TestableCreditParseClient parseClient = new TestableCreditParseClient();
ObjectMapper objectMapper = new ObjectMapper();
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
ReflectionTestUtils.setField(parseClient, "runType", "1");
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
when(httpUtil.postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeature"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
)).thenReturn("{\"success\":true,\"code\":10000,\"data\":{\"mappingOutputFields\":{\"message\":\"文件写入失败\"},\"reasonMessage\":\"文件写入失败\",\"reasonCode\":500,\"status\":0}}");
LsfxApiException exception = assertThrows(LsfxApiException.class,
2026-07-20 15:35:54 +08:00
() -> parseClient.parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/a.html"));
assertTrue(exception.getMessage().contains("文件写入失败"));
verify(httpUtil, times(0)).postUrlEncodedFormForString(
2026-07-20 15:35:54 +08:00
eq(CALLER),
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
isNull()
);
}
private static String initiateSuccessResponse() {
return "{\"success\":true,\"code\":10000,\"data\":{\"traceId\":\"TRACE-001\","
+ "\"mappingOutputFields\":{\"message\":\"文件写入成功,流水号为: CCDI_CREDIT_TEST\"},"
+ "\"reasonMessage\":\"Running successfully\",\"procCode\":\"999000\","
+ "\"reasonCode\":200,\"status\":1}}";
}
private static String resultSuccessResponse(ObjectMapper objectMapper, String payload, String statusCode) throws Exception {
return "{\"success\":true,\"code\":10000,\"data\":{\"status\":1,\"reasonCode\":200,"
+ "\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"" + statusCode + "\",\"payload\":"
+ objectMapper.writeValueAsString(payload) + "}}}";
}
private static class TestableCreditParseClient extends CreditParseClient {
private final List<Long> sleepIntervals = new ArrayList<>();
@Override
protected void sleepBeforeNextResultQuery(long intervalMillis) {
sleepIntervals.add(intervalMillis);
}
private List<Long> getSleepIntervals() {
return sleepIntervals;
}
}
2026-03-23 15:54:50 +08:00
}