Junjie
2 天以前 63b01db83d9aad8a15276b4236a9a22e4aeef065
src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
@@ -7,6 +7,7 @@
import com.zy.ai.entity.AiAutoTuneChange;
import com.zy.ai.entity.AiAutoTuneJob;
import com.zy.ai.service.impl.AutoTuneApplyServiceImpl;
import com.zy.ai.service.impl.AutoTuneControlModeServiceImpl;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.BasDualCrnp;
import com.zy.asrs.entity.BasStation;
@@ -89,6 +90,8 @@
        ReflectionTestUtils.setField(service, "aiAutoTuneJobService", aiAutoTuneJobService);
        ReflectionTestUtils.setField(service, "aiAutoTuneChangeService", aiAutoTuneChangeService);
        ReflectionTestUtils.setField(service, "configService", configService);
        ReflectionTestUtils.setField(service, "autoTuneControlModeService",
                new AutoTuneControlModeServiceImpl(configService));
        ReflectionTestUtils.setField(service, "basStationService", basStationService);
        ReflectionTestUtils.setField(service, "basCrnpService", basCrnpService);
        ReflectionTestUtils.setField(service, "basDualCrnpService", basDualCrnpService);
@@ -111,6 +114,7 @@
        when(aiAutoTuneChangeService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
        when(wrkMastService.count(any(Wrapper.class))).thenReturn(0L);
        when(configService.getConfigValue(eq("aiAutoTuneIntervalMinutes"), any())).thenReturn("10");
        when(configService.getConfigValue("aiAutoTuneAnalysisOnly", "Y")).thenReturn("N");
        when(configService.saveConfigValue(any(), any())).thenReturn(true);
        when(basStationService.update(any(Wrapper.class))).thenReturn(true);
        when(basCrnpService.update(any(Wrapper.class))).thenReturn(true);
@@ -163,46 +167,90 @@
    }
    @Test
    void rejectCrnOutBatchRunningLimitRangeAndStepCases() {
    void crnOutBatchRunningLimitAllowsStepThreeAndRejectsRangeAndStepCases() {
        when(configService.getOne(any(Wrapper.class))).thenReturn(config("crnOutBatchRunningLimit", "10"));
        service.apply(request(true,
                command("sys_config", null, "crnOutBatchRunningLimit", "13"),
                command("sys_config", null, "crnOutBatchRunningLimit", "14"),
                command("sys_config", null, "crnOutBatchRunningLimit", "21")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertTrue(changes.get(0).getRejectReason().contains("步长不能超过 2"));
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("13", changes.get(0).getRequestedValue());
        assertEquals("rejected", changes.get(1).getResultStatus());
        assertTrue(changes.get(1).getRejectReason().contains("1~20"));
        assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3"));
        assertEquals("rejected", changes.get(2).getResultStatus());
        assertTrue(changes.get(2).getRejectReason().contains("1~20"));
    }
    @Test
    void rejectMaxInTaskRangeAndStepCases() {
    void maxInTaskAllowsStepThreeAndRejectsRangeAndStepCases() {
        when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 5));
        service.apply(request(true,
                command("crn", "1", "maxInTask", "7"),
                command("crn", "1", "maxInTask", "8"),
                command("crn", "1", "maxInTask", "9"),
                command("crn", "1", "maxInTask", "11")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertTrue(changes.get(0).getRejectReason().contains("步长不能超过 1"));
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("8", changes.get(0).getRequestedValue());
        assertEquals("rejected", changes.get(1).getResultStatus());
        assertTrue(changes.get(1).getRejectReason().contains("0~10"));
        assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3"));
        assertEquals("rejected", changes.get(2).getResultStatus());
        assertTrue(changes.get(2).getRejectReason().contains("0~10"));
    }
    @Test
    void rejectStationOutTaskLimitAboveDirectionalBufferCapacity() {
        when(basStationService.getById(101)).thenReturn(station(101, 1, 2));
    void maxOutTaskAllowsStepThreeAndRejectsStepFour() {
        when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 1));
        service.apply(request(true, command("station", "101", "outTaskLimit", "3")));
        service.apply(request(true,
                command("crn", "1", "maxOutTask", "4"),
                command("crn", "1", "maxOutTask", "5")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertTrue(changes.get(0).getRejectReason().contains("0~2"));
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("4", changes.get(0).getRequestedValue());
        assertEquals("rejected", changes.get(1).getResultStatus());
        assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3"));
    }
    @Test
    void dualCrnMaxOutTaskAllowsStepThreeAndRejectsStepFour() {
        when(basDualCrnpService.getById(2)).thenReturn(dualCrn(2, 1, 1));
        service.apply(request(true,
                command("dual_crn", "2", "maxOutTask", "4"),
                command("dual_crn", "2", "maxOutTask", "5")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("4", changes.get(0).getRequestedValue());
        assertEquals("rejected", changes.get(1).getResultStatus());
        assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3"));
    }
    @Test
    void stationOutTaskLimitAllowsAboveBufferCapacityButRejectsOverStep() {
        when(basStationService.getById(101)).thenReturn(station(101, 5, 3));
        service.apply(request(true,
                command("station", "101", "outTaskLimit", "6"),
                command("station", "101", "outTaskLimit", "9")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("5", changes.get(0).getOldValue());
        assertEquals("6", changes.get(0).getRequestedValue());
        assertEquals("rejected", changes.get(1).getResultStatus());
        assertTrue(changes.get(1).getRejectReason().contains("步长不能超过 3"));
    }
    @Test
@@ -241,14 +289,15 @@
    }
    @Test
    void rejectStationOutTaskLimitWithoutOutBufferCapacity() {
    void allowStationOutTaskLimitWithoutOutBufferCapacity() {
        when(basStationService.getById(101)).thenReturn(station(101, 0));
        service.apply(request(true, command("station", "101", "outTaskLimit", "1")));
        AutoTuneApplyResult result = service.apply(request(true, command("station", "101", "outTaskLimit", "1")));
        List<AiAutoTuneChange> changes = savedChanges();
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertTrue(changes.get(0).getRejectReason().contains("缺少 outBufferCapacity"));
        assertTrue(result.getSuccess());
        assertEquals("dry_run", changes.get(0).getResultStatus());
        assertEquals("1", changes.get(0).getRequestedValue());
    }
    @Test
@@ -273,7 +322,7 @@
        AutoTuneApplyResult result = service.apply(request(false,
                command("sys_config", null, "conveyorStationTaskLimit", "15"),
                command("station", "101", "outTaskLimit", "3")
                command("station", "101", "outTaskLimit", "5")
        ));
        List<AiAutoTuneChange> changes = savedChanges();
@@ -282,6 +331,38 @@
        assertTrue(changes.stream().allMatch(change -> "rejected".equals(change.getResultStatus())));
        verify(configService, never()).saveConfigValue(any(), any());
        verify(basStationService, never()).update(any(Wrapper.class));
    }
    @Test
    void analysisOnlyRealApplyWritesRejectedAuditAndDoesNotAcquireApplyLock() {
        when(configService.getConfigValue("aiAutoTuneAnalysisOnly", "Y")).thenReturn("Y");
        AutoTuneApplyResult result = service.apply(request(false,
                command("sys_config", null, "conveyorStationTaskLimit", "15")));
        List<AiAutoTuneChange> changes = savedChanges();
        AiAutoTuneJob job = updatedJob();
        assertFalse(result.getSuccess());
        assertTrue(result.getAnalysisOnly());
        assertTrue(result.getNoApply());
        assertEquals("rejected", job.getStatus());
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertTrue(changes.get(0).getRejectReason().contains("仅分析模式禁止实际应用/回滚"));
        verify(redisUtil, never()).trySetStringIfAbsent(anyString(), anyString(), anyLong());
        verify(configService, never()).saveConfigValue(any(), any());
    }
    @Test
    void realApplyResultUsesEntryControlModeSnapshotWhenConfigFlips() {
        when(configService.getConfigValue("aiAutoTuneAnalysisOnly", "Y")).thenReturn("N", "Y");
        when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
        AutoTuneApplyResult result = service.apply(request(false,
                command("sys_config", null, "conveyorStationTaskLimit", "15")));
        assertTrue(result.getSuccess());
        assertFalse(result.getAnalysisOnly());
        verify(configService, times(1)).getConfigValue("aiAutoTuneAnalysisOnly", "Y");
    }
    @Test
@@ -300,6 +381,41 @@
    }
    @Test
    void analysisOnlyRollbackWritesRejectedAuditAndDoesNotAcquireApplyLock() {
        when(configService.getConfigValue("aiAutoTuneAnalysisOnly", "Y")).thenReturn("Y");
        AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback");
        List<AiAutoTuneChange> changes = savedChanges();
        AiAutoTuneJob job = updatedJob();
        assertFalse(result.getSuccess());
        assertTrue(result.getAnalysisOnly());
        assertTrue(result.getNoApply());
        assertEquals("rejected", job.getStatus());
        assertEquals("rejected", changes.get(0).getResultStatus());
        assertEquals("rollback", changes.get(0).getTargetKey());
        verify(redisUtil, never()).trySetStringIfAbsent(anyString(), anyString(), anyLong());
    }
    @Test
    void rollbackResultUsesEntryControlModeSnapshotWhenConfigFlips() {
        when(configService.getConfigValue("aiAutoTuneAnalysisOnly", "Y")).thenReturn("N", "Y");
        AiAutoTuneJob latestRealJob = job(10L, "manual", "success");
        AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15");
        when(aiAutoTuneChangeService.list(any(Wrapper.class)))
                .thenReturn(List.of(configChange))
                .thenReturn(List.of(configChange));
        when(aiAutoTuneJobService.getById(10L)).thenReturn(latestRealJob);
        when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "15"));
        AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback");
        assertTrue(result.getSuccess());
        assertFalse(result.getAnalysisOnly());
        verify(configService, times(1)).getConfigValue("aiAutoTuneAnalysisOnly", "Y");
    }
    @Test
    void applyJobRecordsActiveTasksWhenCountIsPositive() {
        when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
        when(wrkMastService.count(any(Wrapper.class))).thenReturn(1L);