package com.zy.ai.utils; import com.zy.ai.entity.AiAutoTuneChange; import com.zy.ai.entity.AiAutoTuneJob; import com.zy.ai.entity.AiAutoTuneMcpCall; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; class AutoTuneWriteBehaviorUtilsTest { @Test void resolveMcpCallDistinguishesDryRunAndAnalysisOnlyApply() { AiAutoTuneMcpCall dryRunCall = new AiAutoTuneMcpCall(); dryRunCall.setToolName("wcs_local_dispatch_apply_auto_tune_changes"); dryRunCall.setDryRun(1); AiAutoTuneMcpCall analysisOnlyCall = new AiAutoTuneMcpCall(); analysisOnlyCall.setToolName("wcs_local_dispatch_apply_auto_tune_changes"); analysisOnlyCall.setDryRun(0); analysisOnlyCall.setStatus("success"); analysisOnlyCall.setResponseJson("{\"analysisOnly\":true,\"noApply\":true,\"summary\":\"仅分析模式禁止实际应用\"}"); assertEquals("dry_run", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(dryRunCall)); assertEquals("analysis_only", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(analysisOnlyCall)); } @Test void resolveChangeDistinguishesNoChangeAndRejectedAnalysisOnly() { AiAutoTuneChange noChange = new AiAutoTuneChange(); noChange.setResultStatus("no_change"); AiAutoTuneChange analysisOnly = new AiAutoTuneChange(); analysisOnly.setResultStatus("rejected"); analysisOnly.setRejectReason("仅分析模式禁止实际应用/回滚,未修改运行参数"); assertEquals("no_change", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(noChange)); assertEquals("analysis_only", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(analysisOnly)); } @Test void resolveMcpCallClassifiesRollbackOnlyWhenSuccessfulChangeExists() { AiAutoTuneMcpCall countSuccessCall = rollbackCall("success"); countSuccessCall.setSuccessCount(1); AiAutoTuneMcpCall responseSuccessCall = rollbackCall("failed"); responseSuccessCall.setResponseJson("{\"changes\":[{\"resultStatus\":\"success\"}]}"); assertEquals("rollback", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(countSuccessCall)); assertEquals("rollback", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(responseSuccessCall)); } @Test void resolveMcpCallClassifiesRejectedAndFailedRollbackWithoutSuccessfulChange() { AiAutoTuneMcpCall rejectedCall = rollbackCall("rejected"); rejectedCall.setResponseJson("{\"success\":false,\"rejectCount\":1}"); AiAutoTuneMcpCall failedCall = rollbackCall("failed"); failedCall.setErrorMessage("rollback write failed"); assertEquals("rejected", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(rejectedCall)); assertEquals("failed", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(failedCall)); } @Test void resolveMcpCallPrefersAnalysisOnlyAndNoChangeForRollback() { AiAutoTuneMcpCall analysisOnlyCall = rollbackCall("rejected"); analysisOnlyCall.setResponseJson("{\"analysisOnly\":true,\"noApply\":true}"); AiAutoTuneMcpCall noChangeCall = rollbackCall("success"); noChangeCall.setResponseJson("{\"success\":true,\"successCount\":0,\"rejectCount\":0}"); assertEquals("analysis_only", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(analysisOnlyCall)); assertEquals("no_change", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(noChangeCall)); } @Test void resolveMcpCallDoesNotTreatAnalysisOnlyFalseTextAsAnalysisOnly() { AiAutoTuneMcpCall applyCall = new AiAutoTuneMcpCall(); applyCall.setToolName("wcs_local_dispatch_apply_auto_tune_changes"); applyCall.setDryRun(0); applyCall.setStatus("success"); applyCall.setSuccessCount(1); applyCall.setResponseJson("{\"success\":true,\"successCount\":1,\"summary\":\"analysisOnly=false, allowApply=true\"}"); assertEquals("apply", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(applyCall)); } @Test void resolveMcpCallUsesResponseChangeDetailsBeforeSuccessCountForApply() { AiAutoTuneMcpCall noChangeCall = applyCall(0, "success"); noChangeCall.setSuccessCount(1); noChangeCall.setResponseJson("{\"success\":true,\"successCount\":1," + "\"changes\":[{\"resultStatus\":\"no_change\"},{\"resultStatus\":\"no_change\"}]}"); AiAutoTuneMcpCall mixedSuccessCall = applyCall(0, "success"); mixedSuccessCall.setSuccessCount(0); mixedSuccessCall.setResponseJson("{\"success\":true,\"successCount\":0," + "\"changes\":[{\"resultStatus\":\"no_change\"},{\"resultStatus\":\"success\"}]}"); assertEquals("no_change", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(noChangeCall)); assertEquals("apply", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(mixedSuccessCall)); } @Test void resolveMcpCallPrefersFailedAndRejectedOverDryRunAndNoChange() { AiAutoTuneMcpCall failedDryRunCall = applyCall(1, "failed"); AiAutoTuneMcpCall rejectedNoChangeCall = applyCall(0, "rejected"); rejectedNoChangeCall.setResponseJson("{\"success\":true,\"successCount\":0,\"rejectCount\":0}"); assertEquals("failed", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(failedDryRunCall)); assertEquals("rejected", AutoTuneWriteBehaviorUtils.resolveMcpWriteBehavior(rejectedNoChangeCall)); } @Test void resolveChangeUsesOwnerTriggerTypeForRollbackSuccess() { AiAutoTuneChange successChange = new AiAutoTuneChange(); successChange.setResultStatus("success"); assertEquals("apply", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(successChange)); assertEquals("rollback", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(successChange, "rollback")); assertEquals("apply", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(successChange, "manual")); } @Test void resolveChangeClassifiesRejectedAndFailed() { AiAutoTuneChange rejectedChange = new AiAutoTuneChange(); rejectedChange.setResultStatus("rejected"); rejectedChange.setRejectReason("超出允许范围"); AiAutoTuneChange failedChange = new AiAutoTuneChange(); failedChange.setResultStatus("failed"); assertEquals("rejected", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(rejectedChange)); assertEquals("failed", AutoTuneWriteBehaviorUtils.resolveChangeWriteBehavior(failedChange)); } @Test void writeBehaviorLabelIncludesRejectedAndFailed() { assertEquals("已拒绝", AutoTuneWriteBehaviorUtils.writeBehaviorLabel("rejected")); assertEquals("失败", AutoTuneWriteBehaviorUtils.writeBehaviorLabel("failed")); } @Test void resolveJobPrefersReadOnlyWhenOnlySnapshotToolsWereCalled() { AiAutoTuneJob job = new AiAutoTuneJob(); job.setStatus("success"); Map snapshotCall = new LinkedHashMap<>(); snapshotCall.put("writeBehavior", "read_only"); String writeBehavior = AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( job, Collections.singletonList(snapshotCall), List.of() ); assertEquals("read_only", writeBehavior); } @Test void resolveJobPrefersFailedAndRejectedOverDryRunNoChangeAndReadOnly() { AiAutoTuneJob failedDryRunJob = new AiAutoTuneJob(); failedDryRunJob.setStatus("failed"); Map dryRunCall = new LinkedHashMap<>(); dryRunCall.put("writeBehavior", "dry_run"); AiAutoTuneJob rejectedNoChangeJob = new AiAutoTuneJob(); rejectedNoChangeJob.setStatus("rejected"); Map noChange = new LinkedHashMap<>(); noChange.put("writeBehavior", "no_change"); AiAutoTuneJob failedReadOnlyJob = new AiAutoTuneJob(); failedReadOnlyJob.setStatus("failed"); Map readOnlyCall = new LinkedHashMap<>(); readOnlyCall.put("writeBehavior", "read_only"); assertEquals("failed", AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( failedDryRunJob, List.of(dryRunCall), List.of() )); assertEquals("rejected", AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( rejectedNoChangeJob, List.of(), List.of(noChange) )); assertEquals("failed", AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( failedReadOnlyJob, List.of(readOnlyCall), List.of() )); } @Test void resolveJobClassifiesDirectSuccessfulRollbackWithoutMcpCalls() { AiAutoTuneJob job = new AiAutoTuneJob(); job.setTriggerType("rollback"); job.setStatus("success"); job.setSuccessCount(1); String writeBehavior = AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( job, List.of(), List.of() ); assertEquals("rollback", writeBehavior); } @Test void resolveJobPreservesRollbackWhenSomeRollbackChangesFailed() { AiAutoTuneJob job = new AiAutoTuneJob(); job.setTriggerType("rollback"); job.setStatus("partial_success"); Map successChange = new LinkedHashMap<>(); successChange.put("writeBehavior", "rollback"); successChange.put("resultStatus", "success"); Map failedChange = new LinkedHashMap<>(); failedChange.put("writeBehavior", "failed"); failedChange.put("resultStatus", "failed"); String writeBehavior = AutoTuneWriteBehaviorUtils.resolveJobWriteBehavior( job, List.of(), List.of(successChange, failedChange) ); assertEquals("rollback", writeBehavior); } private AiAutoTuneMcpCall applyCall(Integer dryRun, String status) { AiAutoTuneMcpCall mcpCall = new AiAutoTuneMcpCall(); mcpCall.setToolName("wcs_local_dispatch_apply_auto_tune_changes"); mcpCall.setDryRun(dryRun); mcpCall.setStatus(status); return mcpCall; } private AiAutoTuneMcpCall rollbackCall(String status) { AiAutoTuneMcpCall mcpCall = new AiAutoTuneMcpCall(); mcpCall.setToolName("wcs_local_dispatch_revert_last_auto_tune_job"); mcpCall.setStatus(status); mcpCall.setSuccessCount(0); return mcpCall; } }