diff --git a/libs/core/tests/unit_tests/load/test_serializable.py b/libs/core/tests/unit_tests/load/test_serializable.py index 59bb6d54697..e782709fd41 100644 --- a/libs/core/tests/unit_tests/load/test_serializable.py +++ b/libs/core/tests/unit_tests/load/test_serializable.py @@ -1,6 +1,9 @@ +import json + +import pytest from pydantic import BaseModel, ConfigDict, Field -from langchain_core.load import Serializable, dumpd, load +from langchain_core.load import Serializable, dumpd, dumps, load from langchain_core.load.serializable import _is_field_useful from langchain_core.messages import AIMessage from langchain_core.outputs import ChatGeneration, Generation @@ -276,3 +279,92 @@ def test_serialization_with_ignore_unserializable_fields() -> None: ] ] } + + +# Tests for dumps() function +def test_dumps_basic_serialization() -> None: + """Test basic string serialization with `dumps()`.""" + foo = Foo(bar=42, baz="test") + json_str = dumps(foo) + + # Should be valid JSON + parsed = json.loads(json_str) + assert parsed == { + "id": ["tests", "unit_tests", "load", "test_serializable", "Foo"], + "kwargs": {"bar": 42, "baz": "test"}, + "lc": 1, + "type": "constructor", + } + + +def test_dumps_pretty_formatting() -> None: + """Test pretty printing functionality.""" + foo = Foo(bar=1, baz="hello") + + # Test pretty=True with default indent + pretty_json = dumps(foo, pretty=True) + assert " " in pretty_json + + # Test custom indent (4-space) + custom_indent = dumps(foo, pretty=True, indent=4) + assert " " in custom_indent + + # Verify it's still valid JSON + parsed = json.loads(pretty_json) + assert parsed["kwargs"]["bar"] == 1 + + +def test_dumps_invalid_default_kwarg() -> None: + """Test that passing `'default'` as kwarg raises ValueError.""" + foo = Foo(bar=1, baz="test") + + with pytest.raises(ValueError, match="`default` should not be passed to dumps"): + dumps(foo, default=lambda x: x) + + +def test_dumps_additional_json_kwargs() -> None: + """Test that additional JSON kwargs are passed through.""" + foo = Foo(bar=1, baz="test") + + compact_json = dumps(foo, separators=(",", ":")) + assert ", " not in compact_json # Should be compact + + # Test sort_keys + sorted_json = dumps(foo, sort_keys=True) + parsed = json.loads(sorted_json) + assert parsed == dumpd(foo) + + +def test_dumps_non_serializable_object() -> None: + """Test `dumps()` behavior with non-serializable objects.""" + + class NonSerializable: + def __init__(self, value: int) -> None: + self.value = value + + obj = NonSerializable(42) + json_str = dumps(obj) + + # Should create a "not_implemented" representation + parsed = json.loads(json_str) + assert parsed["lc"] == 1 + assert parsed["type"] == "not_implemented" + assert "NonSerializable" in parsed["repr"] + + +def test_dumps_mixed_data_structure() -> None: + """Test `dumps()` with complex nested data structures.""" + data = { + "serializable": Foo(bar=1, baz="test"), + "list": [1, 2, {"nested": "value"}], + "primitive": "string", + } + + json_str = dumps(data) + parsed = json.loads(json_str) + + # Serializable object should be properly serialized + assert parsed["serializable"]["type"] == "constructor" + # Primitives should remain unchanged + assert parsed["list"] == [1, 2, {"nested": "value"}] + assert parsed["primitive"] == "string"