From 5a18d863778ffabcc45da185f7f77e7462289bb9 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 27 八月 2026 20:45:31 +0800
Subject: [PATCH] feat(test): implement test configuration and channel binding

---
 ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java |  275 +++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 188 insertions(+), 87 deletions(-)

diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
index b96b26b..71aa715 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
@@ -4,7 +4,7 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -12,6 +12,7 @@
 
 import java.math.BigDecimal;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
@@ -20,11 +21,12 @@
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.system.domain.TTest;
 import com.ruoyi.system.domain.TTestBattery;
-import com.ruoyi.system.domain.TTestBatteryField;
+import com.ruoyi.system.domain.TTestChannelBinding;
+import com.ruoyi.system.domain.TTestPlanStep;
 import com.ruoyi.system.domain.TTestWarningThreshold;
 import com.ruoyi.system.mapper.TTestMapper;
 
-/** 测试管理 Service 核心业务单元测试。 */
+/** 测试配置服务的业务契约测试。 */
 @ExtendWith(MockitoExtension.class)
 class TTestServiceImplTest
 {
@@ -35,10 +37,10 @@
     private TTestServiceImpl testService;
 
     @Test
-    void insertShouldBackfillRelationIdBeforeSavingFields()
+    void createShouldReturnIdentifierAndInitializeIndependentStatuses()
     {
-        TTest test = validTest(null, "0");
-        mockEnabledResources();
+        TTest test = validTest();
+        mockEnabledOptions();
         when(testMapper.insertTest(any(TTest.class))).thenAnswer(invocation -> {
             invocation.<TTest>getArgument(0).setTestId(10L);
             return 1;
@@ -48,118 +50,217 @@
             return 1;
         }).when(testMapper).insertTestBattery(any(TTestBattery.class));
 
-        assertEquals(1, testService.insertTest(test));
-
-        verify(testMapper).insertTestBatteryField(any(TTestBatteryField.class));
-        assertEquals(20L, test.getBatteries().get(0).getFields().get(0).getRelationId());
+        assertEquals(10L, testService.createTest(test));
+        assertEquals("WAITING", test.getTestStatus());
+        assertEquals("NOT_MONITORED", test.getMonitoringStatus());
+        assertEquals(7L, test.getCreatorId());
+        verify(testMapper).insertTestPlanStep(any(TTestPlanStep.class));
+        verify(testMapper).insertTestChannelBinding(any(TTestChannelBinding.class));
+        verify(testMapper).insertTestThreshold(any(TTestWarningThreshold.class));
+        verify(testMapper).insertTestAudit(anyLong(), any(), anyLong(), any(), isNull(), any());
     }
 
     @Test
-    void insertShouldRejectOccupiedBattery()
+    void createShouldAllowMissingCollector()
     {
-        TTest test = validTest(null, "0");
-        mockEnabledResources();
-        when(testMapper.countTestingByBatteryNo("BAT-001", null)).thenReturn(1);
+        TTest test = validTest();
+        test.getBatteries().get(0).setCollectorId(null);
+        mockEnabledPeopleAndType();
+        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
+        when(testMapper.countRegisteredChannel(100L)).thenReturn(1);
+        when(testMapper.insertTest(any(TTest.class))).thenAnswer(invocation -> {
+            invocation.<TTest>getArgument(0).setTestId(10L);
+            return 1;
+        });
+        doAnswer(invocation -> {
+            invocation.<TTestBattery>getArgument(0).setRelationId(20L);
+            return 1;
+        }).when(testMapper).insertTestBattery(any(TTestBattery.class));
 
-        ServiceException exception = assertThrows(ServiceException.class, () -> testService.insertTest(test));
+        assertEquals(10L, testService.createTest(test));
 
-        assertEquals("电池号或检测柜测试通道已被检测中的任务占用", exception.getMessage());
+        verify(testMapper, never()).countCollectorEnabled(anyLong());
+    }
+
+    @Test
+    void createShouldRejectBatteryOutsideSelectedGroup()
+    {
+        TTest test = validTest();
+        mockEnabledPeopleAndType();
+        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(0);
+
+        ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test));
+
+        assertEquals("电池号不属于所选启用电池组", exception.getMessage());
         verify(testMapper, never()).insertTest(any(TTest.class));
     }
 
     @Test
