|
|
@@ -0,0 +1,249 @@
|
|
|
+package com.zksy.web.controller.cockpit;
|
|
|
+
|
|
|
+import com.zksy.cockpit.device.CockpitDeviceBoundaryAdapter;
|
|
|
+import com.zksy.cockpit.device.CockpitDeviceBoundaryService;
|
|
|
+import com.zksy.cockpit.detail.CockpitEquipmentDetailAggregateService;
|
|
|
+import com.zksy.cockpit.equipment.CockpitEquipmentArchiveAdapter;
|
|
|
+import com.zksy.cockpit.equipment.CockpitEquipmentArchiveService;
|
|
|
+import com.zksy.cockpit.event.CockpitCurrentEventsAdapter;
|
|
|
+import com.zksy.cockpit.event.CockpitCurrentEventsService;
|
|
|
+import com.zksy.cockpit.event.CockpitEventReadAdapter;
|
|
|
+import com.zksy.cockpit.event.CockpitEventReadService;
|
|
|
+import com.zksy.cockpit.event.CockpitHandlingEfficiencyAdapter;
|
|
|
+import com.zksy.cockpit.event.CockpitHandlingEfficiencyService;
|
|
|
+import com.zksy.cockpit.event.CockpitHistoryAnalysisAdapter;
|
|
|
+import com.zksy.cockpit.event.CockpitHistoryAnalysisService;
|
|
|
+import com.zksy.cockpit.event.CockpitMultidimensionalAnalysisAdapter;
|
|
|
+import com.zksy.cockpit.event.CockpitMultidimensionalAnalysisService;
|
|
|
+import com.zksy.cockpit.foundation.CockpitFoundationAdapter;
|
|
|
+import com.zksy.cockpit.foundation.CockpitFoundationService;
|
|
|
+import com.zksy.cockpit.foundation.CockpitFreshnessAdapter;
|
|
|
+import com.zksy.cockpit.foundation.CockpitFreshnessService;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureBaseArchivesAdapter;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureBaseArchivesService;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureEmergencySupportAdapter;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureEmergencySupportService;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureManagementInformationAdapter;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureManagementInformationService;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureOperatingInformationAdapter;
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureOperatingInformationService;
|
|
|
+import com.zksy.cockpit.map.CockpitMapSnapshotAdapter;
|
|
|
+import com.zksy.cockpit.map.CockpitMapSnapshotService;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringDistributionAdapter;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringDistributionService;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringOverviewAdapter;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringOverviewService;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringSituationAdapter;
|
|
|
+import com.zksy.cockpit.monitoring.CockpitMonitoringSituationService;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewInfrastructureAdapter;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewInfrastructureService;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewMonitoringAdapter;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewMonitoringService;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewWarningSettingsAdapter;
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewWarningSettingsService;
|
|
|
+import com.zksy.cockpit.point.CockpitMonitoringPointDetailAdapter;
|
|
|
+import com.zksy.cockpit.point.CockpitMonitoringPointDetailService;
|
|
|
+import com.zksy.cockpit.type.CockpitTypeMetricsAdapter;
|
|
|
+import com.zksy.cockpit.type.CockpitTypeMetricsService;
|
|
|
+import com.zksy.cockpit.workorder.CockpitWorkOrderReadAdapter;
|
|
|
+import com.zksy.cockpit.workorder.CockpitWorkOrderReadService;
|
|
|
+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.time.Clock;
|
|
|
+import java.time.Instant;
|
|
|
+import java.time.ZoneOffset;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.verifyNoInteractions;
|
|
|
+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.jsonPath;
|
|
|
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
+
|
|
|
+/**
|
|
|
+ * End-to-end contract integration over the whole /api/cockpit/v1 surface.
|
|
|
+ *
|
|
|
+ * <p>Assembles the real cockpit services with read-only fake adapters, which is the
|
|
|
+ * highest-level seam agreed in the cockpit spec. It proves the four business modes,
|
|
|
+ * the map snapshot, detail partitions, WebSocket-aware delta providers and the
|
|
|
+ * read-only boundary all stay wired together, and that no adapter is ever touched by
|
|
|
+ * a rejected or mutating request.
|
|
|
+ */
|
|
|
+class CockpitContractIntegrationTest {
|
|
|
+
|
|
|
+ private List<Object> adapters;
|
|
|
+ private MockMvc mockMvc;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ Clock clock = Clock.fixed(Instant.parse("2026-09-02T02:00:00Z"), ZoneOffset.UTC);
|
|
|
+
|
|
|
+ CockpitFoundationAdapter foundation = mock(CockpitFoundationAdapter.class);
|
|
|
+ CockpitFreshnessAdapter freshness = mock(CockpitFreshnessAdapter.class);
|
|
|
+ CockpitMapSnapshotAdapter mapSnapshot = mock(CockpitMapSnapshotAdapter.class);
|
|
|
+ CockpitMonitoringPointDetailAdapter pointDetail = mock(CockpitMonitoringPointDetailAdapter.class);
|
|
|
+ CockpitEquipmentArchiveAdapter archive = mock(CockpitEquipmentArchiveAdapter.class);
|
|
|
+ CockpitTypeMetricsAdapter metrics = mock(CockpitTypeMetricsAdapter.class);
|
|
|
+ CockpitEventReadAdapter eventRead = mock(CockpitEventReadAdapter.class);
|
|
|
+ CockpitWorkOrderReadAdapter workOrder = mock(CockpitWorkOrderReadAdapter.class);
|
|
|
+ CockpitDeviceBoundaryAdapter deviceBoundary = mock(CockpitDeviceBoundaryAdapter.class);
|
|
|
+ CockpitOverviewInfrastructureAdapter overviewInfrastructure =
|
|
|
+ mock(CockpitOverviewInfrastructureAdapter.class);
|
|
|
+ CockpitOverviewMonitoringAdapter overviewMonitoring =
|
|
|
+ mock(CockpitOverviewMonitoringAdapter.class);
|
|
|
+ CockpitOverviewWarningSettingsAdapter overviewWarningSettings =
|
|
|
+ mock(CockpitOverviewWarningSettingsAdapter.class);
|
|
|
+ CockpitInfrastructureBaseArchivesAdapter baseArchives =
|
|
|
+ mock(CockpitInfrastructureBaseArchivesAdapter.class);
|
|
|
+ CockpitInfrastructureOperatingInformationAdapter operatingInformation =
|
|
|
+ mock(CockpitInfrastructureOperatingInformationAdapter.class);
|
|
|
+ CockpitInfrastructureManagementInformationAdapter managementInformation =
|
|
|
+ mock(CockpitInfrastructureManagementInformationAdapter.class);
|
|
|
+ CockpitInfrastructureEmergencySupportAdapter emergencySupport =
|
|
|
+ mock(CockpitInfrastructureEmergencySupportAdapter.class);
|
|
|
+ CockpitMonitoringOverviewAdapter monitoringOverview =
|
|
|
+ mock(CockpitMonitoringOverviewAdapter.class);
|
|
|
+ CockpitMonitoringDistributionAdapter monitoringDistribution =
|
|
|
+ mock(CockpitMonitoringDistributionAdapter.class);
|
|
|
+ CockpitMonitoringSituationAdapter monitoringSituation =
|
|
|
+ mock(CockpitMonitoringSituationAdapter.class);
|
|
|
+ CockpitCurrentEventsAdapter currentEvents = mock(CockpitCurrentEventsAdapter.class);
|
|
|
+ CockpitHistoryAnalysisAdapter historyAnalysis = mock(CockpitHistoryAnalysisAdapter.class);
|
|
|
+ CockpitHandlingEfficiencyAdapter handlingEfficiency =
|
|
|
+ mock(CockpitHandlingEfficiencyAdapter.class);
|
|
|
+ CockpitMultidimensionalAnalysisAdapter multidimensional =
|
|
|
+ mock(CockpitMultidimensionalAnalysisAdapter.class);
|
|
|
+
|
|
|
+ adapters = Arrays.asList(
|
|
|
+ foundation, freshness, mapSnapshot, pointDetail, archive, metrics, eventRead,
|
|
|
+ workOrder, deviceBoundary, overviewInfrastructure, overviewMonitoring,
|
|
|
+ overviewWarningSettings, baseArchives, operatingInformation, managementInformation,
|
|
|
+ emergencySupport, monitoringOverview, monitoringDistribution, monitoringSituation,
|
|
|
+ currentEvents, historyAnalysis, handlingEfficiency, multidimensional);
|
|
|
+
|
|
|
+ CockpitEquipmentArchiveService archiveService =
|
|
|
+ new CockpitEquipmentArchiveService(archive, clock);
|
|
|
+ CockpitTypeMetricsService metricsService = new CockpitTypeMetricsService(metrics, clock);
|
|
|
+ CockpitEventReadService eventService = new CockpitEventReadService(eventRead, clock);
|
|
|
+ CockpitWorkOrderReadService workOrderService =
|
|
|
+ new CockpitWorkOrderReadService(workOrder, clock);
|
|
|
+ CockpitEquipmentDetailAggregateService detailAggregate =
|
|
|
+ new CockpitEquipmentDetailAggregateService(
|
|
|
+ archiveService, metricsService, eventService, workOrderService, clock);
|
|
|
+
|
|
|
+ mockMvc = MockMvcBuilders.standaloneSetup(
|
|
|
+ new CockpitFoundationController(new CockpitFoundationService(foundation, clock)),
|
|
|
+ new CockpitFreshnessController(
|
|
|
+ new CockpitFreshnessService(freshness, clock, 60), clock),
|
|
|
+ new CockpitMapSnapshotController(new CockpitMapSnapshotService(mapSnapshot, clock)),
|
|
|
+ new CockpitMonitoringPointDetailController(
|
|
|
+ new CockpitMonitoringPointDetailService(pointDetail, clock)),
|
|
|
+ new CockpitEquipmentDetailAggregateController(detailAggregate),
|
|
|
+ new CockpitDeviceBoundaryController(
|
|
|
+ new CockpitDeviceBoundaryService(deviceBoundary, clock)),
|
|
|
+ new CockpitOverviewInfrastructureController(
|
|
|
+ new CockpitOverviewInfrastructureService(overviewInfrastructure, clock)),
|
|
|
+ new CockpitOverviewMonitoringController(
|
|
|
+ new CockpitOverviewMonitoringService(overviewMonitoring, clock)),
|
|
|
+ new CockpitOverviewWarningSettingsController(
|
|
|
+ new CockpitOverviewWarningSettingsService(overviewWarningSettings, clock)),
|
|
|
+ new CockpitInfrastructureBaseArchivesController(
|
|
|
+ new CockpitInfrastructureBaseArchivesService(baseArchives, clock)),
|
|
|
+ new CockpitInfrastructureOperatingInformationController(
|
|
|
+ new CockpitInfrastructureOperatingInformationService(
|
|
|
+ operatingInformation, clock, 60)),
|
|
|
+ new CockpitInfrastructureManagementInformationController(
|
|
|
+ new CockpitInfrastructureManagementInformationService(
|
|
|
+ managementInformation, clock)),
|
|
|
+ new CockpitInfrastructureEmergencySupportController(
|
|
|
+ new CockpitInfrastructureEmergencySupportService(emergencySupport, clock)),
|
|
|
+ new CockpitMonitoringOverviewController(
|
|
|
+ new CockpitMonitoringOverviewService(monitoringOverview, clock)),
|
|
|
+ new CockpitMonitoringDistributionController(
|
|
|
+ new CockpitMonitoringDistributionService(monitoringDistribution, clock)),
|
|
|
+ new CockpitMonitoringSituationController(
|
|
|
+ new CockpitMonitoringSituationService(monitoringSituation, clock)),
|
|
|
+ new CockpitCurrentEventsController(
|
|
|
+ new CockpitCurrentEventsService(currentEvents, clock)),
|
|
|
+ new CockpitHistoryAnalysisController(
|
|
|
+ new CockpitHistoryAnalysisService(historyAnalysis, clock)),
|
|
|
+ new CockpitHandlingEfficiencyController(
|
|
|
+ new CockpitHandlingEfficiencyService(handlingEfficiency, clock)),
|
|
|
+ new CockpitMultidimensionalAnalysisController(
|
|
|
+ new CockpitMultidimensionalAnalysisService(multidimensional, clock)))
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void everyCockpitReadEndpointIsWiredIntoTheSingleIntegrationEnvironment() throws Exception {
|
|
|
+ String[] endpoints = {
|
|
|
+ "/api/cockpit/v1/foundation",
|
|
|
+ "/api/cockpit/v1/map-snapshot",
|
|
|
+ "/api/cockpit/v1/device-boundary",
|
|
|
+ "/api/cockpit/v1/overview/infrastructure",
|
|
|
+ "/api/cockpit/v1/overview/monitoring",
|
|
|
+ "/api/cockpit/v1/overview/warning-settings",
|
|
|
+ "/api/cockpit/v1/infrastructure/base-archives",
|
|
|
+ "/api/cockpit/v1/infrastructure/operating-information",
|
|
|
+ "/api/cockpit/v1/infrastructure/management-information",
|
|
|
+ "/api/cockpit/v1/infrastructure/emergency-support",
|
|
|
+ "/api/cockpit/v1/monitoring/overview",
|
|
|
+ "/api/cockpit/v1/monitoring/distribution",
|
|
|
+ "/api/cockpit/v1/monitoring/situation"
|
|
|
+ };
|
|
|
+ for (String endpoint : endpoints) {
|
|
|
+ mockMvc.perform(get(endpoint)).andExpect(status().isOk());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void eventAndAnalysisEndpointsAnswerUnderTheSameIntegrationEnvironment() throws Exception {
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/current").param("mode", "WARNING"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.displayName").value("当前预警"));
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/current").param("mode", "ALARM"))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.data.displayName").value("当前报警"));
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/history-analysis")
|
|
|
+ .param("eventFamily", "WARNING"))
|
|
|
+ .andExpect(status().isOk());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/handling-efficiency")
|
|
|
+ .param("eventFamily", "WARNING"))
|
|
|
+ .andExpect(status().isOk());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/multidimensional-analysis")
|
|
|
+ .param("eventFamily", "WARNING"))
|
|
|
+ .andExpect(status().isOk());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void rejectedAndMutatingRequestsNeverReachAnyReadOnlyAdapter() throws Exception {
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/current")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/history-analysis")
|
|
|
+ .param("eventFamily", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/handling-efficiency")
|
|
|
+ .param("eventFamily", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/events/multidimensional-analysis")
|
|
|
+ .param("eventFamily", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/monitoring/overview")
|
|
|
+ .param("industry", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/monitoring/distribution")
|
|
|
+ .param("industry", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+ mockMvc.perform(get("/api/cockpit/v1/monitoring/situation")
|
|
|
+ .param("industry", "NOPE")).andExpect(status().isBadRequest());
|
|
|
+
|
|
|
+ mockMvc.perform(post("/api/cockpit/v1/events/current")).andExpect(status().isMethodNotAllowed());
|
|
|
+ mockMvc.perform(post("/api/cockpit/v1/map-snapshot")).andExpect(status().isMethodNotAllowed());
|
|
|
+ mockMvc.perform(post("/api/cockpit/v1/monitoring/overview")).andExpect(status().isMethodNotAllowed());
|
|
|
+ mockMvc.perform(post("/api/cockpit/v1/monitoring/distribution")).andExpect(status().isMethodNotAllowed());
|
|
|
+ mockMvc.perform(post("/api/cockpit/v1/monitoring/situation")).andExpect(status().isMethodNotAllowed());
|
|
|
+
|
|
|
+ for (Object adapter : adapters) {
|
|
|
+ verifyNoInteractions(adapter);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|