@@ -32,6 +32,33 @@ def test_compare(actual, op, expected, result):
3232 assert _compare (actual , op , expected ) is result
3333
3434
35+ @pytest .mark .parametrize (
36+ ("actual" , "expected" , "result" ),
37+ [
38+ (1250 , 1250.0 , True ),
39+ (0 , 0.0 , True ),
40+ ("86.40" , 86.4 , True ),
41+ (1250.001 , "1250.00" , False ),
42+ ("00123" , "123" , True ),
43+ (float ("nan" ), 0 , False ),
44+ (float ("inf" ), 0 , False ),
45+ (True , 1 , False ),
46+ ],
47+ )
48+ def test_decimal_equality_uses_shared_exact_numeric_path (actual , expected , result ):
49+ assert _compare (actual , "eq" , expected , "decimal" ) is result
50+
51+
52+ def test_numeric_contract_is_explicit_and_does_not_leak_to_identifiers ():
53+ assert _compare ("00123" , "eq" , "123" ) is False
54+ assert _compare ("1250" , "eq" , 1250 ) is True
55+ assert _compare ("1250" , "eq" , 1250 , "decimal" ) is True
56+
57+
58+ def test_decimal_inequality_fails_closed_for_invalid_values ():
59+ assert _compare ("not-a-number" , "ne" , 0 , "decimal" ) is False
60+
61+
3562def test_parse_offset ():
3663 assert _parse_offset ("" ) == timedelta (0 )
3764 assert _parse_offset ("0" ) == timedelta (0 )
@@ -71,6 +98,13 @@ def test_evaluate_operand_free_column_ops():
7198 assert evaluate (1 , {"note" : "" }, empty , {}, {}, "A" ).grounded
7299
73100
101+ def test_evaluate_decimal_field_contract ():
102+ check = {"match" : [{"column" : "amount" , "op" : "eq" , "arg" : "amount" , "type" : "decimal" }]}
103+ assert evaluate (1 , {"amount" : 1250.0 }, check , {}, {"amount" : 1250 }, "invoice" ).grounded
104+ assert evaluate (1 , {"amount" : 86.4 }, check , {}, {"amount" : "86.40" }, "invoice" ).grounded
105+ assert not evaluate (1 , {"amount" : 1250.0 }, check , {}, {"amount" : 1250.001 }, "invoice" ).grounded
106+
107+
74108def test_compare_dt ():
75109 ref = datetime (2026 , 1 , 1 , tzinfo = timezone .utc )
76110 assert _compare_dt (None , "gt" , ref ) is False # unparseable actual -> safe False
0 commit comments