package com.battery.business.service.impl;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.nio.charset.StandardCharsets;
|
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.battery.business.domain.TCollector;
|
import com.battery.business.domain.TDeviceAdapter;
|
import com.battery.business.mapper.TCollectorMapper;
|
import com.battery.business.mapper.TDeviceAdapterMapper;
|
|
/**
|
* 采集器 Service 单元测试。
|
*
|
* <p>采集器按 ADR-0064 不建独立设备模型:登记不再依赖发现候选(真实来源不产出采集器候选),
|
* 由操作员手工登记来源身份,在线状态取所链接来源实例的连接结果。
|
*/
|
@ExtendWith(MockitoExtension.class)
|
class TCollectorServiceImplTest
|
{
|
@Mock
|
private TCollectorMapper collectorMapper;
|
|
@Mock
|
private TDeviceAdapterMapper adapterMapper;
|
|
private TCollectorServiceImpl collectorService;
|
|
private TCollector collector;
|
|
@BeforeEach
|
void setUp()
|
{
|
collectorService = new TCollectorServiceImpl(collectorMapper, adapterMapper);
|
collector = new TCollector();
|
collector.setCollectorId(1L);
|
collector.setCollectorCode("COL-001");
|
collector.setIpAddress("192.168.1.10");
|
collector.setMacAddress("AA:BB:CC:DD:EE:FF");
|
collector.setCollectorName("一号采集器");
|
collector.setChannelCount(8);
|
collector.setOnlineStatus("0");
|
collector.setTestStatus("0");
|
collector.setStatus("0");
|
}
|
|
@Test
|
void insertCollectorSqlShouldNotWriteUpdateAuditFields() throws IOException
|
{
|
try (InputStream input = getClass().getResourceAsStream(
|
"/mapper/battery/TCollectorMapper.xml"))
|
{
|
assertNotNull(input);
|
String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
int insertStart = mapperXml.indexOf("<insert id=\"insertCollector\"");
|
int insertEnd = mapperXml.indexOf("</insert>", insertStart);
|
String insertSql = mapperXml.substring(insertStart, insertEnd);
|
|
assertFalse(insertSql.contains("update_by"));
|
assertFalse(insertSql.contains("update_time"));
|
}
|
}
|
|
/** 在线事实只有一个来源:所链接来源实例的连接结果;登记表里的存储列不再充当这个事实。 */
|
@Test
|
void onlineStatusSqlShouldDeriveFromLinkedAdapter() throws IOException
|
{
|
try (InputStream input = getClass().getResourceAsStream(
|
"/mapper/battery/TCollectorMapper.xml"))
|
{
|
assertNotNull(input);
|
String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
int fragmentStart = mapperXml.indexOf("<sql id=\"derivedOnlineStatus\"");
|
assertTrue(fragmentStart >= 0, "在线状态的派生表达式应当是一个共用片段");
|
String fragment = mapperXml.substring(fragmentStart, mapperXml.indexOf("</sql>", fragmentStart));
|
assertTrue(fragment.contains("from t_device_adapter adapter_fact"), fragment);
|
// 存储列不再参与:只有派生的那个表达式。
|
assertFalse(mapperXml.contains("upper(c.online_status)"));
|
// SELECT 与 WHERE 各用一次(WHERE 里不能用别名,所以走同一个片段)。
|
assertEquals(2, mapperXml.split("<include refid=\"derivedOnlineStatus\" />", -1).length - 1);
|
}
|
}
|
|
/** 登记不再要求发现候选:手工填写的来源身份即可落库。 */
|
@Test
|
void insertCollectorShouldNotRequireADiscoveryCandidate()
|
{
|
collector.setCollectorId(null);
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.insertCollector(collector));
|
|
verify(collectorMapper).insertCollector(any(TCollector.class));
|
}
|
|
@Test
|
void insertCollectorShouldAcceptCollectorWithoutIpAndMac()
|
{
|
collector.setCollectorId(null);
|
collector.setIpAddress(null);
|
collector.setMacAddress(null);
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.insertCollector(collector));
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).insertCollector(captor.capture());
|
assertNull(captor.getValue().getIpAddress());
|
assertNull(captor.getValue().getMacAddress());
|
}
|
|
@Test
|
void insertCollectorShouldNormalizeFieldsAndInitializeStatus()
|
{
|
collector.setCollectorId(null);
|
collector.setCollectorCode(" COL-001 ");
|
collector.setMacAddress("aa-bb-cc-dd-ee-ff");
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
int rows = collectorService.insertCollector(collector);
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).insertCollector(captor.capture());
|
TCollector saved = captor.getValue();
|
assertEquals(1, rows);
|
assertEquals("COL-001", saved.getCollectorCode());
|
assertEquals("AA:BB:CC:DD:EE:FF", saved.getMacAddress());
|
assertEquals("0", saved.getOnlineStatus());
|
assertEquals("0", saved.getTestStatus());
|
assertEquals("0", saved.getStatus());
|
assertEquals("0", saved.getDelFlag());
|
}
|
|
@Test
|
void insertCollectorShouldRejectMalformedIpAndMacInsteadOfGuessing()
|
{
|
collector.setCollectorId(null);
|
collector.setIpAddress("invalid-host");
|
collector.setMacAddress("AA:BB:CC:DD:EE:FF");
|
|
ServiceException ip = assertThrows(ServiceException.class,
|
() -> collectorService.insertCollector(collector));
|
assertEquals("IP地址格式不正确", ip.getMessage());
|
|
collector.setIpAddress("192.168.1.10");
|
collector.setMacAddress("invalid-mac");
|
ServiceException mac = assertThrows(ServiceException.class,
|
() -> collectorService.insertCollector(collector));
|
assertEquals("MAC地址格式不正确", mac.getMessage());
|
verify(collectorMapper, never()).insertCollector(any(TCollector.class));
|
}
|
|
@Test
|
void insertCollectorShouldRejectUnknownLinkedSource()
|
{
|
collector.setCollectorId(null);
|
collector.setAdapterId(9L);
|
when(adapterMapper.selectDeviceAdapterById(9L)).thenReturn(null);
|
|
ServiceException exception = assertThrows(ServiceException.class,
|
() -> collectorService.insertCollector(collector));
|
|
assertEquals("所链接的数据来源不存在", exception.getMessage());
|
verify(collectorMapper, never()).insertCollector(any(TCollector.class));
|
}
|
|
/** 链接到来源实例后,登记保存该链接:采集器的辅助测量由它提供。 */
|
@Test
|
void insertCollectorShouldPersistLinkedSource()
|
{
|
collector.setCollectorId(null);
|
collector.setAdapterId(3L);
|
when(adapterMapper.selectDeviceAdapterById(3L)).thenReturn(new TDeviceAdapter());
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.insertCollector(collector));
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).insertCollector(captor.capture());
|
assertEquals(Long.valueOf(3L), captor.getValue().getAdapterId());
|
}
|
|
@Test
|
void insertCollectorShouldRetainRequestedDisabledStatus()
|
{
|
collector.setCollectorId(null);
|
collector.setStatus("1");
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.insertCollector(collector));
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).insertCollector(captor.capture());
|
assertEquals("1", captor.getValue().getStatus());
|
}
|
|
@Test
|
void insertCollectorShouldDefaultToEnabledWhenStatusIsMissing()
|
{
|
collector.setCollectorId(null);
|
collector.setStatus(null);
|
when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.insertCollector(collector));
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).insertCollector(captor.capture());
|
assertEquals("0", captor.getValue().getStatus());
|
}
|
|
@Test
|
void insertCollectorShouldRejectDuplicateCode()
|
{
|
collector.setCollectorId(null);
|
TCollector existing = new TCollector();
|
existing.setCollectorId(2L);
|
when(collectorMapper.selectCollectorByCode("COL-001")).thenReturn(existing);
|
|
ServiceException exception = assertThrows(ServiceException.class,
|
() -> collectorService.insertCollector(collector));
|
|
assertEquals("设备编码已存在", exception.getMessage());
|
verify(collectorMapper, never()).insertCollector(any(TCollector.class));
|
}
|
|
@Test
|
void updateCollectorShouldAllowCurrentUniqueValues()
|
{
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
when(collectorMapper.selectCollectorByCode("COL-001")).thenReturn(collector);
|
when(collectorMapper.selectCollectorByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(collector);
|
when(collectorMapper.updateCollector(any(TCollector.class))).thenReturn(1);
|
|
assertEquals(1, collectorService.updateCollector(collector));
|
|
verify(collectorMapper).updateCollector(collector);
|
}
|
|
/** 编辑只提交可改字段:未提交的 IP/MAC 不能被当成"清空"。 */
|
@Test
|
void updateCollectorShouldKeepUnsubmittedIpAndMac()
|
{
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
when(collectorMapper.selectCollectorByCode("COL-001")).thenReturn(collector);
|
when(collectorMapper.selectCollectorByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(collector);
|
when(collectorMapper.updateCollector(any(TCollector.class))).thenReturn(1);
|
TCollector edit = new TCollector();
|
edit.setCollectorId(1L);
|
edit.setCollectorName("改名后");
|
|
assertEquals(1, collectorService.updateCollector(edit));
|
|
ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
|
verify(collectorMapper).updateCollector(captor.capture());
|
assertEquals("192.168.1.10", captor.getValue().getIpAddress());
|
assertEquals("AA:BB:CC:DD:EE:FF", captor.getValue().getMacAddress());
|
assertEquals("改名后", captor.getValue().getCollectorName());
|
}
|
|
@Test
|
void updateCollectorShouldRejectDisablingTestingCollector()
|
{
|
collector.setStatus("1");
|
collector.setTestStatus("1");
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
|
ServiceException exception = assertThrows(ServiceException.class,
|
() -> collectorService.updateCollector(collector));
|
|
assertEquals("采集器正在测试,不能禁用", exception.getMessage());
|
verify(collectorMapper, never()).updateCollector(any(TCollector.class));
|
}
|
|
@Test
|
void updateCollectorStatusShouldRejectDisablingTestingCollector()
|
{
|
collector.setTestStatus("1");
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
TCollector command = statusCommand("1");
|
|
ServiceException exception = assertThrows(ServiceException.class,
|
() -> collectorService.updateCollectorStatus(command));
|
|
assertEquals("采集器正在测试,不能禁用", exception.getMessage());
|
verify(collectorMapper, never()).updateCollectorStatus(any(TCollector.class));
|
}
|
|
@Test
|
void updateCollectorStatusShouldAllowEnablingTestingCollector()
|
{
|
collector.setTestStatus("1");
|
collector.setStatus("1");
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
when(collectorMapper.updateCollectorStatus(any(TCollector.class))).thenReturn(1);
|
TCollector command = statusCommand("0");
|
|
assertEquals(1, collectorService.updateCollectorStatus(command));
|
|
verify(collectorMapper).updateCollectorStatus(command);
|
}
|
|
@Test
|
void updateCollectorStatusShouldBeIdempotent()
|
{
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
|
TCollector command = statusCommand("0");
|
|
assertEquals(1, collectorService.updateCollectorStatus(command));
|
|
verify(collectorMapper, never()).updateCollectorStatus(any(TCollector.class));
|
}
|
|
@Test
|
void deleteCollectorByIdsShouldRejectTestingCollectorBeforeAnyDelete()
|
{
|
TCollector idle = new TCollector();
|
idle.setCollectorId(1L);
|
idle.setTestStatus("0");
|
TCollector testing = new TCollector();
|
testing.setCollectorId(2L);
|
testing.setTestStatus("1");
|
when(collectorMapper.selectCollectorById(1L)).thenReturn(idle);
|
when(collectorMapper.selectCollectorById(2L)).thenReturn(testing);
|
|
ServiceException exception = assertThrows(ServiceException.class,
|
() -> collectorService.deleteCollectorByIds(new Long[] { 1L, 2L }, "admin"));
|
|
assertEquals("采集器正在测试,不能删除", exception.getMessage());
|
verify(collectorMapper, never()).deleteCollectorById(anyLong(), any(String.class));
|
}
|
|
private TCollector statusCommand(String status)
|
{
|
TCollector command = new TCollector();
|
command.setCollectorId(1L);
|
command.setStatus(status);
|
command.setUpdateBy("admin");
|
return command;
|
}
|
}
|