| | |
| | | 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; |
| | | import com.zy.asrs.entity.StationFlowCapacity; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | 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.asrs.service.WrkMastService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | 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.InOrder; |
| | | 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 org.springframework.transaction.PlatformTransactionManager; |
| | | import org.springframework.transaction.TransactionDefinition; |
| | | import org.springframework.transaction.TransactionStatus; |
| | | import org.springframework.transaction.support.SimpleTransactionStatus; |
| | | |
| | | 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.AtomicInteger; |
| | | 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.assertThrows; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | import static org.mockito.ArgumentMatchers.anyLong; |
| | | import static org.mockito.ArgumentMatchers.anyString; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.mockito.Mockito.atLeastOnce; |
| | | import static org.mockito.Mockito.doThrow; |
| | | import static org.mockito.Mockito.inOrder; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.times; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | |
| | | @Mock |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Mock |
| | | private StationFlowCapacityService stationFlowCapacityService; |
| | | private WrkMastService wrkMastService; |
| | | @Mock |
| | | private RedisUtil redisUtil; |
| | | private RecordingTransactionManager transactionManager; |
| | | |
| | | @BeforeEach |
| | | void setUp() { |
| | | service = new AutoTuneApplyServiceImpl(); |
| | | transactionManager = new RecordingTransactionManager(); |
| | | 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); |
| | | ReflectionTestUtils.setField(service, "stationFlowCapacityService", stationFlowCapacityService); |
| | | ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService); |
| | | ReflectionTestUtils.setField(service, "transactionManager", transactionManager); |
| | | ReflectionTestUtils.setField(service, "redisUtil", redisUtil); |
| | | |
| | | AtomicLong jobId = new AtomicLong(100); |
| | | when(aiAutoTuneJobService.save(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobSaveCall(); |
| | | AiAutoTuneJob job = invocation.getArgument(0); |
| | | job.setId(jobId.incrementAndGet()); |
| | | return true; |
| | | }); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenReturn(true); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobUpdateCall(); |
| | | return true; |
| | | }); |
| | | when(aiAutoTuneChangeService.saveBatch(any(Collection.class))).thenReturn(true); |
| | | 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); |
| | | when(basDualCrnpService.update(any(Wrapper.class))).thenReturn(true); |
| | | when(redisUtil.trySetStringIfAbsent(anyString(), anyString(), anyLong())).thenReturn(true); |
| | | } |
| | | |
| | | @Test |
| | |
| | | } |
| | | |
| | | @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)); |
| | | when(stationFlowCapacityService.getOne(any(Wrapper.class))).thenReturn(capacity(101, "OUT", 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("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 |
| | | void rejectStationOutTaskLimitNullUnlimitedCurrentValue() { |
| | | when(basStationService.getById(101)).thenReturn(station(101, null)); |
| | | |
| | | 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("0~2")); |
| | | assertTrue(changes.get(0).getRejectReason().contains("需要人工先初始化为有限值")); |
| | | } |
| | | |
| | | @Test |
| | | void rejectStationOutTaskLimitNegativeUnlimitedCurrentValue() { |
| | | when(basStationService.getById(101)).thenReturn(station(101, -1)); |
| | | |
| | | 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("需要人工先初始化为有限值")); |
| | | } |
| | | |
| | | @Test |
| | | void allowStationOutTaskLimitZeroToOneAsFiniteStep() { |
| | | when(basStationService.getById(101)).thenReturn(station(101, 0, 1)); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(true, command("station", "101", "outTaskLimit", "1"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertTrue(result.getSuccess()); |
| | | assertEquals("dry_run", changes.get(0).getResultStatus()); |
| | | assertEquals("0", changes.get(0).getOldValue()); |
| | | assertEquals("1", changes.get(0).getRequestedValue()); |
| | | } |
| | | |
| | | @Test |
| | | void allowStationOutTaskLimitWithoutOutBufferCapacity() { |
| | | when(basStationService.getById(101)).thenReturn(station(101, 0)); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(true, command("station", "101", "outTaskLimit", "1"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertTrue(result.getSuccess()); |
| | | assertEquals("dry_run", changes.get(0).getResultStatus()); |
| | | assertEquals("1", changes.get(0).getRequestedValue()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | @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)); |
| | | when(basStationService.getById(101)).thenReturn(station(101, 1, 2)); |
| | | |
| | | 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(); |
| | |
| | | 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 |
| | |
| | | } |
| | | |
| | | @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); |
| | | |
| | | service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertEquals(1, updatedJob.getHasActiveTasks()); |
| | | |
| | | ArgumentCaptor<Wrapper<WrkMast>> captor = ArgumentCaptor.forClass(Wrapper.class); |
| | | verify(wrkMastService).count(captor.capture()); |
| | | String sqlSegment = captor.getValue().getSqlSegment(); |
| | | assertTrue(sqlSegment.contains("NOT IN")); |
| | | assertTrue(sqlSegment.contains("OR")); |
| | | assertTrue(sqlSegment.contains("wrk_sts IS NULL")); |
| | | } |
| | | |
| | | @Test |
| | | void applyJobRecordsNoActiveTasksWhenCountIsZero() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | when(wrkMastService.count(any(Wrapper.class))).thenReturn(0L); |
| | | |
| | | service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertEquals(0, updatedJob.getHasActiveTasks()); |
| | | } |
| | | |
| | | @Test |
| | | void applyJobFallsBackToNoActiveTasksWhenCountFails() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | when(wrkMastService.count(any(Wrapper.class))).thenThrow(new IllegalStateException("count failed")); |
| | | |
| | | service.apply(request(true, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertEquals(0, updatedJob.getHasActiveTasks()); |
| | | } |
| | | |
| | | @Test |
| | | void whitespaceTargetFieldsAreNormalizedBeforeValidationWriteAndAudit() { |
| | | when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 9)); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(false, command(" crn ", " 1 ", " maxOutTask ", "2"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertTrue(result.getSuccess()); |
| | | assertEquals("crn", changes.get(0).getTargetType()); |
| | | assertEquals("1", changes.get(0).getTargetId()); |
| | | assertEquals("maxOutTask", changes.get(0).getTargetKey()); |
| | | assertEquals("success", changes.get(0).getResultStatus()); |
| | | assertEquals(1, transactionManager.getCommitCount()); |
| | | verify(basCrnpService).update(any(Wrapper.class)); |
| | | } |
| | | |
| | | @Test |
| | | void whitespaceTargetFieldsUseNormalizedCooldownScope() { |
| | | when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 9)); |
| | | 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)); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(true, command(" crn ", " 1 ", " maxOutTask ", "2"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("crn", changes.get(0).getTargetType()); |
| | | assertEquals("1", changes.get(0).getTargetId()); |
| | | assertEquals("maxOutTask", changes.get(0).getTargetKey()); |
| | | 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(basStationService.getById(101)).thenReturn(station(101, 1, 2)); |
| | | when(basCrnpService.getById(1)).thenReturn(crn(1, 1, 1)); |
| | | when(basDualCrnpService.getById(2)).thenReturn(dualCrn(2, 1, 1)); |
| | | |
| | |
| | | assertEquals(4, result.getSuccessCount()); |
| | | assertEquals(0, result.getRejectCount()); |
| | | assertTrue(changes.stream().allMatch(change -> "success".equals(change.getResultStatus()))); |
| | | assertEquals(1, transactionManager.getCommitCount()); |
| | | 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 applyCacheRefreshFailureDoesNotAppendFailedAuditOrJob() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | doThrow(new IllegalStateException("cache refresh failed")) |
| | | .when(configService).refreshSystemConfigCache(); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(false, |
| | | command("sys_config", null, "conveyorStationTaskLimit", "15") |
| | | )); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertTrue(result.getSuccess()); |
| | | assertEquals("success", updatedJob.getStatus()); |
| | | assertEquals(1, changes.size()); |
| | | assertEquals("success", changes.get(0).getResultStatus()); |
| | | verify(aiAutoTuneChangeService, times(1)).saveBatch(any(Collection.class)); |
| | | verify(aiAutoTuneJobService, times(1)).updateById(any(AiAutoTuneJob.class)); |
| | | verify(configService).refreshSystemConfigCache(); |
| | | } |
| | | |
| | | @Test |
| | |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("db write failed")); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void auditSaveBatchFailureRollsBackTargetWriteAndReturnsFailedAudit() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | when(aiAutoTuneChangeService.saveBatch(any(Collection.class))) |
| | | .thenThrow(new IllegalStateException("audit failed")) |
| | | .thenReturn(true); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(false, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("audit failed")); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "15"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void jobUpdateFailureRollsBackTargetWriteTransaction() { |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobUpdateCall(); |
| | | return false; |
| | | }); |
| | | |
| | | IllegalStateException exception = assertThrows(IllegalStateException.class, |
| | | () -> service.apply(request(false, command("sys_config", null, "conveyorStationTaskLimit", "15")))); |
| | | |
| | | assertTrue(exception.getMessage().contains("更新调参任务状态失败")); |
| | | assertEquals(2, transactionManager.getRollbackCount()); |
| | | assertEquals(0, transactionManager.getCommitCount()); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "15"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void jobUpdateFailureRecordsFailureJobWhenRecoveryUpdateSucceeds() { |
| | | AtomicInteger updateAttempts = new AtomicInteger(); |
| | | when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "10")); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobUpdateCall(); |
| | | return updateAttempts.incrementAndGet() > 1; |
| | | }); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(false, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals(1, changes.size()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("更新调参任务状态失败")); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | assertTrue(transactionManager.getCommitCount() >= 1); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "15"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(aiAutoTuneChangeService, times(2)).saveBatch(any(Collection.class)); |
| | | verify(aiAutoTuneJobService, times(2)).updateById(any(AiAutoTuneJob.class)); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void realApplyLockNotAcquiredRejectsWithoutTargetWrite() { |
| | | when(redisUtil.trySetStringIfAbsent(anyString(), anyString(), anyLong())).thenReturn(false); |
| | | |
| | | AutoTuneApplyResult result = service.apply(request(false, command("sys_config", null, "conveyorStationTaskLimit", "15"))); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("锁不可用")); |
| | | assertTrue(changes.get(0).getRejectReason().contains("Redis")); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService, never()).getOne(any(Wrapper.class)); |
| | | verify(configService, never()).saveConfigValue(any(), any()); |
| | | verify(redisUtil).hasKey(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key); |
| | | verify(redisUtil, never()).compareAndDelete(anyString(), anyString()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "10"); |
| | | verify(configService).refreshSystemConfigCache(); |
| | | verify(basStationService).update(any(Wrapper.class)); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | InOrder rollbackOrder = inOrder(redisUtil, aiAutoTuneChangeService); |
| | | rollbackOrder.verify(redisUtil).trySetStringIfAbsent(anyString(), anyString(), anyLong()); |
| | | rollbackOrder.verify(aiAutoTuneChangeService, atLeastOnce()).list(any(Wrapper.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackJobRecordsActiveTasksWhenCountIsPositive() { |
| | | AiAutoTuneJob latestRealJob = job(10L, "manual", "success"); |
| | | AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15"); |
| | | when(wrkMastService.count(any(Wrapper.class))).thenReturn(1L); |
| | | 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")); |
| | | |
| | | service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertEquals(1, updatedJob.getHasActiveTasks()); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackCacheRefreshFailureDoesNotAppendFailedAuditOrJob() { |
| | | 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")); |
| | | doThrow(new IllegalStateException("cache refresh failed")) |
| | | .when(configService).refreshSystemConfigCache(); |
| | | |
| | | AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertTrue(result.getSuccess()); |
| | | assertEquals("success", updatedJob.getStatus()); |
| | | assertEquals(1, changes.size()); |
| | | assertEquals("success", changes.get(0).getResultStatus()); |
| | | verify(aiAutoTuneChangeService, times(1)).saveBatch(any(Collection.class)); |
| | | verify(aiAutoTuneJobService, times(1)).updateById(any(AiAutoTuneJob.class)); |
| | | verify(configService).refreshSystemConfigCache(); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackAuditSaveBatchFailureRollsBackTargetWriteAndReturnsFailedAudit() { |
| | | 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")); |
| | | when(aiAutoTuneChangeService.saveBatch(any(Collection.class))) |
| | | .thenThrow(new IllegalStateException("rollback audit failed")) |
| | | .thenReturn(true); |
| | | |
| | | AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("rollback audit failed")); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "10"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackJobUpdateFailureRollsBackAndDoesNotSaveRunningJobOutsideTransaction() { |
| | | 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")); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobUpdateCall(); |
| | | return false; |
| | | }); |
| | | |
| | | IllegalStateException exception = assertThrows(IllegalStateException.class, |
| | | () -> service.rollbackLastSuccessfulJob("manual rollback")); |
| | | |
| | | assertTrue(exception.getMessage().contains("更新调参任务状态失败")); |
| | | assertEquals(2, transactionManager.getRollbackCount()); |
| | | assertEquals(0, transactionManager.getCommitCount()); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "10"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackJobUpdateFailureRecordsFailureJobWhenRecoveryUpdateSucceeds() { |
| | | AtomicInteger updateAttempts = new AtomicInteger(); |
| | | 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")); |
| | | when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> { |
| | | transactionManager.recordJobUpdateCall(); |
| | | return updateAttempts.incrementAndGet() > 1; |
| | | }); |
| | | |
| | | AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals(1, changes.size()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("更新调参任务状态失败")); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | assertTrue(transactionManager.getCommitCount() >= 1); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(configService).saveConfigValue("conveyorStationTaskLimit", "10"); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(aiAutoTuneChangeService, times(2)).saveBatch(any(Collection.class)); |
| | | verify(aiAutoTuneJobService, times(2)).updateById(any(AiAutoTuneJob.class)); |
| | | verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void rollbackLockNotAcquiredReturnsFailedAuditWithoutTargetWrite() { |
| | | when(redisUtil.trySetStringIfAbsent(anyString(), anyString(), anyLong())).thenReturn(false); |
| | | |
| | | AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertTrue(changes.get(0).getRejectReason().contains("锁不可用")); |
| | | assertTrue(changes.get(0).getRejectReason().contains("Redis")); |
| | | assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0); |
| | | assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0); |
| | | assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount()); |
| | | assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount()); |
| | | verify(aiAutoTuneChangeService, never()).list(any(Wrapper.class)); |
| | | verify(aiAutoTuneJobService, never()).getById(any()); |
| | | verify(configService, never()).saveConfigValue(any(), any()); |
| | | verify(configService, never()).refreshSystemConfigCache(); |
| | | verify(redisUtil).hasKey(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key); |
| | | verify(redisUtil, never()).compareAndDelete(anyString(), anyString()); |
| | | } |
| | | |
| | | @Test |
| | | void fullyFailedRollbackReturnsFailedStatus() { |
| | | 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")); |
| | | when(configService.saveConfigValue("conveyorStationTaskLimit", "10")) |
| | | .thenThrow(new IllegalStateException("rollback write failed")); |
| | | |
| | | AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback"); |
| | | |
| | | List<AiAutoTuneChange> changes = savedChanges(); |
| | | AiAutoTuneJob updatedJob = updatedJob(); |
| | | assertFalse(result.getSuccess()); |
| | | assertEquals("failed", updatedJob.getStatus()); |
| | | assertEquals("failed", changes.get(0).getResultStatus()); |
| | | assertEquals(1, transactionManager.getRollbackCount()); |
| | | } |
| | | |
| | | private AutoTuneApplyRequest request(boolean dryRun, AutoTuneChangeCommand... commands) { |
| | |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | |
| | |
| | | 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, |
| | |
| | | |
| | | private List<AiAutoTuneChange> savedChanges() { |
| | | ArgumentCaptor<Collection<AiAutoTuneChange>> captor = ArgumentCaptor.forClass(Collection.class); |
| | | verify(aiAutoTuneChangeService).saveBatch(captor.capture()); |
| | | return new ArrayList<>(captor.getValue()); |
| | | verify(aiAutoTuneChangeService, atLeastOnce()).saveBatch(captor.capture()); |
| | | List<Collection<AiAutoTuneChange>> allValues = captor.getAllValues(); |
| | | return new ArrayList<>(allValues.get(allValues.size() - 1)); |
| | | } |
| | | |
| | | private AiAutoTuneJob updatedJob() { |
| | | ArgumentCaptor<AiAutoTuneJob> captor = ArgumentCaptor.forClass(AiAutoTuneJob.class); |
| | | verify(aiAutoTuneJobService, atLeastOnce()).updateById(captor.capture()); |
| | | List<AiAutoTuneJob> allValues = captor.getAllValues(); |
| | | return allValues.get(allValues.size() - 1); |
| | | } |
| | | |
| | | private static class RecordingTransactionManager implements PlatformTransactionManager { |
| | | private int beginCount; |
| | | private int commitCount; |
| | | private int rollbackCount; |
| | | private boolean transactionActive; |
| | | private int jobSaveInsideTransactionCount; |
| | | private int jobSaveOutsideTransactionCount; |
| | | private int jobUpdateInsideTransactionCount; |
| | | private int jobUpdateOutsideTransactionCount; |
| | | |
| | | @Override |
| | | public TransactionStatus getTransaction(TransactionDefinition definition) { |
| | | beginCount++; |
| | | transactionActive = true; |
| | | return new SimpleTransactionStatus(); |
| | | } |
| | | |
| | | @Override |
| | | public void commit(TransactionStatus status) { |
| | | commitCount++; |
| | | transactionActive = false; |
| | | } |
| | | |
| | | @Override |
| | | public void rollback(TransactionStatus status) { |
| | | rollbackCount++; |
| | | transactionActive = false; |
| | | } |
| | | |
| | | public void recordJobSaveCall() { |
| | | if (transactionActive) { |
| | | jobSaveInsideTransactionCount++; |
| | | } else { |
| | | jobSaveOutsideTransactionCount++; |
| | | } |
| | | } |
| | | |
| | | public void recordJobUpdateCall() { |
| | | if (transactionActive) { |
| | | jobUpdateInsideTransactionCount++; |
| | | } else { |
| | | jobUpdateOutsideTransactionCount++; |
| | | } |
| | | } |
| | | |
| | | public int getCommitCount() { |
| | | return commitCount; |
| | | } |
| | | |
| | | public int getRollbackCount() { |
| | | return rollbackCount; |
| | | } |
| | | |
| | | public int getJobSaveInsideTransactionCount() { |
| | | return jobSaveInsideTransactionCount; |
| | | } |
| | | |
| | | public int getJobSaveOutsideTransactionCount() { |
| | | return jobSaveOutsideTransactionCount; |
| | | } |
| | | |
| | | public int getJobUpdateInsideTransactionCount() { |
| | | return jobUpdateInsideTransactionCount; |
| | | } |
| | | |
| | | public int getJobUpdateOutsideTransactionCount() { |
| | | return jobUpdateOutsideTransactionCount; |
| | | } |
| | | } |
| | | } |