package com.zy.ai.service;
|
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.zy.ai.domain.autotune.AutoTuneApplyRequest;
|
import com.zy.ai.domain.autotune.AutoTuneApplyResult;
|
import com.zy.ai.domain.autotune.AutoTuneChangeCommand;
|
import com.zy.ai.entity.AiAutoTuneChange;
|
import com.zy.ai.entity.AiAutoTuneJob;
|
import com.zy.ai.service.impl.AutoTuneApplyServiceImpl;
|
import com.zy.asrs.entity.BasCrnp;
|
import com.zy.asrs.entity.BasDualCrnp;
|
import com.zy.asrs.entity.BasStation;
|
import com.zy.asrs.entity.StationFlowCapacity;
|
import com.zy.asrs.service.BasCrnpService;
|
import com.zy.asrs.service.BasDualCrnpService;
|
import com.zy.asrs.service.BasStationService;
|
import com.zy.asrs.service.StationFlowCapacityService;
|
import com.zy.system.entity.Config;
|
import com.zy.system.service.ConfigService;
|
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 org.mockito.junit.jupiter.MockitoSettings;
|
import org.mockito.quality.Strictness;
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.List;
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
@ExtendWith(MockitoExtension.class)
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
class AutoTuneApplyServiceImplTest {
|
|
private AutoTuneApplyServiceImpl service;
|
|
@Mock
|
private AiAutoTuneJobService aiAutoTuneJobService;
|
@Mock
|
private AiAutoTuneChangeService aiAutoTuneChangeService;
|
@Mock
|
private ConfigService configService;
|
@Mock
|
private BasStationService basStationService;
|
@Mock
|
private BasCrnpService basCrnpService;
|
@Mock
|
private BasDualCrnpService basDualCrnpService;
|
@Mock
|
private StationFlowCapacityService stationFlowCapacityService;
|
|
@BeforeEach
|
void setUp() {
|
service = new AutoTuneApplyServiceImpl();
|
ReflectionTestUtils.setField(service, "aiAutoTuneJobService", aiAutoTuneJobService);
|
ReflectionTestUtils.setField(service, "aiAutoTuneChangeService", aiAutoTuneChangeService);
|
ReflectionTestUtils.setField(service, "configService", configService);
|
ReflectionTestUtils.setField(service, "basStationService", basStationService);
|
ReflectionTestUtils.setField(service, "basCrnpService", basCrnpService);
|
ReflectionTestUtils.setField(service, "basDualCrnpService", basDualCrnpService);
|
ReflectionTestUtils.setField(service, "stationFlowCapacityService", stationFlowCapacityService);
|
|
AtomicLong jobId = new AtomicLong(100);
|
when(aiAutoTuneJobService.save(any(AiAutoTuneJob.class))).thenAnswer(invocation -> {
|
AiAutoTuneJob job = invocation.getArgument(0);
|
job.setId(jobId.incrementAndGet());
|
return true;
|
});
|
when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenReturn(true);
|
when(aiAutoTuneChangeService.saveBatch(any(Collection.class))).thenReturn(true);
|
when(aiAutoTuneChangeService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
when(configService.getConfigValue(eq("aiAutoTuneIntervalMinutes"), any())).thenReturn("10");
|
when(configService.saveConfigValue(any(), any())).thenReturn(true);
|
when(basStationService.update(any(Wrapper.class))).thenReturn(true);
|
when(basCrnpService.update(any(Wrapper.class))).thenReturn(true);
|
when(basDualCrnpService.update(any(Wrapper.class))).thenReturn(true);
|
}
|
|
@Test
|
void rejectNonWhitelistedKey() {
|
AutoTuneApplyResult result = service.apply(request(true, command("sys_config", null, "badKey", "10")));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertFalse(result.getSuccess());
|
assertEquals("rejected", changes.get(0).getResultStatus());
|
assertTrue(changes.get(0).getRejectReason().contains("不支持的调参目标"));
|
verify(configService, never()).saveConfigValue(any(), any());
|
}
|
|
@Test
|
void rejectOutOfRangeInterval() {
|
when(configService.getOne(any(Wrapper.class))).thenReturn(config("aiAutoTuneIntervalMinutes", "10"));
|
|
AutoTuneApplyResult result = service.apply(request(true, command("sys_config", null, "aiAutoTuneIntervalMinutes", "100")));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertFalse(result.getSuccess());
|
assertEquals("rejected", changes.get(0).getResultStatus());
|
assertTrue(changes.get(0).getRejectReason().contains("5~60"));
|
}
|
|
@Test
|
void rejectOverStepConveyorLimitChange() {
|
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
|
|
service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "16")));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertEquals("rejected", changes.get(0).getResultStatus());
|
assertTrue(changes.get(0).getRejectReason().contains("步长不能超过 5"));
|
}
|
|
@Test
|
void rejectStationOutTaskLimitAboveDirectionalBufferCapacity() {
|
when(basStationService.getById(101)).thenReturn(station(101, 1));
|
when(stationFlowCapacityService.getOne(any(Wrapper.class))).thenReturn(capacity(101, "OUT", 2));
|
|
service.apply(request(true, command("station", "101", "outTaskLimit", "3")));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertEquals("rejected", changes.get(0).getResultStatus());
|
assertTrue(changes.get(0).getRejectReason().contains("0~2"));
|
}
|
|
@Test
|
void rejectCooldownHit() {
|
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
|
AiAutoTuneChange cooldownChange = new AiAutoTuneChange();
|
cooldownChange.setResultStatus("success");
|
cooldownChange.setCooldownExpireTime(new Date(System.currentTimeMillis() + 60_000L));
|
when(aiAutoTuneChangeService.list(any(Wrapper.class))).thenReturn(Collections.singletonList(cooldownChange));
|
|
service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "15")));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertEquals("rejected", changes.get(0).getResultStatus());
|
assertTrue(changes.get(0).getRejectReason().contains("冷却期"));
|
}
|
|
@Test
|
void applyMixedBatchSuccessfully() {
|
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
|
when(basStationService.getById(101)).thenReturn(station(101, 1));
|
when(stationFlowCapacityService.getOne(any(Wrapper.class))).thenReturn(capacity(101, "OUT", 2));
|
when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 1));
|
when(basDualCrnpService.getById(2)).thenReturn(dualCrn(2, 1, 1));
|
|
AutoTuneApplyResult result = service.apply(request(false,
|
command("sys_config", null, "conveyorStationTaskLimit", "15"),
|
command("station", "101", "outTaskLimit", "2"),
|
command("crn", "1", "maxOutTask", "2"),
|
command("dual_crn", "2", "maxInTask", "2")
|
));
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertTrue(result.getSuccess());
|
assertEquals(4, changes.size());
|
assertEquals(4, result.getSuccessCount());
|
assertEquals(0, result.getRejectCount());
|
assertTrue(changes.stream().allMatch(change -> "success".equals(change.getResultStatus())));
|
verify(configService).saveConfigValue("conveyorStationTaskLimit", "15");
|
verify(configService).refreshSystemConfigCache();
|
verify(basStationService).update(any(Wrapper.class));
|
verify(basCrnpService).update(any(Wrapper.class));
|
verify(basDualCrnpService).update(any(Wrapper.class));
|
}
|
|
@Test
|
void rollbackLastJobSuccessfully() {
|
AiAutoTuneJob latestJob = new AiAutoTuneJob();
|
latestJob.setId(10L);
|
when(aiAutoTuneJobService.list(any(Wrapper.class))).thenReturn(Collections.singletonList(latestJob));
|
|
AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15");
|
AiAutoTuneChange stationChange = successChange(10L, "station", "101", "outTaskLimit", "1", "2");
|
when(aiAutoTuneChangeService.list(any(Wrapper.class))).thenReturn(List.of(configChange, stationChange));
|
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "15"));
|
when(basStationService.getById(101)).thenReturn(station(101, 2));
|
|
AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback");
|
|
List<AiAutoTuneChange> changes = savedChanges();
|
assertTrue(result.getSuccess());
|
assertEquals(2, changes.size());
|
assertTrue(changes.stream().allMatch(change -> "success".equals(change.getResultStatus())));
|
verify(configService).saveConfigValue("conveyorStationTaskLimit", "10");
|
verify(configService).refreshSystemConfigCache();
|
verify(basStationService).update(any(Wrapper.class));
|
}
|
|
private AutoTuneApplyRequest request(boolean dryRun, AutoTuneChangeCommand... commands) {
|
AutoTuneApplyRequest request = new AutoTuneApplyRequest();
|
request.setDryRun(dryRun);
|
request.setReason("test");
|
request.setTriggerType("manual");
|
request.setChanges(List.of(commands));
|
return request;
|
}
|
|
private AutoTuneChangeCommand command(String targetType, String targetId, String targetKey, String newValue) {
|
AutoTuneChangeCommand command = new AutoTuneChangeCommand();
|
command.setTargetType(targetType);
|
command.setTargetId(targetId);
|
command.setTargetKey(targetKey);
|
command.setNewValue(newValue);
|
return command;
|
}
|
|
private Config config(String code, String value) {
|
Config config = new Config();
|
config.setCode(code);
|
config.setValue(value);
|
config.setStatus((short) 1);
|
return config;
|
}
|
|
private BasStation station(Integer stationId, Integer outTaskLimit) {
|
BasStation station = new BasStation();
|
station.setStationId(stationId);
|
station.setOutTaskLimit(outTaskLimit);
|
return station;
|
}
|
|
private BasCrnp crn(Integer crnNo, Integer maxOutTask, Integer maxInTask) {
|
BasCrnp crnp = new BasCrnp();
|
crnp.setCrnNo(crnNo);
|
crnp.setMaxOutTask(maxOutTask);
|
crnp.setMaxInTask(maxInTask);
|
return crnp;
|
}
|
|
private BasDualCrnp dualCrn(Integer crnNo, Integer maxOutTask, Integer maxInTask) {
|
BasDualCrnp dualCrnp = new BasDualCrnp();
|
dualCrnp.setCrnNo(crnNo);
|
dualCrnp.setMaxOutTask(maxOutTask);
|
dualCrnp.setMaxInTask(maxInTask);
|
return dualCrnp;
|
}
|
|
private StationFlowCapacity capacity(Integer stationId, String directionCode, Integer bufferCapacity) {
|
StationFlowCapacity capacity = new StationFlowCapacity();
|
capacity.setStationId(stationId);
|
capacity.setDirectionCode(directionCode);
|
capacity.setBufferCapacity(bufferCapacity);
|
return capacity;
|
}
|
|
private AiAutoTuneChange successChange(Long jobId,
|
String targetType,
|
String targetId,
|
String targetKey,
|
String oldValue,
|
String appliedValue) {
|
AiAutoTuneChange change = new AiAutoTuneChange();
|
change.setJobId(jobId);
|
change.setTargetType(targetType);
|
change.setTargetId(targetId);
|
change.setTargetKey(targetKey);
|
change.setOldValue(oldValue);
|
change.setAppliedValue(appliedValue);
|
change.setResultStatus("success");
|
return change;
|
}
|
|
private List<AiAutoTuneChange> savedChanges() {
|
ArgumentCaptor<Collection<AiAutoTuneChange>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(aiAutoTuneChangeService).saveBatch(captor.capture());
|
return new ArrayList<>(captor.getValue());
|
}
|
}
|