From f955390da5d6fe785bfb9828b44f1603cd4ff0b8 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期一, 27 四月 2026 11:07:25 +0800
Subject: [PATCH] fix: harden auto tune apply audit and rollback
---
src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 105 insertions(+), 5 deletions(-)
diff --git a/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java b/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
index d17da07..cab797d 100644
--- a/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
+++ b/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
@@ -115,6 +115,16 @@
}
@Test
+ void rejectNonnumericNewValue() {
+ AutoTuneApplyResult result = service.apply(request(true, command("sys_config", null, "aiAutoTuneIntervalMinutes", "abc")));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertFalse(result.getSuccess());
+ assertEquals("rejected", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("蹇呴』涓烘暣鏁�"));
+ }
+
+ @Test
void rejectOverStepConveyorLimitChange() {
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
@@ -123,6 +133,38 @@
List<AiAutoTuneChange> changes = savedChanges();
assertEquals("rejected", changes.get(0).getResultStatus());
assertTrue(changes.get(0).getRejectReason().contains("姝ラ暱涓嶈兘瓒呰繃 5"));
+ }
+
+ @Test
+ void rejectCrnOutBatchRunningLimitRangeAndStepCases() {
+ 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", "21")
+ ));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertEquals("rejected", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("姝ラ暱涓嶈兘瓒呰繃 2"));
+ assertEquals("rejected", changes.get(1).getResultStatus());
+ assertTrue(changes.get(1).getRejectReason().contains("1~20"));
+ }
+
+ @Test
+ void rejectMaxInTaskRangeAndStepCases() {
+ when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 5));
+
+ service.apply(request(true,
+ command("crn", "1", "maxInTask", "7"),
+ command("crn", "1", "maxInTask", "11")
+ ));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertEquals("rejected", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("姝ラ暱涓嶈兘瓒呰繃 1"));
+ assertEquals("rejected", changes.get(1).getResultStatus());
+ assertTrue(changes.get(1).getRejectReason().contains("0~10"));
}
@Test
@@ -150,6 +192,40 @@
List<AiAutoTuneChange> changes = savedChanges();
assertEquals("rejected", changes.get(0).getResultStatus());
assertTrue(changes.get(0).getRejectReason().contains("鍐峰嵈鏈�"));
+ }
+
+ @Test
+ void realMixedValidAndInvalidBatchRejectsAndDoesNotWriteTargets() {
+ 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));
+
+ AutoTuneApplyResult result = service.apply(request(false,
+ command("sys_config", null, "conveyorStationTaskLimit", "15"),
+ command("station", "101", "outTaskLimit", "3")
+ ));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertFalse(result.getSuccess());
+ assertEquals(2, changes.size());
+ assertTrue(changes.stream().allMatch(change -> "rejected".equals(change.getResultStatus())));
+ verify(configService, never()).saveConfigValue(any(), any());
+ verify(basStationService, never()).update(any(Wrapper.class));
+ }
+
+ @Test
+ void acceptedDryRunDoesNotWriteTargetStores() {
+ when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
+
+ AutoTuneApplyResult result = service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "15")));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertTrue(result.getSuccess());
+ assertEquals("dry_run", changes.get(0).getResultStatus());
+ verify(configService, never()).saveConfigValue(any(), any());
+ verify(basStationService, never()).update(any(Wrapper.class));
+ verify(basCrnpService, never()).update(any(Wrapper.class));
+ verify(basDualCrnpService, never()).update(any(Wrapper.class));
}
@Test
@@ -181,14 +257,30 @@
}
@Test
- void rollbackLastJobSuccessfully() {
- AiAutoTuneJob latestJob = new AiAutoTuneJob();
- latestJob.setId(10L);
- when(aiAutoTuneJobService.list(any(Wrapper.class))).thenReturn(Collections.singletonList(latestJob));
+ void writeFailureReturnsFailedAuditWithoutThrowing() {
+ when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10"));
+ when(configService.saveConfigValue("conveyorStationTaskLimit", "15")).thenThrow(new IllegalStateException("db write failed"));
+ AutoTuneApplyResult result = service.apply(request(false, command("sys_config", null, "conveyorStationTaskLimit", "15")));
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ assertFalse(result.getSuccess());
+ assertEquals("failed", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("db write failed"));
+ }
+
+ @Test
+ void rollbackLastJobSuccessfully() {
+ AiAutoTuneJob rollbackJob = job(20L, "rollback", "success");
+ AiAutoTuneJob latestRealJob = job(10L, "manual", "success");
+ AiAutoTuneChange rollbackChange = successChange(20L, "sys_config", "", "conveyorStationTaskLimit", "15", "18");
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(aiAutoTuneChangeService.list(any(Wrapper.class)))
+ .thenReturn(List.of(rollbackChange, configChange, stationChange))
+ .thenReturn(List.of(configChange, stationChange));
+ when(aiAutoTuneJobService.getById(10L)).thenReturn(latestRealJob);
+ when(aiAutoTuneJobService.getById(20L)).thenReturn(rollbackJob);
when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "15"));
when(basStationService.getById(101)).thenReturn(station(101, 2));
@@ -277,6 +369,14 @@
return change;
}
+ private AiAutoTuneJob job(Long id, String triggerType, String status) {
+ AiAutoTuneJob job = new AiAutoTuneJob();
+ job.setId(id);
+ job.setTriggerType(triggerType);
+ job.setStatus(status);
+ return job;
+ }
+
private List<AiAutoTuneChange> savedChanges() {
ArgumentCaptor<Collection<AiAutoTuneChange>> captor = ArgumentCaptor.forClass(Collection.class);
verify(aiAutoTuneChangeService).saveBatch(captor.capture());
--
Gitblit v1.9.1