| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package com.zksy.web.controller.cockpit;
- import com.zksy.base.service.GasPipePointService;
- import com.zksy.base.service.ManholeBaseService;
- import com.zksy.base.service.PipeNetworkBaseService;
- import com.zksy.cockpit.foundation.CockpitReadException;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Feature;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Properties;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryService;
- import com.zksy.cockpit.foundation.CockpitCoordinateValidator;
- import org.junit.jupiter.api.BeforeEach;
- import org.junit.jupiter.api.Test;
- import org.springframework.test.web.servlet.MockMvc;
- import org.springframework.test.web.servlet.setup.MockMvcBuilders;
- import java.math.BigDecimal;
- import java.time.Clock;
- import java.time.Instant;
- import java.time.ZoneOffset;
- import java.util.Arrays;
- import java.util.Collections;
- import static org.hamcrest.Matchers.hasSize;
- import static org.hamcrest.Matchers.nullValue;
- import static org.mockito.Mockito.*;
- import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
- import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
- import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
- /** Standalone MVC tests lock JSON/routing, not the deployed authentication chain. */
- class CockpitFacilityGeometryControllerTest {
- private static final String URL = "/api/cockpit/v1/infrastructure/geometry";
- private CockpitFacilityGeometryService service;
- private MockMvc mvc;
- @BeforeEach
- void setUp() {
- service = mock(CockpitFacilityGeometryService.class);
- mvc = MockMvcBuilders.standaloneSetup(new CockpitFacilityGeometryController(service,
- Clock.fixed(Instant.parse("2026-10-12T02:00:00Z"), ZoneOffset.UTC))).build();
- }
- @Test
- void returnsGeoJsonInsideTheExistingAjaxEnvelopeWithoutRequiringAnAuthorizationHeader() throws Exception {
- PipeNetworkBaseService networks = mock(PipeNetworkBaseService.class);
- GasPipePointService points = mock(GasPipePointService.class);
- ManholeBaseService manholes = mock(ManholeBaseService.class);
- when(networks.list()).thenReturn(Collections.emptyList());
- when(points.list()).thenReturn(Collections.emptyList());
- when(manholes.list()).thenReturn(Collections.emptyList());
- CockpitFacilityGeometryResponse response = new CockpitFacilityGeometryService(networks, points, manholes,
- Clock.fixed(Instant.parse("2026-10-12T02:00:00Z"), ZoneOffset.UTC)).load();
- when(service.load()).thenReturn(response);
- mvc.perform(get(URL))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.code").value(200))
- .andExpect(jsonPath("$.data.type").value("FeatureCollection"))
- .andExpect(jsonPath("$.data.features", hasSize(0)))
- .andExpect(jsonPath("$.data.coverage", hasSize(9)))
- .andExpect(jsonPath("$.data.coverage[4].state").value("UNAVAILABLE"));
- }
- @Test
- void invalidGeometryIsExplicitJsonNullWithRawCoordinatesAndSameValidationShape() throws Exception {
- Properties properties = new Properties("gas_pipe_point", "1", "G1", "阀门", "GAS", "GAS_PIPE_POINT",
- null, "阀门", null, Collections.singletonList(Arrays.asList(null, new BigDecimal("28.4"))),
- CockpitCoordinateValidator.validate(null, new BigDecimal("28.4"), true),
- Collections.singletonList(CockpitCoordinateValidator.validate(null, new BigDecimal("28.4"), true)));
- when(service.load()).thenReturn(new CockpitFacilityGeometryResponse("2026-10-12T10:00:00+08:00",
- Collections.singletonList(new Feature("gas_pipe_point:1", null, properties)), Collections.emptyList()));
- mvc.perform(get(URL))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.data.features[0].type").value("Feature"))
- .andExpect(jsonPath("$.data.features[0].geometry").value(nullValue()))
- .andExpect(jsonPath("$.data.features[0].properties.networkId").value(nullValue()))
- .andExpect(jsonPath("$.data.features[0].properties.pointType").value("阀门"))
- .andExpect(jsonPath("$.data.features[0].properties.sourceCoordinates[0][0]").value(nullValue()))
- .andExpect(jsonPath("$.data.features[0].properties.coordinateValidation.state").value("INVALID"))
- .andExpect(jsonPath("$.data.features[0].properties.coordinateValidation.reason").value("MISSING_LONGITUDE"))
- .andExpect(jsonPath("$.data.features[0].properties.coordinateValidation.renderable").value(false))
- .andExpect(jsonPath("$.data.features[0].properties.coordinateValidation.crs").value("WGS84"));
- }
- @Test
- void sourceFailureReturns500AndStableCategoryRatherThanSuccess() throws Exception {
- when(service.load()).thenThrow(new CockpitReadException("private database details"));
- mvc.perform(get(URL).header("X-Request-Id", "geometry-failure"))
- .andExpect(status().isInternalServerError())
- .andExpect(jsonPath("$.code").value(500))
- .andExpect(jsonPath("$.data.category").value("SERVER_ERROR"))
- .andExpect(jsonPath("$.data.message").value("facility geometry source unavailable"))
- .andExpect(jsonPath("$.data.requestId").value("geometry-failure"));
- }
- @Test
- void serializesActualServicePointAndLineCoordinatesInLongitudeLatitudeOrder() throws Exception {
- PipeNetworkBaseService networks = mock(PipeNetworkBaseService.class);
- GasPipePointService points = mock(GasPipePointService.class);
- ManholeBaseService manholes = mock(ManholeBaseService.class);
- com.zksy.base.domain.PipeNetworkBase line = new com.zksy.base.domain.PipeNetworkBase();
- line.setNetworkId("gas-line");
- line.setNetworkType("0");
- line.setStartLongitude(new BigDecimal("110.4"));
- line.setStartLatitude(new BigDecimal("28.4"));
- line.setEndLongitude(new BigDecimal("110.6"));
- line.setEndLatitude(new BigDecimal("28.6"));
- com.zksy.base.domain.GasPipePoint point = new com.zksy.base.domain.GasPipePoint();
- point.setPointId(1L);
- point.setPointType("三通");
- point.setNetworkId("gas-line");
- point.setLongitude(new BigDecimal("110.5"));
- point.setLatitude(new BigDecimal("28.5"));
- when(networks.list()).thenReturn(Collections.singletonList(line));
- when(points.list()).thenReturn(Collections.singletonList(point));
- when(manholes.list()).thenReturn(Collections.emptyList());
- CockpitFacilityGeometryService actual = new CockpitFacilityGeometryService(networks, points, manholes,
- Clock.fixed(Instant.parse("2026-10-12T02:00:00Z"), ZoneOffset.UTC));
- mvc = MockMvcBuilders.standaloneSetup(new CockpitFacilityGeometryController(actual,
- Clock.fixed(Instant.parse("2026-10-12T02:00:00Z"), ZoneOffset.UTC))).build();
- mvc.perform(get(URL))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.data.features", hasSize(2)))
- .andExpect(jsonPath("$.data.features[0].geometry.type").value("LineString"))
- .andExpect(jsonPath("$.data.features[0].geometry.coordinates", hasSize(2)))
- .andExpect(jsonPath("$.data.features[0].geometry.coordinates[0][0]").value(110.4))
- .andExpect(jsonPath("$.data.features[0].geometry.coordinates[0][1]").value(28.4))
- .andExpect(jsonPath("$.data.features[0].geometry.coordinates[1][0]").value(110.6))
- .andExpect(jsonPath("$.data.features[0].geometry.coordinates[1][1]").value(28.6))
- .andExpect(jsonPath("$.data.features[1].geometry.type").value("Point"))
- .andExpect(jsonPath("$.data.features[1].geometry.coordinates", hasSize(2)))
- .andExpect(jsonPath("$.data.features[1].geometry.coordinates[0]").value(110.5))
- .andExpect(jsonPath("$.data.features[1].geometry.coordinates[1]").value(28.5))
- .andExpect(jsonPath("$.data.features[1].properties.pointType").value("三通"))
- .andExpect(jsonPath("$.data.features[1].properties.networkId").value("gas-line"))
- .andExpect(jsonPath("$.data.features[1].properties.coordinateValidation.state").value("UNVERIFIED"))
- .andExpect(jsonPath("$.data.features[1].properties.coordinateValidation.renderable").value(false));
- }
- @Test
- void transactionProxyFailureIsAlsoMappedToSanitized500() throws Exception {
- when(service.load()).thenThrow(new org.springframework.transaction.CannotCreateTransactionException(
- "private connection detail"));
- mvc.perform(get(URL).header("X-Request-Id", "transaction-failure"))
- .andExpect(status().isInternalServerError())
- .andExpect(jsonPath("$.code").value(500))
- .andExpect(jsonPath("$.data.category").value("SERVER_ERROR"))
- .andExpect(jsonPath("$.data.message").value("facility geometry source unavailable"))
- .andExpect(jsonPath("$.data.requestId").value("transaction-failure"));
- }
- @Test
- void postIsNotAWriteEntryPoint() throws Exception {
- mvc.perform(post(URL)).andExpect(status().isMethodNotAllowed());
- verifyNoInteractions(service);
- }
- }
|