|
|
@@ -0,0 +1,328 @@
|
|
|
+package com.zksy.WaterSupply.MonitorAlarm;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.zksy.WaterSupply.MonitorAlarm.dto.in.WaterThresholdSaveInDTO;
|
|
|
+import com.zksy.WaterSupply.MonitorAlarm.service.impl.WaterSupplyThresholdServiceImpl;
|
|
|
+import com.zksy.base.alarm.domain.WarningThreshold;
|
|
|
+import com.zksy.base.alarm.mapper.WarningThresholdMapper;
|
|
|
+import com.zksy.base.domain.EquipmentBase;
|
|
|
+import com.zksy.base.service.EquipmentBaseService;
|
|
|
+import com.zksy.common.exception.ServiceException;
|
|
|
+import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
+import org.junit.jupiter.api.BeforeAll;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertNull;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.never;
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+class WaterSupplyThresholdServiceContractTest {
|
|
|
+
|
|
|
+ @BeforeAll
|
|
|
+ static void initializeMybatisPlusLambdaCache() {
|
|
|
+ TableInfoHelper.initTableInfo(
|
|
|
+ new MapperBuilderAssistant(new MybatisConfiguration(), ""), WarningThreshold.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private WaterSupplyThresholdServiceImpl service;
|
|
|
+ private WarningThresholdMapper thresholdMapper;
|
|
|
+ private EquipmentBaseService equipmentBaseService;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ service = new WaterSupplyThresholdServiceImpl();
|
|
|
+ thresholdMapper = mock(WarningThresholdMapper.class);
|
|
|
+ equipmentBaseService = mock(EquipmentBaseService.class);
|
|
|
+ ReflectionTestUtils.setField(service, "baseMapper", thresholdMapper);
|
|
|
+ ReflectionTestUtils.setField(service, "equipmentBaseService", equipmentBaseService);
|
|
|
+ when(equipmentBaseService.findByTopLevelType("供水")).thenReturn(Arrays.asList(
|
|
|
+ equipment("WS-FLOW-1"), equipment("WS-FLOW-2"), equipment("WS-PRESSURE-1")
|
|
|
+ ));
|
|
|
+ when(thresholdMapper.selectList(any())).thenReturn(Collections.emptyList());
|
|
|
+ when(thresholdMapper.insert(any(WarningThreshold.class))).thenReturn(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveCreatesOneTrimmedThresholdPerDistinctDevice() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Arrays.asList(" WS-FLOW-1 ", "WS-FLOW-1", "WS-FLOW-2"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+ request.setMaxValue(40D);
|
|
|
+ request.setPeriodStart("08:00");
|
|
|
+ request.setPeriodEnd("20:00");
|
|
|
+
|
|
|
+ int count = service.saveThresholdBatch(request);
|
|
|
+
|
|
|
+ assertEquals(2, count);
|
|
|
+ ArgumentCaptor<WarningThreshold> captor = ArgumentCaptor.forClass(WarningThreshold.class);
|
|
|
+ verify(thresholdMapper, times(2)).insert(captor.capture());
|
|
|
+ List<WarningThreshold> saved = captor.getAllValues();
|
|
|
+ assertEquals("WS-FLOW-1", saved.get(0).getDeviceCode());
|
|
|
+ assertEquals("WS-FLOW-2", saved.get(1).getDeviceCode());
|
|
|
+ assertEquals("流量预警", saved.get(0).getWarningType());
|
|
|
+ assertEquals("WARN-FLOW", saved.get(0).getWarningCode());
|
|
|
+ assertEquals("08:00", saved.get(0).getPeriodStart());
|
|
|
+ assertEquals("20:00", saved.get(0).getPeriodEnd());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveRejectsRequestsWithOnlyBlankDeviceCodes() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Arrays.asList(" ", ""));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.saveThresholdBatch(request));
|
|
|
+
|
|
|
+ assertEquals("设备编码不能为空", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveRejectsInvertedThresholdBounds() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(40D);
|
|
|
+ request.setMaxValue(20D);
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.saveThresholdBatch(request));
|
|
|
+
|
|
|
+ assertEquals("阈值最小值不能大于最大值", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveFallsBackToAllDayWhenPeriodIsOmitted() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+ request.setMaxValue(40D);
|
|
|
+
|
|
|
+ service.saveThresholdBatch(request);
|
|
|
+
|
|
|
+ ArgumentCaptor<WarningThreshold> captor = ArgumentCaptor.forClass(WarningThreshold.class);
|
|
|
+ verify(thresholdMapper).insert(captor.capture());
|
|
|
+ assertNull(captor.getValue().getPeriodStart());
|
|
|
+ assertNull(captor.getValue().getPeriodEnd());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveRejectsPartialPeriod() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+ request.setPeriodStart("08:00");
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.saveThresholdBatch(request));
|
|
|
+
|
|
|
+ assertEquals("分时阈值的生效开始时间和结束时间必须同时填写", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveRejectsOverlappingPeriodForSameDeviceAndWarningCode() {
|
|
|
+ WarningThreshold existing = new WarningThreshold();
|
|
|
+ existing.setId("existing-id");
|
|
|
+ existing.setDeviceCode("WS-FLOW-1");
|
|
|
+ existing.setWarningCode("WARN-FLOW");
|
|
|
+ existing.setPeriodStart("08:00");
|
|
|
+ existing.setPeriodEnd("20:00");
|
|
|
+ when(thresholdMapper.selectList(any())).thenReturn(Collections.singletonList(existing));
|
|
|
+
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+ request.setPeriodStart("19:00");
|
|
|
+ request.setPeriodEnd("21:00");
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.saveThresholdBatch(request));
|
|
|
+
|
|
|
+ assertEquals("设备[WS-FLOW-1]在相同预警编码[WARN-FLOW]下已存在重叠时段阈值,请调整分时设置",
|
|
|
+ error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void updateRejectsOverlappingPeriodForIncomingDeviceAndWarningCode() {
|
|
|
+ WarningThreshold current = new WarningThreshold();
|
|
|
+ current.setId("current-id");
|
|
|
+ current.setDeviceCode("WS-FLOW-1");
|
|
|
+ current.setWarningCode("WARN-FLOW-1");
|
|
|
+ current.setMinValue(20D);
|
|
|
+ when(thresholdMapper.selectById("current-id")).thenReturn(current);
|
|
|
+
|
|
|
+ WarningThreshold conflict = new WarningThreshold();
|
|
|
+ conflict.setId("conflict-id");
|
|
|
+ conflict.setDeviceCode("WS-FLOW-2");
|
|
|
+ conflict.setWarningCode("WARN-FLOW-2");
|
|
|
+ conflict.setPeriodStart("08:00");
|
|
|
+ conflict.setPeriodEnd("20:00");
|
|
|
+ when(thresholdMapper.selectList(any())).thenAnswer(invocation -> {
|
|
|
+ Object wrapper = invocation.getArgument(0);
|
|
|
+ Set<String> values = wrapperParamValues(wrapper);
|
|
|
+ return values.contains("WS-FLOW-2") && values.contains("WARN-FLOW-2")
|
|
|
+ ? Collections.singletonList(conflict)
|
|
|
+ : Collections.emptyList();
|
|
|
+ });
|
|
|
+
|
|
|
+ WarningThreshold update = new WarningThreshold();
|
|
|
+ update.setId("current-id");
|
|
|
+ update.setDeviceCode("WS-FLOW-2");
|
|
|
+ update.setWarningCode("WARN-FLOW-2");
|
|
|
+ update.setMinValue(20D);
|
|
|
+ update.setPeriodStart("19:00");
|
|
|
+ update.setPeriodEnd("21:00");
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.updateThreshold(update));
|
|
|
+
|
|
|
+ assertEquals("设备[WS-FLOW-2]在相同预警编码[WARN-FLOW-2]下已存在重叠时段阈值,请调整分时设置",
|
|
|
+ error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void updateRejectsThresholdWithoutLowerOrUpperBound() {
|
|
|
+ WarningThreshold current = new WarningThreshold();
|
|
|
+ current.setId("current-id");
|
|
|
+ current.setDeviceCode("WS-FLOW-1");
|
|
|
+ current.setWarningCode("WARN-FLOW-1");
|
|
|
+ current.setMinValue(20D);
|
|
|
+ when(thresholdMapper.selectById("current-id")).thenReturn(current);
|
|
|
+
|
|
|
+ WarningThreshold update = new WarningThreshold();
|
|
|
+ update.setId("current-id");
|
|
|
+ update.setDeviceCode("WS-FLOW-1");
|
|
|
+ update.setWarningCode("WARN-FLOW-1");
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.updateThreshold(update));
|
|
|
+
|
|
|
+ assertEquals("阈值最小值/最大值至少填写一项", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void batchSaveRejectsNonWaterDevice() {
|
|
|
+ WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
|
|
|
+ request.setDeviceCodes(Collections.singletonList("GAS-FLOW-1"));
|
|
|
+ request.setWarningType("流量预警");
|
|
|
+ request.setWarningCode("WARN-FLOW");
|
|
|
+ request.setMinValue(20D);
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.saveThresholdBatch(request));
|
|
|
+
|
|
|
+ assertEquals("仅支持供水设备配置阈值", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void updateRejectsNonWaterDevice() {
|
|
|
+ WarningThreshold current = new WarningThreshold();
|
|
|
+ current.setId("current-id");
|
|
|
+ current.setDeviceCode("WS-FLOW-1");
|
|
|
+ current.setWarningCode("WARN-FLOW-1");
|
|
|
+ current.setMinValue(20D);
|
|
|
+ when(thresholdMapper.selectById("current-id")).thenReturn(current);
|
|
|
+
|
|
|
+ WarningThreshold update = new WarningThreshold();
|
|
|
+ update.setId("current-id");
|
|
|
+ update.setDeviceCode("GAS-FLOW-1");
|
|
|
+ update.setWarningCode("WARN-FLOW-1");
|
|
|
+ update.setMinValue(20D);
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.updateThreshold(update));
|
|
|
+
|
|
|
+ assertEquals("仅支持供水设备配置阈值", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void listOnlyQueriesWaterDevices() {
|
|
|
+ service.findThresholdList("WS-FLOW-1", null, null);
|
|
|
+
|
|
|
+ ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<WarningThreshold>> captor =
|
|
|
+ ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper.class);
|
|
|
+ verify(thresholdMapper).selectList(captor.capture());
|
|
|
+ Set<String> values = wrapperParamValues(captor.getValue());
|
|
|
+ assertTrue(values.contains("WS-FLOW-1"));
|
|
|
+ assertTrue(values.contains("WS-FLOW-2"));
|
|
|
+ assertTrue(values.contains("WS-PRESSURE-1"));
|
|
|
+ assertFalse(values.contains("GAS-FLOW-1"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void pageReturnsEmptyDataWhenNoWaterDevicesExist() {
|
|
|
+ when(equipmentBaseService.findByTopLevelType("供水")).thenReturn(Collections.emptyList());
|
|
|
+
|
|
|
+ Page<WarningThreshold> page = service.findThresholdPage(1, 10, null, null, null, null);
|
|
|
+
|
|
|
+ assertTrue(page.getRecords().isEmpty());
|
|
|
+ assertEquals(0, page.getTotal());
|
|
|
+ verify(thresholdMapper, never()).selectList(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void deleteRejectsNonWaterThreshold() {
|
|
|
+ WarningThreshold threshold = new WarningThreshold();
|
|
|
+ threshold.setId("threshold-id");
|
|
|
+ threshold.setDeviceCode("GAS-FLOW-1");
|
|
|
+ when(thresholdMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(threshold));
|
|
|
+
|
|
|
+ ServiceException error = assertThrows(ServiceException.class,
|
|
|
+ () -> service.deleteThresholdBatch(Collections.singletonList("threshold-id")));
|
|
|
+
|
|
|
+ assertEquals("仅支持供水设备配置阈值", error.getMessage());
|
|
|
+ verify(thresholdMapper, never()).deleteBatchIds(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Set<String> wrapperParamValues(Object wrapper) {
|
|
|
+ ((com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<?>) wrapper).getSqlSegment();
|
|
|
+ return ((java.util.Map<?, ?>) ReflectionTestUtils.getField(wrapper, "paramNameValuePairs"))
|
|
|
+ .values().stream()
|
|
|
+ .map(String::valueOf)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static EquipmentBase equipment(String equipmentCode) {
|
|
|
+ EquipmentBase equipment = new EquipmentBase();
|
|
|
+ equipment.setEquipmentCode(equipmentCode);
|
|
|
+ return equipment;
|
|
|
+ }
|
|
|
+}
|