package com.ruoyi.system.service.impl;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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.eq;
|
import static org.mockito.ArgumentMatchers.isNull;
|
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
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;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.system.domain.TTest;
|
import com.ruoyi.system.domain.TTestBattery;
|
import com.ruoyi.system.domain.TTestChannelBinding;
|
import com.ruoyi.system.domain.TTestPlanStep;
|
import com.ruoyi.system.domain.TTestWarningThreshold;
|
import com.ruoyi.system.mapper.TTestMapper;
|
|
/** 测试配置服务的业务契约测试。 */
|
@ExtendWith(MockitoExtension.class)
|
class TTestServiceImplTest
|
{
|
@Mock
|
private TTestMapper testMapper;
|
|
@InjectMocks
|
private TTestServiceImpl testService;
|
|
@Test
|
void createShouldReturnIdentifierAndInitializeIndependentStatuses()
|
{
|
TTest test = validTest();
|
mockEnabledOptions();
|
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));
|
|
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 createShouldAllowMissingCollector()
|
{
|
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));
|
|
assertEquals(10L, testService.createTest(test));
|
|
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 createShouldRejectDisabledCreator()
|
{
|
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());
|
verify(testMapper, never()).updateTest(any(TTest.class));
|
}
|
|
@Test
|
void updateShouldReplaceConfigurationBeforeMonitoringAndAppendAudit()
|
{
|
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());
|
}
|
|
@Test
|
void deleteBeforeMonitoringShouldAppendAudit()
|
{
|
TTest current = validTest();
|
current.setTestId(10L);
|
current.setMonitoringStatus("NOT_MONITORED");
|
when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current);
|
when(testMapper.deleteTestById(10L, "operator")).thenReturn(1);
|
|
assertEquals(1, testService.deleteTestByIds(new Long[] { 10L }, 7L, "operator"));
|
|
verify(testMapper).insertTestAudit(eq(10L), eq("TEST_DELETED"), eq(7L), eq("operator"), any(), isNull());
|
}
|
|
private void mockEnabledOptions()
|
{
|
mockEnabledPeopleAndType();
|
when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1);
|
when(testMapper.countRegisteredChannel(100L)).thenReturn(1);
|
when(testMapper.countCollectorEnabled(8L)).thenReturn(1);
|
}
|
|
private void mockEnabledPeopleAndType()
|
{
|
when(testMapper.countUserEnabled(7L)).thenReturn(1);
|
when(testMapper.countUserEnabled(9L)).thenReturn(1);
|
when(testMapper.countTestTypeEnabled(3L)).thenReturn(1);
|
}
|
|
private TTest validTest()
|
{
|
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.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.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;
|
}
|
}
|