yeg
2026-09-05 92f32acd847ea1700590bc669750f23ef499840e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package com.battery.business.service.impl;
 
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import com.alibaba.fastjson2.JSON;
import com.battery.business.domain.*;
import com.battery.business.mapper.TTestMapper;
import com.ruoyi.common.exception.ServiceException;
 
/** 修订入口必须保留数据库中的修改前事实,并拒绝越界身份和字段。 */
class TestConsoleDataRevisionQueryServiceTest
{
    private final TTestMapper mapper = mock(TTestMapper.class);
    private final TestConsoleDataServiceImpl service = new TestConsoleDataServiceImpl(mapper);
 
    @Test void savesDatabaseBeforeContentInsteadOfClientClaims()
    {
        var action = action("EDIT", "realtime:7");
        when(mapper.selectDataRecord(1L, "REALTIME", 7L)).thenReturn(Map.of("voltage_v", new BigDecimal("3.7"), "source_identity", "cabinet-a"));
        var item = new DataRevisionItem(); item.setStableIdentity("realtime:7"); item.setFieldValues("{\"voltage\":4.125}");
        service.submitAction(action, List.of(item));
        var before = JSON.parseArray(action.getBeforeContentJson()).getJSONObject(0);
        assertEquals("cabinet-a", before.getString("source_identity"));
        assertEquals(new BigDecimal("3.7"), before.getBigDecimal("voltage_v"));
        var order = inOrder(mapper);
        order.verify(mapper).insertDataRevisionAction(action);
        order.verify(mapper).updateDataRecord(1L, "REALTIME", 7L, Map.of("voltage_v", new BigDecimal("4.125")));
    }
 
    @Test void deletionPersistsCompleteContentBeforeRemovingRecord()
    {
        var action = action("DELETE", "realtime:7");
        when(mapper.selectDataRecord(1L, "REALTIME", 7L)).thenReturn(Map.of("cabinet_device_time_raw", "2026-09-05 12:00:00", "voltage_v", 3.7));
        service.submitAction(action, List.of());
        assertTrue(action.getBeforeContentJson().contains("cabinet_device_time_raw"));
        var order = inOrder(mapper);
        order.verify(mapper).insertDataRevisionAction(action);
        order.verify(mapper).deleteDataRecord(1L, "REALTIME", 7L);
    }
 
    @Test void stepExtensionEditPreservesOtherMetricsAndDisplaysPreviousValue()
    {
        var action = action("EDIT", "step:7"); action.setDataType("STEP");
        when(mapper.selectDataRecord(1L, "STEP", 7L)).thenReturn(Map.of("extension_measurements_json", "{\"startVoltage\":3.2,\"stepDuration\":60}"));
        var item = new DataRevisionItem(); item.setStableIdentity("step:7"); item.setFieldValues("{\"startVoltage\":3.4}");
        service.submitAction(action, List.of(item));
        var change = JSON.parseArray(action.getChangesSummary()).getJSONObject(0);
        assertEquals(new BigDecimal("3.2"), change.getJSONObject("before").getBigDecimal("startVoltage"));
        var values = org.mockito.ArgumentCaptor.forClass(Map.class);
        verify(mapper).updateDataRecord(eq(1L), eq("STEP"), eq(7L), values.capture());
        var extension = JSON.parseObject(values.getValue().get("extension_measurements_json").toString());
        assertEquals(60, extension.getIntValue("stepDuration"));
        assertEquals(new BigDecimal("3.4"), extension.getBigDecimal("startVoltage"));
    }
 
    @Test void rejectsWrongDataTypeIdentityAndOtherTestsRecords()
    {
        assertThrows(ServiceException.class, () -> service.submitAction(action("DELETE", "step:7"), List.of()));
        assertThrows(ServiceException.class, () -> service.submitAction(action("DELETE", "realtime:8"), List.of()));
        verify(mapper, never()).insertDataRevisionAction(any());
    }
 
    @Test void rejectsIdentityEditsAndNonNumericMeasurements()
    {
        when(mapper.selectDataRecord(1L, "REALTIME", 7L)).thenReturn(Map.of("voltage_v", 3.7));
        for (String fields : List.of("{\"test_id\":2}", "{\"voltage\":\"invalid\"}")) {
            var item = new DataRevisionItem(); item.setStableIdentity("realtime:7"); item.setFieldValues(fields);
            assertThrows(ServiceException.class, () -> service.submitAction(action("EDIT", "realtime:7"), List.of(item)));
        }
        verify(mapper, never()).insertDataRevisionAction(any());
    }
 
    @Test void importRequiresCompletedTest()
    {
        assertThrows(ServiceException.class, () -> service.submitAction(action("IMPORT", "manual-1"), List.of()));
        verify(mapper, never()).insertImportedDataRecord(anyString(), any());
    }
 
    @Test void completedRealtimeImportKeepsSourceRecordAndSequence()
    {
        var action = action("IMPORT", "manual-1");
        mapper.selectTestByIdForUpdate(1L).setTestStatus("COMPLETED");
        when(mapper.selectImportBinding(1L, "BAT-1")).thenReturn(Map.of("test_battery_id", 4L, "binding_id", 5L, "adapter_code", "adapter-a"));
        action.setChangesSummary("[{\"recordNo\":\"manual-1\",\"batteryNo\":\"BAT-1\",\"recordedAt\":1770000000123,\"sequenceId\":17,\"voltage\":3.6}]");
        service.submitAction(action, List.of());
        var values = org.mockito.ArgumentCaptor.forClass(Map.class);
        verify(mapper).insertImportedDataRecord(eq("REALTIME"), values.capture());
        assertEquals("manual-1", values.getValue().get("source_record_id"));
        assertEquals(17, values.getValue().get("cabinet_sequence_id"));
        assertEquals("IMPORT", values.getValue().get("source_identity"));
    }
 
    private TTestDataRevisionAction action(String operation, String identity)
    {
        TTest test = new TTest(); test.setTestId(1L); test.setTestStatus("RUNNING");
        when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test);
        var action = new TTestDataRevisionAction(); action.setTestId(1L); action.setDataType("REALTIME");
        action.setOperationType(operation); action.setIdentitySummary(JSON.toJSONString(List.of(identity)));
        action.setScopeSummary("核对一条数据"); action.setReason("现场核对"); action.setOperatorName("operator");
        action.setChangesSummary("[]"); return action;
    }
}