diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java b/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java index f9a32ad6..2fc24b53 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java +++ b/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java @@ -51,7 +51,7 @@ public class DirtyStateCalculator { if (!(desired instanceof ObjectNode desiredObj) || !(deployed instanceof ObjectNode deployedObj)) { if (!Objects.equals(desired, deployed)) { diffs.add(new DirtyStateResult.Difference(prefix, - String.valueOf(desired), String.valueOf(deployed))); + nodeToString(desired), nodeToString(deployed))); } return; } @@ -61,10 +61,18 @@ public class DirtyStateCalculator { for (String key : keys) { JsonNode d = desiredObj.get(key); JsonNode p = deployedObj.get(key); - if (!Objects.equals(d, p)) { - diffs.add(new DirtyStateResult.Difference(prefix + "." + key, - String.valueOf(d), String.valueOf(p))); + if (Objects.equals(d, p)) continue; + if (d instanceof ObjectNode && p instanceof ObjectNode) { + compareJson(prefix + "." + key, d, p, diffs); + } else { + diffs.add(new DirtyStateResult.Difference(prefix + "." + key, nodeToString(d), nodeToString(p))); } } } + + private static String nodeToString(JsonNode n) { + if (n == null) return "(none)"; + if (n.isValueNode()) return n.asText(); + return n.toString(); // arrays/objects: compact JSON + } } diff --git a/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java b/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java index ba9ec733..bb57be38 100644 --- a/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java +++ b/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java @@ -88,4 +88,57 @@ class DirtyStateCalculatorTest { assertThat(result.differences()).extracting(DirtyStateResult.Difference::field) .contains("containerConfig.memoryLimitMb"); } + + @Test + void nullAgentConfigInSnapshot_marksAgentConfigDiff() { + DirtyStateCalculator calc = new DirtyStateCalculator(); + ApplicationConfig desired = new ApplicationConfig(); + desired.setSamplingRate(1.0); + UUID jarId = UUID.randomUUID(); + DeploymentConfigSnapshot snap = new DeploymentConfigSnapshot(jarId, null, Map.of()); + + DirtyStateResult result = calc.compute(jarId, desired, Map.of(), snap); + + assertThat(result.dirty()).isTrue(); + assertThat(result.differences()).extracting(DirtyStateResult.Difference::field) + .contains("agentConfig"); + } + + @Test + void nestedAgentField_reportsDeepPath() { + DirtyStateCalculator calc = new DirtyStateCalculator(); + + ApplicationConfig deployed = new ApplicationConfig(); + deployed.setTracedProcessors(Map.of("proc-1", "DEBUG")); + ApplicationConfig desired = new ApplicationConfig(); + desired.setTracedProcessors(Map.of("proc-1", "TRACE")); + UUID jarId = UUID.randomUUID(); + DeploymentConfigSnapshot snap = new DeploymentConfigSnapshot(jarId, deployed, Map.of()); + + DirtyStateResult result = calc.compute(jarId, desired, Map.of(), snap); + + assertThat(result.dirty()).isTrue(); + assertThat(result.differences()).extracting(DirtyStateResult.Difference::field) + .contains("agentConfig.tracedProcessors.proc-1"); + } + + @Test + void stringField_differenceValueIsUnquoted() { + DirtyStateCalculator calc = new DirtyStateCalculator(); + + ApplicationConfig deployed = new ApplicationConfig(); + deployed.setApplicationLogLevel("INFO"); + ApplicationConfig desired = new ApplicationConfig(); + desired.setApplicationLogLevel("DEBUG"); + UUID jarId = UUID.randomUUID(); + DeploymentConfigSnapshot snap = new DeploymentConfigSnapshot(jarId, deployed, Map.of()); + + DirtyStateResult result = calc.compute(jarId, desired, Map.of(), snap); + + DirtyStateResult.Difference diff = result.differences().stream() + .filter(d -> d.field().equals("agentConfig.applicationLogLevel")) + .findFirst().orElseThrow(); + assertThat(diff.staged()).isEqualTo("DEBUG"); + assertThat(diff.deployed()).isEqualTo("INFO"); + } }