package com.battery.business.web;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.Mockito.*;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.ArrayList;
|
import java.util.LinkedHashMap;
|
import com.alibaba.fastjson2.JSON;
|
import com.battery.business.domain.TTest;
|
import com.battery.business.mapper.TTestMapper;
|
import com.battery.business.service.impl.TestConsoleDataServiceImpl;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.BeforeEach;
|
import com.ruoyi.common.exception.ServiceException;
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
/** 从控制台提交入口验证单页导入,数据库使用边界替身。 */
|
class ConsoleSingleSheetImportTest
|
{
|
private TTestMapper mapper;
|
private TTestController controller;
|
private final List<Map<String, Object>> saved = new ArrayList<>();
|
|
@BeforeEach void setUp()
|
{
|
mapper = mock(TTestMapper.class);
|
TTest test = new TTest(); test.setTestStatus("COMPLETED");
|
when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test);
|
when(mapper.selectImportBinding(1L, "BAT-1")).thenReturn(Map.of(
|
"test_battery_id", 4L, "binding_id", 5L, "adapter_code", "adapter-a",
|
"devtype", 27, "devid", 1, "subdevid", 1, "chlid", 1));
|
when(mapper.selectImportBinding(1L, "BAT-2")).thenReturn(Map.of(
|
"test_battery_id", 6L, "binding_id", 7L, "adapter_code", "adapter-a",
|
"devtype", 27, "devid", 1, "subdevid", 1, "chlid", 2));
|
when(mapper.selectImportDataRecords(eq(1L), anyString())).thenAnswer(call -> new ArrayList<>(saved));
|
when(mapper.insertImportedDataRecord(anyString(), anyMap())).thenAnswer(call -> {
|
saved.add(new LinkedHashMap<>(call.getArgument(1))); return 1;
|
});
|
controller = new TTestController() {
|
@Override public Long getUserId() { return 1L; }
|
@Override public String getUsername() { return "tester"; }
|
};
|
ReflectionTestUtils.setField(controller, "consoleDataService", new TestConsoleDataServiceImpl(mapper));
|
}
|
|
@Test void identicalRowsInOneSheetAreSkipped()
|
{
|
var response = submit("REALTIME", List.of(row(), row()));
|
assertEquals(1, response.get("importedCount"));
|
assertEquals(1, response.get("skippedCount"));
|
}
|
|
@Test void allThreeTypesSkipRetryButRejectChangedContent()
|
{
|
for (String type : List.of("REALTIME", "STEP", "CYCLE"))
|
{
|
saved.clear();
|
var row = row();
|
row.put("deviceTimeRaw", "2023-05-08 11:42:11"); row.put("adapterTimezoneId", "Asia/Hong_Kong");
|
row.put("capacity", 8.5122); row.put("dischargeCapacity", 8.5122);
|
assertEquals(1, submit(type, List.of(row)).get("importedCount"));
|
row.put("sourceRow", 19);
|
row.put("capacity", new java.math.BigDecimal("8.512200"));
|
var repeated = submit(type, List.of(row));
|
assertEquals(0, repeated.get("importedCount"));
|
assertEquals(1, repeated.get("skippedCount"));
|
assertNull(repeated.get("actionId"));
|
row.put("cycleNo", 2);
|
var failure = assertThrows(ServiceException.class, () -> submit(type, List.of(row)));
|
assertTrue(failure.getMessage().contains("第19行,列「记录编号」"));
|
assertEquals(1, saved.size());
|
}
|
}
|
|
@Test void sameRecordNumberForDifferentBatteriesHasIndependentIdentity()
|
{
|
var other = row(); other.put("batteryNo", "BAT-2");
|
assertEquals(2, submit("REALTIME", List.of(row(), other)).get("importedCount"));
|
assertEquals("adapter-a:27:1:1:1", saved.get(0).get("source_identity"));
|
assertEquals("adapter-a:27:1:1:2", saved.get(1).get("source_identity"));
|
}
|
|
@Test void pollingSlotIsNotPartOfImportedDeviceContent()
|
{
|
submit("REALTIME", List.of(row()));
|
saved.get(0).put("capture_slot_utc", new java.util.Date(1683517380000L));
|
assertEquals(1, submit("REALTIME", List.of(row())).get("skippedCount"));
|
}
|
|
@Test void anyInvalidRowLeavesWholeSheetAndRevisionsUnwritten()
|
{
|
for (Map.Entry<String, Object> invalid : Map.<String, Object>of("cycleNo", 0, "recordedAt", "2023-05-08",
|
"voltage", "NaN", "batteryNo", "OTHER").entrySet())
|
{
|
var bad = row(); bad.put(invalid.getKey(), invalid.getValue()); bad.put("sourceRow", 23);
|
var failure = assertThrows(ServiceException.class, () -> submit("REALTIME", List.of(row(), bad)));
|
assertTrue(failure.getMessage().contains("第23行,列「"));
|
assertTrue(saved.isEmpty());
|
}
|
verify(mapper, never()).insertDataRevisionAction(any());
|
}
|
|
@Test void conflictAfterValidRowDoesNotWriteValidPrefixOrAudit()
|
{
|
submit("REALTIME", List.of(row()));
|
clearInvocations(mapper);
|
var next = row(); next.put("recordNo", "18"); next.put("recordedAt", 1683517391000L);
|
var conflict = row(); conflict.put("voltage", 9);
|
assertThrows(ServiceException.class, () -> submit("REALTIME", List.of(next, conflict)));
|
assertEquals(1, saved.size());
|
verify(mapper, never()).insertDataRevisionAction(any());
|
var sameSecond = row(); sameSecond.put("recordNo", "19"); sameSecond.put("sequenceId", 19);
|
assertEquals(1, submit("REALTIME", List.of(sameSecond)).get("importedCount"));
|
}
|
|
private Map<String, Object> row()
|
{
|
return new LinkedHashMap<>(Map.of("recordNo", "17", "batteryNo", "BAT-1", "recordedAt", 1683517331000L,
|
"cycleNo", 1, "stepNo", 1, "stepName", "恒流放电", "sequenceId", 17, "voltage", 1.3349));
|
}
|
|
private com.ruoyi.common.core.domain.AjaxResult submit(String type, List<Map<String, Object>> rows)
|
{
|
return controller.dataAction(1L, new TTestController.ConsoleDataActionRequest(type, "IMPORT",
|
"单页导入", "[]", JSON.toJSONString(rows), "开发测试", List.of()));
|
}
|
}
|