-    void updateShouldRejectNonWaitingStatus()
+    void createShouldRejectDisabledCreator()
     {
-        TTest request = validTest(10L, "0");
-        TTest current = validTest(10L, "1");
-        when(testMapper.countUserEnabled(1L)).thenReturn(1);
-        when(testMapper.countTestTypeEnabled(1L)).thenReturn(1);
-        when(testMapper.selectTestById(10L)).thenReturn(current);
-        when(testMapper.selectTestBatteries(10L)).thenReturn(current.getBatteries());
-        when(testMapper.selectTestBatteryFields(10L)).thenReturn(current.getBatteries().get(0).getFields());
-        when(testMapper.selectTestThresholds(10L)).thenReturn(current.getThresholds());
+        TTest test = validTest();
+        when(testMapper.countUserEnabled(7L)).thenReturn(0);
+
+        ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test));
+
+        assertEquals("创建人不存在或已停用", exception.getMessage());
+        verify(testMapper, never()).insertTest(any(TTest.class));
+    }
+
+    @Test
+    void createShouldRejectDisabledPrincipal()
+    {
+        TTest test = validTest();
+        when(testMapper.countUserEnabled(7L)).thenReturn(1);
+        when(testMapper.countUserEnabled(9L)).thenReturn(0);
+
+        ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test));
+
+        assertEquals("测试负责人不存在或已停用", exception.getMessage());
+        verify(testMapper, never()).insertTest(any(TTest.class));
+    }
+
+    @Test
+    void createShouldRejectDuplicatePrimaryChannelBinding()
+    {
+        TTest test = validTest();
+        TTestBattery duplicate = validBattery("BAT-002", 2L, 100L);
+        test.setBatteries(java.util.List.of(test.getBatteries().get(0), duplicate));
+        mockEnabledPeopleAndType();
+        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
+        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-002")).thenReturn(1);
+        when(testMapper.countRegisteredChannel(100L)).thenReturn(1);
+        when(testMapper.countCollectorEnabled(8L)).thenReturn(1);
+
+        ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test));
+
+        assertEquals("同一检测柜通道不可重复绑定", exception.getMessage());
+        verify(testMapper, never()).insertTest(any(TTest.class));
+    }
+
+    @Test
+    void createShouldRejectBatteriesFromDifferentGroups()
+    {
+        TTest test = validTest();
+        TTestBattery otherGroup = validBattery("BAT-002", 2L, 101L);
+        otherGroup.setGroupId(2L);
+        test.setBatteries(java.util.List.of(test.getBatteries().get(0), otherGroup));
+        mockEnabledPeopleAndType();
+        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
+        when(testMapper.countRegisteredChannel(100L)).thenReturn(1);
+        when(testMapper.countCollectorEnabled(8L)).thenReturn(1);
+
+        ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test));
+
+        assertEquals("同一测试只能选择一个电池组", exception.getMessage());
+        verify(testMapper, never()).insertTest(any(TTest.class));
+    }
+
+    @Test
+    void updateShouldRejectConfigurationAfterMonitoringStarted()
+    {
+        TTest request = validTest();
+        request.setTestId(10L);
+        TTest current = validTest();
+        current.setTestId(10L);
+        current.setMonitoringStatus("MONITORING");
+        when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current);
 
         ServiceException exception = assertThrows(ServiceException.class, () -> testService.updateTest(request));
 
-        assertEquals("仅待检测状态允许编辑草稿", exception.getMessage());
+        assertEquals("开始监测后不能修改测试配置", exception.getMessage());
         verify(testMapper, never()).updateTest(any(TTest.class));
     }
 
     @Test
-    void startShouldTransitionWaitingToTesting()
+    void updateShouldReplaceConfigurationBeforeMonitoringAndAppendAudit()
     {
-        TTest current = validTest(10L, "0");
-        when(testMapper.selectTestById(10L)).thenReturn(current);
-        when(testMapper.selectTestBatteries(10L)).thenReturn(current.getBatteries());
-        when(testMapper.selectTestBatteryFields(10L)).thenReturn(current.getBatteries().get(0).getFields());
-        when(testMapper.selectTestThresholds(10L)).thenReturn(current.getThresholds());
+        TTest request = validTest();
+        request.setTestId(10L);
+        request.setMonitorTimeoutMinutes(45);
+        TTest current = validTest();
+        current.setTestId(10L);
+        current.setCreatorId(7L);
+        current.setMonitoringStatus("NOT_MONITORED");
+        current.setMonitorTimeoutMinutes(30);
+        when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current);
+        mockEnabledOptions();
+        when(testMapper.updateTest(request)).thenReturn(1);
+        doAnswer(invocation -> {
+            invocation.<TTestBattery>getArgument(0).setRelationId(21L);
+            return 1;
+        }).when(testMapper).insertTestBattery(any(TTestBattery.class));
+
+        assertEquals(1, testService.updateTest(request));
+        assertEquals(45, request.getMonitorTimeoutMinutes());
+        verify(testMapper).deleteTestBindingsByTestId(10L);
+        verify(testMapper).deleteTestPlanStepsByTestId(10L);
+        verify(testMapper).insertTestAudit(anyLong(), any(), anyLong(), any(), any(), any());
+    }
+
+    private void mockEnabledOptions()
+    {
+        mockEnabledPeopleAndType();
         when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
-        when(testMapper.countCabinetEnabled(1L)).thenReturn(1);
-        when(testMapper.countCollectorEnabled(1L, 1L)).thenReturn(1);
-        when(testMapper.updateTestStatus(10L, "1", "admin")).thenReturn(1);
-
-        assertEquals(1, testService.startTest(10L, "admin"));
-        verify(testMapper).updateTestStatus(10L, "1", "admin");
+        when(testMapper.countRegisteredChannel(100L)).thenReturn(1);
+        when(testMapper.countCollectorEnabled(8L)).thenReturn(1);
     }
 
