Skip to content

Commit 45542d5

Browse files
committed
test: add mismatch tests
1 parent cf73a16 commit 45542d5

File tree

1 file changed

+72
-14
lines changed

1 file changed

+72
-14
lines changed

python/cocoindex/tests/test_convert.py

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
import datetime
44
from dataclasses import dataclass
55
import pytest
6-
from cocoindex.convert import to_engine_value, make_engine_value_converter
76
from cocoindex.typing import encode_enriched_type
7+
from cocoindex.convert import to_engine_value
8+
from cocoindex.convert import make_engine_value_converter
89

910
@dataclass
1011
class Order:
@@ -33,13 +34,13 @@ class NestedStruct:
3334
orders: list[Order]
3435
count: int = 0
3536

36-
def build_converter(py_type, target_type=None):
37+
def build_engine_value_converter(engine_type_in_py, python_type=None):
3738
"""
38-
Helper to build a converter for the given Python type.
39-
If target_type is not specified, uses py_type as the target.
39+
Helper to build a converter for the given engine-side type (as represented in Python).
40+
If python_type is not specified, uses engine_type_in_py as the target.
4041
"""
41-
engine_type = encode_enriched_type(py_type)["type"]
42-
return make_engine_value_converter([], engine_type, target_type or py_type)
42+
engine_type = encode_enriched_type(engine_type_in_py)["type"]
43+
return make_engine_value_converter([], engine_type, python_type or engine_type_in_py)
4344

4445
def test_to_engine_value_basic_types():
4546
assert to_engine_value(123) == 123
@@ -90,18 +91,18 @@ def test_to_engine_value_none():
9091
assert to_engine_value(None) is None
9192

9293
def test_make_engine_value_converter_basic_types():
93-
for py_type, value in [
94+
for engine_type_in_py, value in [
9495
(int, 42),
9596
(float, 3.14),
9697
(str, "hello"),
9798
(bool, True),
9899
# (type(None), None), # Removed unsupported NoneType
99100
]:
100-
converter = build_converter(py_type)
101+
converter = build_engine_value_converter(engine_type_in_py)
101102
assert converter(value) == value
102103

103104
def test_make_engine_value_converter_struct():
104-
converter = build_converter(Order)
105+
converter = build_engine_value_converter(Order)
105106
# All fields match
106107
engine_val = ["O123", "mixed nuts", 25.0, "default_extra"]
107108
assert converter(engine_val) == Order("O123", "mixed nuts", 25.0, "default_extra")
@@ -121,25 +122,25 @@ def test_make_engine_value_converter_struct():
121122
def test_make_engine_value_converter_struct_field_order():
122123
# Engine fields in different order
123124
# Use encode_enriched_type to avoid manual mistakes
124-
converter = build_converter(Order)
125+
converter = build_engine_value_converter(Order)
125126
# Provide all fields in the correct order
126127
engine_val = ["O123", "mixed nuts", 25.0, "default_extra"]
127128
assert converter(engine_val) == Order("O123", "mixed nuts", 25.0, "default_extra")
128129

129130
def test_make_engine_value_converter_collections():
130131
# List of structs
131-
converter = build_converter(list[Order])
132+
converter = build_engine_value_converter(list[Order])
132133
engine_val = [
133134
["O1", "item1", 10.0, "default_extra"],
134135
["O2", "item2", 20.0, "default_extra"]
135136
]
136137
assert converter(engine_val) == [Order("O1", "item1", 10.0, "default_extra"), Order("O2", "item2", 20.0, "default_extra")]
137138
# Struct with list field
138-
converter = build_converter(Customer)
139+
converter = build_engine_value_converter(Customer)
139140
engine_val = ["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"], ["premium"]]]
140141
assert converter(engine_val) == Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), [Tag("vip"), Tag("premium")])
141142
# Struct with struct field
142-
converter = build_converter(NestedStruct)
143+
converter = build_engine_value_converter(NestedStruct)
143144
engine_val = [
144145
["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"]]],
145146
[["O1", "item1", 10.0, "default_extra"], ["O2", "item2", 20.0, "default_extra"]],
@@ -153,9 +154,66 @@ def test_make_engine_value_converter_collections():
153154

154155
def test_make_engine_value_converter_defaults_and_missing_fields():
155156
# Missing optional field in engine value
156-
converter = build_converter(Customer)
157+
converter = build_engine_value_converter(Customer)
157158
engine_val = ["Alice", ["O1", "item1", 10.0, "default_extra"], None] # tags explicitly None
158159
assert converter(engine_val) == Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), None)
159160
# Extra field in engine value (should ignore)
160161
engine_val = ["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"]], "extra"]
161162
assert converter(engine_val) == Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), [Tag("vip")])
163+
164+
def test_engine_python_schema_field_order():
165+
"""
166+
Engine and Python dataclasses have the same fields but different order.
167+
Converter should map by field name, not order.
168+
"""
169+
@dataclass
170+
class EngineOrder:
171+
id: str
172+
name: str
173+
price: float
174+
@dataclass
175+
class PythonOrder:
176+
name: str
177+
id: str
178+
price: float
179+
extra: str = "default"
180+
converter = build_engine_value_converter(EngineOrder, PythonOrder)
181+
engine_val = ["O123", "mixed nuts", 25.0] # matches EngineOrder order
182+
assert converter(engine_val) == PythonOrder("mixed nuts", "O123", 25.0, "default")
183+
184+
def test_engine_python_schema_extra_field():
185+
"""
186+
Python dataclass has an extra field not present in engine schema.
187+
Converter should fill with default value.
188+
"""
189+
@dataclass
190+
class EngineOrder:
191+
id: str
192+
name: str
193+
@dataclass
194+
class PythonOrder:
195+
id: str
196+
name: str
197+
price: float = 0.0
198+
converter = build_engine_value_converter(EngineOrder, PythonOrder)
199+
engine_val = ["O123", "mixed nuts"]
200+
assert converter(engine_val) == PythonOrder("O123", "mixed nuts", 0.0)
201+
202+
def test_engine_python_schema_missing_field():
203+
"""
204+
Engine dataclass has a field missing in Python dataclass.
205+
Converter should ignore the missing field.
206+
"""
207+
from dataclasses import dataclass
208+
@dataclass
209+
class EngineOrder:
210+
id: str
211+
name: str
212+
price: float
213+
@dataclass
214+
class PythonOrder:
215+
id: str
216+
name: str
217+
converter = build_engine_value_converter(EngineOrder, PythonOrder)
218+
engine_val = ["O123", "mixed nuts", 25.0]
219+
assert converter(engine_val) == PythonOrder("O123", "mixed nuts")

0 commit comments

Comments
 (0)