Junjie
2 天以前 d7d7e0edf4d8dc422402be9a1fbb6e535ae3761e
src/test/java/com/zy/ai/service/impl/AutoTuneSnapshotServiceImplTest.java
@@ -1,20 +1,61 @@
package com.zy.ai.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.zy.ai.domain.autotune.AutoTuneRoutePressureSnapshot;
import com.zy.ai.domain.autotune.AutoTuneRuleSnapshotItem;
import com.zy.ai.domain.autotune.AutoTuneSnapshot;
import com.zy.ai.domain.autotune.AutoTuneStationRuntimeItem;
import com.zy.ai.domain.autotune.AutoTuneTaskDetailItem;
import com.zy.ai.domain.autotune.AutoTuneTaskSnapshot;
import com.zy.ai.service.FlowTopologySnapshotService;
import com.zy.ai.service.RoutePressureSnapshotService;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.BasStationService;
import com.zy.asrs.service.WrkMastService;
import com.zy.core.enums.WrkIoType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.model.StationObjModel;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.system.service.ConfigService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class AutoTuneSnapshotServiceImplTest {
    private AutoTuneSnapshotServiceImpl service;
    @BeforeEach
    void setUp() {
        ConfigService configService = mock(ConfigService.class);
        service = new AutoTuneSnapshotServiceImpl();
        ReflectionTestUtils.setField(service, "configService", configService);
        ReflectionTestUtils.setField(service, "autoTuneControlModeService",
                new AutoTuneControlModeServiceImpl(configService));
    }
    @Test
    void buildStationOutTaskLimitMapPreservesNullZeroAndNegativeLimits() {
        AutoTuneSnapshotServiceImpl service = new AutoTuneSnapshotServiceImpl();
        Map<String, Integer> result = service.buildStationOutTaskLimitMap(Arrays.asList(
                station(101, null),
                station(102, 0),
@@ -28,10 +69,254 @@
        assertEquals(5, result.get("104"));
    }
    @Test
    void buildStationOutBufferCapacityMapPreservesConfiguredCapacity() {
        Map<String, Integer> result = service.buildStationOutBufferCapacityMap(Arrays.asList(
                station(101, 1, null),
                station(102, 1, 0),
                station(103, 1, 3)
        ));
        assertNull(result.get("101"));
        assertEquals(0, result.get("102"));
        assertEquals(3, result.get("103"));
    }
    @Test
    void buildRuleSnapshotExposesStepRangeCooldownAndRiskNote() {
        List<AutoTuneRuleSnapshotItem> result = service.buildRuleSnapshot();
        AutoTuneRuleSnapshotItem stationOutTaskRule = findRule(result, "station", "outTaskLimit");
        assertEquals(0, stationOutTaskRule.getMinValue());
        assertNull(stationOutTaskRule.getMaxValue());
        assertEquals(3, stationOutTaskRule.getMaxStep());
        assertEquals(10, stationOutTaskRule.getCooldownMinutes());
        assertEquals(Boolean.FALSE, stationOutTaskRule.getDynamicMaxValue());
        assertNull(stationOutTaskRule.getDynamicMaxSource());
        assertEquals("单次调整幅度不能超过 maxStep;outBufferCapacity 代表出库站缓存位置数量,超过该容量可能使任务出现在主干道,仅作为风险参考,不是合法性上限。",
                stationOutTaskRule.getNote());
        AutoTuneRuleSnapshotItem crnMaxOutRule = findRule(result, "crn", "maxOutTask");
        assertEquals(3, crnMaxOutRule.getMaxStep());
        assertNull(crnMaxOutRule.getDynamicMaxSource());
        assertEquals("单次调整幅度不能超过 maxStep。", crnMaxOutRule.getNote());
        AutoTuneRuleSnapshotItem crnMaxInRule = findRule(result, "crn", "maxInTask");
        assertEquals(3, crnMaxInRule.getMaxStep());
        assertRuleMaxStep(result, "sys_config", "crnOutBatchRunningLimit", 3);
        assertRuleMaxStep(result, "dual_crn", "maxOutTask", 3);
        assertRuleMaxStep(result, "dual_crn", "maxInTask", 3);
    }
    @Test
    void loadActiveTasksIncludesNullWrkStatusInGroupedCondition() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        when(wrkMastService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
        ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService);
        ReflectionTestUtils.invokeMethod(service, "loadActiveTasks");
        ArgumentCaptor<Wrapper<WrkMast>> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
        verify(wrkMastService).list(wrapperCaptor.capture());
        String sqlSegment = wrapperCaptor.getValue().getSqlSegment();
        String normalizedSqlSegment = sqlSegment.replaceAll("\\s+", " ");
        assertTrue(normalizedSqlSegment.contains("(wrk_sts NOT IN"));
        assertTrue(normalizedSqlSegment.contains("OR wrk_sts IS NULL"));
    }
    @Test
    void buildTaskSnapshotExposesStationLimitBlockedTasksAndOutboundOrder() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        when(wrkMastService.list(any(Wrapper.class))).thenReturn(Arrays.asList(
                outboundTask(190264, WrkStsType.STATION_RUN.sts, 101, 1,
                        "MANUAL_OUT_20260427181201", 9, ""),
                outboundTask(190263, WrkStsType.NEW_OUTBOUND.sts, 101, 1,
                        "MANUAL_OUT_20260427181201", 8,
                        "目标出库站:101 已达出库任务上限,当前=1,上限=1")
        ));
        ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService);
        AutoTuneTaskSnapshot snapshot = ReflectionTestUtils.invokeMethod(service, "buildTaskSnapshot");
        assertEquals(2, snapshot.getActiveTaskCount());
        assertEquals(1, snapshot.getByStatus().get("101"));
        assertEquals(1, snapshot.getByStatus().get("104"));
        List<AutoTuneTaskDetailItem> blockedTasks = snapshot.getStationLimitBlockedTasks();
        assertEquals(1, blockedTasks.size());
        assertEquals(190263, blockedTasks.get(0).getWrkNo());
        assertEquals("生成出库任务", blockedTasks.get(0).getWrkStsDesc());
        assertTrue(blockedTasks.get(0).getSystemMsg().contains("出库任务上限"));
        List<AutoTuneTaskDetailItem> outboundSamples = snapshot.getOutboundTaskSamples();
        assertEquals(2, outboundSamples.size());
        assertEquals(190263, outboundSamples.get(0).getWrkNo());
        assertEquals(8, outboundSamples.get(0).getBatchSeq());
        assertEquals(190264, outboundSamples.get(1).getWrkNo());
        assertEquals(9, outboundSamples.get(1).getBatchSeq());
    }
    @Test
    void buildSnapshotIncludesRoutePressureSnapshot() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        FlowTopologySnapshotService flowTopologySnapshotService = mock(FlowTopologySnapshotService.class);
        RoutePressureSnapshotService routePressureSnapshotService = mock(RoutePressureSnapshotService.class);
        AutoTuneRoutePressureSnapshot routePressureSnapshot = new AutoTuneRoutePressureSnapshot();
        List<WrkMast> activeTasks = Collections.singletonList(
                outboundTask(190263, WrkStsType.NEW_OUTBOUND.sts, 101, 1,
                        "BATCH", 8, "目标出库站:101 已达出库任务上限,当前=1,上限=1")
        );
        when(wrkMastService.list(any(Wrapper.class))).thenReturn(activeTasks);
        when(flowTopologySnapshotService.buildSnapshot(any())).thenReturn(Collections.emptyList());
        when(routePressureSnapshotService.buildSnapshot(any(), any(), any())).thenReturn(routePressureSnapshot);
        ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(service, "flowTopologySnapshotService", flowTopologySnapshotService);
        ReflectionTestUtils.setField(service, "routePressureSnapshotService", routePressureSnapshotService);
        AutoTuneSnapshot snapshot = service.buildSnapshot();
        assertSame(routePressureSnapshot, snapshot.getRoutePressureSnapshot());
        assertEquals(1, snapshot.getTaskSnapshot().getActiveTaskCount());
        ArgumentCaptor<List<WrkMast>> activeTasksCaptor = ArgumentCaptor.forClass(List.class);
        ArgumentCaptor<AutoTuneTaskSnapshot> taskSnapshotCaptor = ArgumentCaptor.forClass(AutoTuneTaskSnapshot.class);
        ArgumentCaptor<List<AutoTuneStationRuntimeItem>> stationRuntimeCaptor = ArgumentCaptor.forClass(List.class);
        verify(routePressureSnapshotService).buildSnapshot(
                activeTasksCaptor.capture(),
                taskSnapshotCaptor.capture(),
                stationRuntimeCaptor.capture()
        );
        assertSame(activeTasks, activeTasksCaptor.getValue());
        assertSame(snapshot.getTaskSnapshot(), taskSnapshotCaptor.getValue());
        assertSame(snapshot.getStationRuntimeSnapshot(), stationRuntimeCaptor.getValue());
        verify(wrkMastService).list(any(Wrapper.class));
    }
    @Test
    void loadOutStationListOnlyQueriesBasDevpOutStations() {
        BasStationService basStationService = mock(BasStationService.class);
        BasDevpService basDevpService = mock(BasDevpService.class);
        ReflectionTestUtils.setField(service, "basStationService", basStationService);
        ReflectionTestUtils.setField(service, "basDevpService", basDevpService);
        when(basDevpService.list(any(Wrapper.class))).thenReturn(Arrays.asList(
                basDevp(1, 101, 102),
                basDevp(2, 102)
        ));
        when(basStationService.list(any(Wrapper.class))).thenReturn(Arrays.asList(
                station(101, 2),
                station(102, 3)
        ));
        java.util.List<BasStation> result = ReflectionTestUtils.invokeMethod(service, "loadOutStationList");
        assertEquals(2, result.size());
        assertEquals(101, result.get(0).getStationId());
        assertEquals(102, result.get(1).getStationId());
        ArgumentCaptor<Wrapper<BasDevp>> basDevpWrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
        verify(basDevpService).list(basDevpWrapperCaptor.capture());
        assertTrue(basDevpWrapperCaptor.getValue().getSqlSegment().contains("status ="));
        ArgumentCaptor<Wrapper<BasStation>> stationWrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
        verify(basStationService).list(stationWrapperCaptor.capture());
        assertTrue(stationWrapperCaptor.getValue().getSqlSegment().contains("station_id IN"));
    }
    @Test
    void loadOutStationListDoesNotQueryStationsWhenBasDevpOutStationsAreEmpty() {
        BasStationService basStationService = mock(BasStationService.class);
        BasDevpService basDevpService = mock(BasDevpService.class);
        ReflectionTestUtils.setField(service, "basStationService", basStationService);
        ReflectionTestUtils.setField(service, "basDevpService", basDevpService);
        when(basDevpService.list(any(Wrapper.class))).thenReturn(Collections.singletonList(basDevp(1)));
        java.util.List<BasStation> result = ReflectionTestUtils.invokeMethod(service, "loadOutStationList");
        assertTrue(result.isEmpty());
        verify(basStationService, never()).list(any(Wrapper.class));
    }
    @Test
    void toRuntimeItemExposesRunBlockForRoutePressure() {
        StationProtocol protocol = new StationProtocol();
        protocol.setStationId(101);
        protocol.setAutoing(true);
        protocol.setLoading(false);
        protocol.setTaskNo(0);
        protocol.setRunBlock(true);
        AutoTuneStationRuntimeItem item = ReflectionTestUtils.invokeMethod(service, "toRuntimeItem", protocol);
        assertEquals(1, item.getRunBlock());
    }
    private BasStation station(Integer stationId, Integer outTaskLimit) {
        return station(stationId, outTaskLimit, null);
    }
    private BasStation station(Integer stationId, Integer outTaskLimit, Integer outBufferCapacity) {
        BasStation station = new BasStation();
        station.setStationId(stationId);
        station.setOutTaskLimit(outTaskLimit);
        station.setOutBufferCapacity(outBufferCapacity);
        return station;
    }
    private WrkMast outboundTask(Integer wrkNo,
                                 long wrkSts,
                                 Integer stationNo,
                                 Integer crnNo,
                                 String batch,
                                 Integer batchSeq,
                                 String systemMsg) {
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(wrkNo);
        wrkMast.setWrkSts(wrkSts);
        wrkMast.setIoType(WrkIoType.OUT.id);
        wrkMast.setStaNo(stationNo);
        wrkMast.setCrnNo(crnNo);
        wrkMast.setBatch(batch);
        wrkMast.setBatchSeq(batchSeq);
        wrkMast.setSystemMsg(systemMsg);
        return wrkMast;
    }
    private BasDevp basDevp(Integer devpNo, Integer... stationIds) {
        BasDevp basDevp = new BasDevp();
        basDevp.setDevpNo(devpNo);
        basDevp.setStatus(1);
        if (stationIds == null || stationIds.length == 0) {
            return basDevp;
        }
        basDevp.setOutStationList(JSON.toJSONString(Arrays.stream(stationIds)
                .map(this::stationObjModel)
                .toList()));
        return basDevp;
    }
    private StationObjModel stationObjModel(Integer stationId) {
        StationObjModel stationObjModel = new StationObjModel();
        stationObjModel.setStationId(stationId);
        return stationObjModel;
    }
    private AutoTuneRuleSnapshotItem findRule(List<AutoTuneRuleSnapshotItem> rules,
                                              String targetType,
                                              String targetKey) {
        for (AutoTuneRuleSnapshotItem rule : rules) {
            if (targetType.equals(rule.getTargetType()) && targetKey.equals(rule.getTargetKey())) {
                return rule;
            }
        }
        throw new AssertionError("rule not found: " + targetType + "/" + targetKey);
    }
    private void assertRuleMaxStep(List<AutoTuneRuleSnapshotItem> rules,
                                   String targetType,
                                   String targetKey,
                                   int expectedMaxStep) {
        AutoTuneRuleSnapshotItem rule = findRule(rules, targetType, targetKey);
        assertEquals(expectedMaxStep, rule.getMaxStep());
    }
}