package com.baiyue.integration.controller; import com.baiyue.common.web.GlobalExceptionHandler; import com.baiyue.common.web.LongIdJacksonConfiguration; import com.baiyue.integration.dto.lingxing.LingxingWmsDtos.OrderSummary; import com.baiyue.integration.dto.lingxing.LingxingWmsDtos.PageResult; import com.baiyue.integration.provider.lingxing.LingxingWmsClient; import com.baiyue.integration.provider.lingxing.LingxingWmsException; import com.baiyue.integration.security.InternalServiceTokenGuard; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.time.Instant; import java.util.List; import java.util.UUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class LingxingInternalControllerTest { private static final String PATH = "/internal/integrations/lingxing/wms/outbound-orders/query"; private static final String BODY = """ {"startTime":"2026-09-12T08:00:00","endTime":"2026-10-12T08:00:00","current":1,"size":1} """; private final String token = UUID.randomUUID().toString(); private LingxingWmsClient client; private MockMvc mvc; @BeforeEach void setUp() { var guard = new InternalServiceTokenGuard(); ReflectionTestUtils.setField(guard, "token", token); ReflectionTestUtils.invokeMethod(guard, "initializeToken"); client = mock(LingxingWmsClient.class); var builder = Jackson2ObjectMapperBuilder.json(); try (var context = new AnnotationConfigApplicationContext(LongIdJacksonConfiguration.class)) { context.getBean(Jackson2ObjectMapperBuilderCustomizer.class).customize(builder); } mvc = MockMvcBuilders.standaloneSetup(new LingxingInternalController(guard, client)) .setMessageConverters(new MappingJackson2HttpMessageConverter(builder.build())) .setControllerAdvice(new GlobalExceptionHandler()).build(); } @Test void missingOrWrongInternalTokenCannotCallProvider() throws Exception { mvc.perform(post(PATH).contentType(MediaType.APPLICATION_JSON).content(BODY)) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value("A0401")); mvc.perform(post(PATH).header("X-Internal-Service-Token", "wrong") .contentType(MediaType.APPLICATION_JSON).content(BODY)) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value("A0401")); verifyNoInteractions(client); } @Test void authenticatedReadReturnsOnlyOutboundSummary() throws Exception { when(client.queryPage(any())).thenReturn(new PageResult( List.of(new OrderSummary("TEST-OUT-001", "TEST-SRC-001", "TEST-CUSTOMER", "TEST-WH", 100, "2026-10-01 08:00:00", "2026-10-02 08:00:00")), 1L, 1, 1, 200, Instant.parse("2026-10-12T00:00:00Z"))); mvc.perform(post(PATH).header("X-Internal-Service-Token", token) .contentType(MediaType.APPLICATION_JSON).content(BODY)) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value("200")) .andExpect(jsonPath("$.data.total").isString()).andExpect(jsonPath("$.data.total").value("1")) .andExpect(jsonPath("$.data.records[0].deliveryNo").value("TEST-OUT-001")) .andExpect(jsonPath("$.data.records[0].recipient").doesNotExist()); } @Test void invalidPageCannotReachProvider() throws Exception { mvc.perform(post(PATH).header("X-Internal-Service-Token", token) .contentType(MediaType.APPLICATION_JSON).content(BODY.replace("\"current\":1", "\"current\":0"))) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value("A0422")); verifyNoInteractions(client); } @Test void providerFailureRemainsAnExplicitBusinessFailure() throws Exception { when(client.queryPage(any())).thenThrow(new LingxingWmsException( "LINGXING_WMS_HTTP_RATE_LIMITED", "领星 WMS 请求受到限流", true, 429, null)); mvc.perform(post(PATH).header("X-Internal-Service-Token", token) .contentType(MediaType.APPLICATION_JSON).content(BODY)) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value("S0503")) .andExpect(jsonPath("$.data.errorCode").value("LINGXING_WMS_HTTP_RATE_LIMITED")) .andExpect(jsonPath("$.data.upstreamStatus").value(429)) .andExpect(jsonPath("$.data.records").doesNotExist()); } }