This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch unomi-3-dev
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/unomi-3-dev by this push:
new 793015042 UNOMI-920 Add YAML serialization support for Condition and
ConditionType classes
793015042 is described below
commit 793015042d2d928bc4c4d5f4b16262061d00ee31
Author: Serge Huber <[email protected]>
AuthorDate: Tue Dec 9 10:43:37 2025 +0100
UNOMI-920 Add YAML serialization support for Condition and ConditionType
classes
- Implemented toYaml methods in Condition and ConditionType classes to
convert instances to a Map structure for YAML output, enhancing serialization
capabilities.
- Updated toString methods in both classes to utilize the new YAML
formatting.
- Introduced YamlUtils utility class for building YAML structures and
handling circular references.
- Added comprehensive unit tests for YamlUtils to ensure functionality and
reliability.
---
.../org/apache/unomi/api/conditions/Condition.java | 49 +++-
.../apache/unomi/api/conditions/ConditionType.java | 46 +++-
.../unomi/api/conditions/ConditionValidation.java | 34 ++-
.../java/org/apache/unomi/api/utils/YamlUtils.java | 243 ++++++++++++++++++++
.../org/apache/unomi/api/utils/YamlUtilsTest.java | 255 +++++++++++++++++++++
5 files changed, 604 insertions(+), 23 deletions(-)
diff --git a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
index 12cffe0f7..d41890bff 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
@@ -17,11 +17,19 @@
package org.apache.unomi.api.conditions;
+import org.apache.unomi.api.utils.YamlUtils;
+import org.apache.unomi.api.utils.YamlUtils.YamlMapBuilder;
+
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import java.io.Serializable;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+
+import static org.apache.unomi.api.utils.YamlUtils.circularRef;
/**
* A set of elements that can be evaluated.
@@ -158,13 +166,42 @@ public class Condition implements Serializable {
return result;
}
+ /**
+ * Converts this condition to a Map structure for YAML output.
+ *
+ * @param visited set of already visited conditions to prevent infinite
recursion
+ * @return a Map representation of this condition
+ */
+ public Map<String, Object> toYaml(Set<Condition> visited) {
+ if (visited.contains(this)) {
+ return circularRef();
+ }
+ visited.add(this);
+ try {
+ YamlMapBuilder builder = YamlMapBuilder.create()
+ .put("type", conditionTypeId != null ? conditionTypeId :
"Condition");
+ if (parameterValues != null && !parameterValues.isEmpty()) {
+ parameterValues.forEach((name, value) ->
+ builder.put(name, toYamlValue(value, visited)));
+ }
+ return builder.build();
+ } finally {
+ visited.remove(this);
+ }
+ }
+
+ private Object toYamlValue(Object value, Set<Condition> visited) {
+ if (value instanceof Condition) {
+ return ((Condition) value).toYaml(visited);
+ }
+ // For non-Condition values, use empty visited set since
YamlUtils.toYamlValue
+ // doesn't currently use it for circular reference detection
+ return YamlUtils.toYamlValue(value, Collections.emptySet());
+ }
+
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder("Condition{");
- sb.append("conditionType=").append(conditionType);
- sb.append(", conditionTypeId='").append(conditionTypeId).append('\'');
- sb.append(", parameterValues=").append(parameterValues);
- sb.append('}');
- return sb.toString();
+ Map<String, Object> map = toYaml(new HashSet<>());
+ return YamlUtils.format(map);
}
}
diff --git
a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
index 9d649ae0b..621e3aab8 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
@@ -21,10 +21,17 @@ import org.apache.unomi.api.Metadata;
import org.apache.unomi.api.MetadataItem;
import org.apache.unomi.api.Parameter;
import org.apache.unomi.api.PluginType;
+import org.apache.unomi.api.utils.YamlUtils;
import javax.xml.bind.annotation.XmlElement;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.apache.unomi.api.utils.YamlUtils.*;
/**
* ConditionTypes define new conditions that can be applied to items (for
example to decide whether a rule needs to be triggered or if a profile is
considered as taking part in a
@@ -155,14 +162,39 @@ public class ConditionType extends MetadataItem
implements PluginType {
this.pluginId = pluginId;
}
+ /**
+ * Converts this condition type to a Map structure for YAML output.
+ *
+ * @param visited set of already visited condition types to prevent
infinite recursion
+ * @return a Map representation of this condition type
+ */
+ public Map<String, Object> toYaml(Set<ConditionType> visited) {
+ if (visited.contains(this)) {
+ return circularRef();
+ }
+ visited.add(this);
+ try {
+ return YamlMapBuilder.create()
+ .putIfNotNull("id", itemId)
+ .putIfNotNull("conditionEvaluator", conditionEvaluator)
+ .putIfNotNull("queryBuilder", queryBuilder)
+ .putIfNotNull("parentCondition", parentCondition != null ?
parentCondition.toYaml(new HashSet<>()) : null)
+ .putIfNotEmpty("parameters", parameters != null ?
parameters.stream()
+ .map(Parameter::toYaml)
+ .collect(Collectors.toList()) : null)
+ .put("pluginId", pluginId)
+ .putIfNotNull("name", metadata != null ? metadata.getName() :
null)
+ .putIfNotNull("description", metadata != null ?
metadata.getDescription() : null)
+ .putIfNotNull("scope", metadata != null ? metadata.getScope()
: null)
+ .build();
+ } finally {
+ visited.remove(this);
+ }
+ }
+
@Override
public String toString() {
- return "ConditionType{" +
- "conditionEvaluator='" + conditionEvaluator + '\'' +
- ", queryBuilder='" + queryBuilder + '\'' +
- ", parentCondition=" + parentCondition +
- ", parameters=" + parameters +
- ", pluginId=" + pluginId +
- '}';
+ Map<String, Object> map = toYaml(new HashSet<>());
+ return YamlUtils.format(map);
}
}
diff --git
a/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
b/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
index 2a0c0956c..10aa0cd21 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
@@ -16,9 +16,14 @@
*/
package org.apache.unomi.api.conditions;
+import org.apache.unomi.api.utils.YamlUtils;
+
import java.io.Serializable;
+import java.util.Map;
import java.util.Set;
+import static org.apache.unomi.api.utils.YamlUtils.*;
+
/**
* Validation metadata for condition parameters
*/
@@ -113,17 +118,26 @@ public class ConditionValidation implements Serializable {
this.customType = customType;
}
+ /**
+ * Converts this validation to a Map structure for YAML output.
+ *
+ * @return a Map representation of this validation
+ */
+ public Map<String, Object> toYaml() {
+ return YamlUtils.YamlMapBuilder.create()
+ .putIf("required", true, required)
+ .putIf("recommended", true, recommended)
+ .putIfNotNull("allowedValues", setToSortedList(allowedValues))
+ .putIfNotNull("allowedConditionTags",
setToSortedList(allowedConditionTags))
+ .putIfNotNull("disallowedConditionTypes",
setToSortedList(disallowedConditionTypes))
+ .putIf("exclusive", true, exclusive)
+ .putIfNotNull("exclusiveGroup", exclusiveGroup)
+ .putIfNotNull("customType", customType != null ?
customType.getName() : null)
+ .build();
+ }
+
@Override
public String toString() {
- return "ConditionValidation{" +
- "required=" + required +
- ", allowedValues=" + allowedValues +
- ", allowedConditionTags=" + allowedConditionTags +
- ", disallowedConditionTypes=" + disallowedConditionTypes +
- ", exclusive=" + exclusive +
- ", exclusiveGroup='" + exclusiveGroup + '\'' +
- ", recommended=" + recommended +
- ", customType=" + customType +
- '}';
+ return YamlUtils.format(toYaml());
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java
b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java
new file mode 100644
index 000000000..4dd4cce85
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.unomi.api.utils;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * YAML utilities using SnakeYaml with fluent API wrapper.
+ * Provides utilities for building YAML structures and formatting them via
SnakeYaml.
+ */
+public class YamlUtils {
+ // SnakeYaml instance with configured options
+ private static final Yaml YAML_INSTANCE;
+
+ static {
+ DumperOptions options = new DumperOptions();
+ options.setIndent(2);
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ options.setPrettyFlow(true);
+ options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
+ YAML_INSTANCE = new Yaml(options);
+ }
+
+ /**
+ * Interface for objects that can convert themselves to YAML Map
structures.
+ */
+ public interface YamlConvertible {
+ /**
+ * Converts this object to a Map structure for YAML output.
+ *
+ * @return a Map representation of this object
+ */
+ Map<String, Object> toYaml();
+ }
+
+ /**
+ * Fluent builder for creating YAML Map structures.
+ * Provides chaining methods to avoid repeating the map variable.
+ */
+ public static class YamlMapBuilder {
+ private final Map<String, Object> map;
+
+ private YamlMapBuilder() {
+ this.map = new LinkedHashMap<>();
+ }
+
+ /**
+ * Creates a new builder instance.
+ *
+ * @return a new YamlMapBuilder
+ */
+ public static YamlMapBuilder create() {
+ return new YamlMapBuilder();
+ }
+
+ /**
+ * Adds a field if the value is not null.
+ *
+ * @param key the key (must not be null)
+ * @param value the value (only added if not null)
+ * @return this builder for chaining
+ * @throws NullPointerException if key is null
+ */
+ public YamlMapBuilder putIfNotNull(String key, Object value) {
+ if (key == null) {
+ throw new NullPointerException("Key must not be null");
+ }
+ if (value != null) {
+ map.put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * Adds a field if the condition is true.
+ *
+ * @param key the key (must not be null)
+ * @param value the value (only added if condition is true)
+ * @param condition the condition
+ * @return this builder for chaining
+ * @throws NullPointerException if key is null
+ */
+ public YamlMapBuilder putIf(String key, Object value, boolean
condition) {
+ if (key == null) {
+ throw new NullPointerException("Key must not be null");
+ }
+ if (condition) {
+ map.put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * Adds a field unconditionally.
+ *
+ * @param key the key (must not be null)
+ * @param value the value
+ * @return this builder for chaining
+ * @throws NullPointerException if key is null
+ */
+ public YamlMapBuilder put(String key, Object value) {
+ if (key == null) {
+ throw new NullPointerException("Key must not be null");
+ }
+ map.put(key, value);
+ return this;
+ }
+
+ /**
+ * Adds a field if the collection is not null and not empty.
+ *
+ * @param key the key (must not be null)
+ * @param collection the collection (only added if not null and not
empty)
+ * @return this builder for chaining
+ * @throws NullPointerException if key is null
+ */
+ public YamlMapBuilder putIfNotEmpty(String key,
java.util.Collection<?> collection) {
+ if (key == null) {
+ throw new NullPointerException("Key must not be null");
+ }
+ if (collection != null && !collection.isEmpty()) {
+ map.put(key, collection);
+ }
+ return this;
+ }
+
+ /**
+ * Builds and returns a defensive copy of the map.
+ *
+ * @return a new LinkedHashMap containing the built entries
+ */
+ public Map<String, Object> build() {
+ return new LinkedHashMap<>(map);
+ }
+ }
+
+ /**
+ * Converts a Set to a sorted List for YAML output.
+ *
+ * @param set the set to convert
+ * @return a sorted list, or null if the set is null or empty
+ */
+ public static <T extends Comparable<T>> List<T> setToSortedList(Set<T>
set) {
+ if (set == null || set.isEmpty()) {
+ return null;
+ }
+ return set.stream().sorted().collect(Collectors.toList());
+ }
+
+ /**
+ * Converts a Set to a sorted List using a mapper function.
+ *
+ * @param set the set to convert
+ * @param mapper the mapper function (must not be null)
+ * @return a sorted list, or null if the set is null or empty
+ * @throws NullPointerException if mapper is null
+ */
+ public static <T, R extends Comparable<R>> List<R> setToSortedList(Set<T>
set, Function<T, R> mapper) {
+ if (mapper == null) {
+ throw new NullPointerException("Mapper function must not be null");
+ }
+ if (set == null || set.isEmpty()) {
+ return null;
+ }
+ return set.stream().map(mapper).sorted().collect(Collectors.toList());
+ }
+
+ /**
+ * Converts a value to YAML-compatible format, handling nested structures.
+ * Note: This method does not perform circular reference detection for
generic objects.
+ * For objects that implement YamlConvertible, circular reference
detection should be
+ * handled in their toYaml() implementation.
+ *
+ * @param value the value to convert
+ * @param visited set of visited objects (currently unused, reserved for
future circular reference detection)
+ * @return the converted value
+ */
+ public static Object toYamlValue(Object value, Set<Object> visited) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof YamlConvertible) {
+ return ((YamlConvertible) value).toYaml();
+ }
+ if (value instanceof List) {
+ return ((List<?>) value).stream()
+ .map(item -> toYamlValue(item, visited))
+ .collect(Collectors.toList());
+ }
+ if (value instanceof Map) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ ((Map<?, ?>) value).forEach((key, val) ->
+ result.put(String.valueOf(key), toYamlValue(val, visited)));
+ return result;
+ }
+ return value;
+ }
+
+ /**
+ * Formats a value as YAML using SnakeYaml.
+ * This is a convenience method that delegates to SnakeYaml.
+ *
+ * @param value the value to format
+ * @return YAML string representation
+ */
+ public static String format(Object value) {
+ return YAML_INSTANCE.dump(value);
+ }
+
+ /**
+ * Creates a circular reference marker map.
+ *
+ * @return a map indicating a circular reference
+ */
+ public static Map<String, Object> circularRef() {
+ return YamlMapBuilder.create()
+ .put("$ref", "circular")
+ .build();
+ }
+}
diff --git a/api/src/test/java/org/apache/unomi/api/utils/YamlUtilsTest.java
b/api/src/test/java/org/apache/unomi/api/utils/YamlUtilsTest.java
new file mode 100644
index 000000000..95a00e523
--- /dev/null
+++ b/api/src/test/java/org/apache/unomi/api/utils/YamlUtilsTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.api.utils;
+
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for YamlUtils fluent API.
+ * Tests focus on our fluent API, not SnakeYaml's implementation.
+ */
+public class YamlUtilsTest {
+
+ @Test
+ public void testYamlMapBuilderCreate() {
+ YamlUtils.YamlMapBuilder builder = YamlUtils.YamlMapBuilder.create();
+ assertNotNull("Builder should be created", builder);
+ }
+
+ @Test
+ public void testYamlMapBuilderPut() {
+ Map<String, Object> map = YamlUtils.YamlMapBuilder.create()
+ .put("key1", "value1")
+ .put("key2", 42)
+ .build();
+ assertEquals("First value should be set", "value1", map.get("key1"));
+ assertEquals("Second value should be set", 42, map.get("key2"));
+ }
+
+ @Test
+ public void testYamlMapBuilderPutIfNotNull() {
+ Map<String, Object> map = YamlUtils.YamlMapBuilder.create()
+ .putIfNotNull("key1", "value1")
+ .putIfNotNull("key2", null)
+ .putIfNotNull("key3", "value3")
+ .build();
+ assertEquals("Non-null value should be set", "value1",
map.get("key1"));
+ assertFalse("Null value should not be set", map.containsKey("key2"));
+ assertEquals("Another non-null value should be set", "value3",
map.get("key3"));
+ }
+
+ @Test
+ public void testYamlMapBuilderPutIf() {
+ Map<String, Object> map = YamlUtils.YamlMapBuilder.create()
+ .putIf("key1", "value1", true)
+ .putIf("key2", "value2", false)
+ .putIf("key3", "value3", true)
+ .build();
+ assertEquals("Value with true condition should be set", "value1",
map.get("key1"));
+ assertFalse("Value with false condition should not be set",
map.containsKey("key2"));
+ assertEquals("Another value with true condition should be set",
"value3", map.get("key3"));
+ }
+
+ @Test
+ public void testYamlMapBuilderPutIfNotEmpty() {
+ Map<String, Object> map = YamlUtils.YamlMapBuilder.create()
+ .putIfNotEmpty("key1", Arrays.asList("a", "b"))
+ .putIfNotEmpty("key2", Collections.emptyList())
+ .putIfNotEmpty("key3", null)
+ .putIfNotEmpty("key4", Arrays.asList("c"))
+ .build();
+ assertTrue("Non-empty collection should be set",
map.containsKey("key1"));
+ assertFalse("Empty collection should not be set",
map.containsKey("key2"));
+ assertFalse("Null collection should not be set",
map.containsKey("key3"));
+ assertTrue("Another non-empty collection should be set",
map.containsKey("key4"));
+ }
+
+ @Test
+ public void testYamlMapBuilderChaining() {
+ Map<String, Object> map = YamlUtils.YamlMapBuilder.create()
+ .put("a", 1)
+ .putIfNotNull("b", "value")
+ .putIf("c", 3, true)
+ .putIfNotEmpty("d", Arrays.asList(1, 2))
+ .build();
+ assertEquals("All valid entries should be added", 4, map.size());
+ }
+
+ @Test
+ public void testYamlMapBuilderNullKeyThrowsException() {
+ try {
+ YamlUtils.YamlMapBuilder.create().put(null, "value");
+ fail("Null key should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testYamlMapBuilderNullKeyInPutIfNotNull() {
+ try {
+ YamlUtils.YamlMapBuilder.create().putIfNotNull(null, "value");
+ fail("Null key in putIfNotNull should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testYamlMapBuilderNullKeyInPutIf() {
+ try {
+ YamlUtils.YamlMapBuilder.create().putIf(null, "value", true);
+ fail("Null key in putIf should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testYamlMapBuilderNullKeyInPutIfNotEmpty() {
+ try {
+ YamlUtils.YamlMapBuilder.create().putIfNotEmpty(null,
Arrays.asList(1));
+ fail("Null key in putIfNotEmpty should throw
NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testYamlMapBuilderBuildReturnsNewMap() {
+ YamlUtils.YamlMapBuilder builder = YamlUtils.YamlMapBuilder.create();
+ builder.put("key", "value");
+ Map<String, Object> map1 = builder.build();
+ Map<String, Object> map2 = builder.build();
+ assertNotSame("Each build() should return a new map", map1, map2);
+ assertEquals("Both maps should have same content", map1, map2);
+ }
+
+ @Test
+ public void testSetToSortedList() {
+ Set<String> set = new LinkedHashSet<>(Arrays.asList("zebra", "apple",
"banana"));
+ List<String> result = YamlUtils.setToSortedList(set);
+ assertNotNull("Result should not be null", result);
+ assertEquals("Set should be converted to sorted list",
Arrays.asList("apple", "banana", "zebra"), result);
+ }
+
+ @Test
+ public void testSetToSortedListNull() {
+ List<String> result = YamlUtils.setToSortedList((Set<String>) null);
+ assertNull("Null set should return null", result);
+ }
+
+ @Test
+ public void testSetToSortedListEmpty() {
+ List<String> result =
YamlUtils.setToSortedList(Collections.<String>emptySet());
+ assertNull("Empty set should return null", result);
+ }
+
+ @Test
+ public void testSetToSortedListWithMapper() {
+ Set<Integer> set = new LinkedHashSet<>(Arrays.asList(3, 1, 2));
+ List<String> result = YamlUtils.setToSortedList(set, String::valueOf);
+ assertNotNull("Result should not be null", result);
+ assertEquals("Set should be converted to sorted list using mapper",
Arrays.asList("1", "2", "3"), result);
+ }
+
+ @Test
+ public void testSetToSortedListWithMapperNull() {
+ try {
+ YamlUtils.<Integer,
String>setToSortedList(Collections.singleton(1), null);
+ fail("Null mapper should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testSetToSortedListWithMapperNullSet() {
+ List<String> result = YamlUtils.setToSortedList(null, String::valueOf);
+ assertNull("Null set should return null even with mapper", result);
+ }
+
+ @Test
+ public void testToYamlValueWithYamlConvertible() {
+ YamlUtils.YamlConvertible convertible = () -> {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("test", "value");
+ return map;
+ };
+ Set<Object> visited = new HashSet<>();
+ Object result = YamlUtils.toYamlValue(convertible, visited);
+ assertTrue("YamlConvertible should be converted to Map", result
instanceof Map);
+ Map<?, ?> map = (Map<?, ?>) result;
+ assertEquals("Converted map should contain test value", "value",
map.get("test"));
+ }
+
+ @Test
+ public void testToYamlValueWithList() {
+ List<Object> list = Arrays.asList("a", "b", "c");
+ Set<Object> visited = new HashSet<>();
+ Object result = YamlUtils.toYamlValue(list, visited);
+ assertTrue("List should remain a List", result instanceof List);
+ assertEquals("List should be unchanged", list, result);
+ }
+
+ @Test
+ public void testToYamlValueWithMap() {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("key", "value");
+ Set<Object> visited = new HashSet<>();
+ Object result = YamlUtils.toYamlValue(map, visited);
+ assertTrue("Map should remain a Map", result instanceof Map);
+ assertEquals("Map should contain key-value", "value", ((Map<?, ?>)
result).get("key"));
+ }
+
+ @Test
+ public void testToYamlValueWithNull() {
+ Set<Object> visited = new HashSet<>();
+ Object result = YamlUtils.toYamlValue(null, visited);
+ assertNull("Null should return null", result);
+ }
+
+ @Test
+ public void testToYamlValueWithPrimitive() {
+ Set<Object> visited = new HashSet<>();
+ Object result = YamlUtils.toYamlValue(42, visited);
+ assertEquals("Primitive should remain unchanged", 42, result);
+ }
+
+ @Test
+ public void testCircularRef() {
+ Map<String, Object> result = YamlUtils.circularRef();
+ assertNotNull("circularRef should return a map", result);
+ assertEquals("Should contain $ref: circular", "circular",
result.get("$ref"));
+ assertEquals("Should have only one entry", 1, result.size());
+ }
+
+ @Test
+ public void testFormatBasic() {
+ // Just verify format() works - we don't test SnakeYaml's output format
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("key", "value");
+ String result = YamlUtils.format(map);
+ assertNotNull("Format should return a string", result);
+ assertTrue("Format should contain key", result.contains("key"));
+ assertTrue("Format should contain value", result.contains("value"));
+ }
+}