-    @Test
-    void deleteShouldRejectTestingTask()
+    private void mockEnabledPeopleAndType()
     {
-        TTest current = validTest(10L, "1");
-        when(testMapper.selectTestById(10L)).thenReturn(current);
-        when(testMapper.selectTestBatteries(10L)).thenReturn(current.getBatteries());
-        when(testMapper.selectTestBatteryFields(10L)).thenReturn(current.getBatteries().get(0).getFields());
-        when(testMapper.selectTestThresholds(10L)).thenReturn(current.getThresholds());
-
-        ServiceException exception = assertThrows(ServiceException.class,
-                () -> testService.deleteTestByIds(new Long[] {10L}, "admin"));
-
-        assertEquals("检测中或异常测试不能删除", exception.getMessage());
-        verify(testMapper, never()).deleteTestById(anyLong(), anyString());
+        when(testMapper.countUserEnabled(7L)).thenReturn(1);
+        when(testMapper.countUserEnabled(9L)).thenReturn(1);
+        when(testMapper.countTestTypeEnabled(3L)).thenReturn(1);
     }
 
-    private void mockEnabledResources()
+    private TTest validTest()
     {
-        when(testMapper.countUserEnabled(1L)).thenReturn(1);
-        when(testMapper.countTestTypeEnabled(1L)).thenReturn(1);
-        when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
-        when(testMapper.countCabinetEnabled(1L)).thenReturn(1);
-        when(testMapper.countCollectorEnabled(1L, 1L)).thenReturn(1);
-    }
-
-    private TTest validTest(Long testId, String status)
-    {
-        TTestBatteryField field = new TTestBatteryField();
-        field.setRelationId(20L);
-        field.setFieldName("容量");
-        field.setFieldValue("100");
-
-        TTestBattery battery = new TTestBattery();
-        battery.setRelationId(20L);
-        battery.setGroupId(1L);
-        battery.setBatteryNo("BAT-001");
-        battery.setCabinetId(1L);
-        battery.setTestChannelNo(0);
-        battery.setCollectorId(1L);
-        battery.setTemperatureChannelNo(1);
-        battery.setPositiveChannelNo(2);
-        battery.setNegativeChannelNo(3);
-        battery.setFields(Collections.singletonList(field));
+        TTestPlanStep step = new TTestPlanStep();
+        step.setCycleNo(1);
+        step.setStepNo(1);
+        step.setStepType("CONSTANT_CURRENT_CHARGE");
+        step.setStepName("恒流充电");
+        step.setSortOrder(1);
+        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
+        parameters.put("currentA", new BigDecimal("1.5"));
+        step.setParameters(parameters);
 
         TTestWarningThreshold threshold = new TTestWarningThreshold();
-        threshold.setThresholdType("VOLTAGE:2");
-        threshold.setMinValue(BigDecimal.ZERO);
-        threshold.setMaxValue(BigDecimal.TEN);
+        threshold.setMetricCode("VOLTAGE");
+        threshold.setMinValue(new BigDecimal("2.5"));
+        threshold.setMaxValue(new BigDecimal("4.2"));
+        threshold.setUnit("V");
+        threshold.setSeverity("WARNING");
 
         TTest test = new TTest();
-        test.setTestId(testId);
-        test.setTestName("测试任务");
-        test.setPrincipalId(1L);
-        test.setTestTypeId(1L);
-        test.setTestPurpose("验证容量");
-        test.setStatus(status);
-        test.setBatteries(Collections.singletonList(battery));
+        test.setTestName("低温循环测试");
+        test.setCreatorId(7L);
+        test.setOperatorId(7L);
+        test.setCreateBy("creator");
+        test.setUpdateBy("creator");
+        test.setPrincipalId(9L);
+        test.setTestTypeId(3L);
+        test.setTestPurpose("验证低温循环性能");
+        test.setMonitorTimeoutMinutes(30);
+        test.setBatteries(Collections.singletonList(validBattery("BAT-001", 1L, 100L)));
+        test.setPlanSteps(Collections.singletonList(step));
         test.setThresholds(Collections.singletonList(threshold));
         return test;
     }
+
+    private TTestBattery validBattery(String batteryNo, Long relationId, Long channelId)
+    {
+        TTestChannelBinding binding = new TTestChannelBinding();
+        binding.setChannelId(channelId);
+        binding.setExpectedBarcode("CODE-" + batteryNo);
+
+        TTestBattery battery = new TTestBattery();
+        battery.setRelationId(relationId);
+        battery.setGroupId(1L);
+        battery.setBatteryNo(batteryNo);
+        battery.setCollectorId(8L);
+        battery.setBinding(binding);
+        battery.setFields(Collections.emptyList());
+        return battery;
+    }
 }

--
Gitblit v1.7.1