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"));
+    }
+}

Reply via email to