1+ """Tests for the generic JSON value codec helpers."""
2+
3+ import numpy as np
4+ import pytest
5+
6+ from plaid .utils .json_codec import (
7+ _decode_array ,
8+ decode_json_value ,
9+ decode_leaf_value ,
10+ encode_json_value ,
11+ encode_leaf_value ,
12+ )
13+
14+
15+ @pytest .mark .parametrize (
16+ "value, expected" ,
17+ [
18+ (np .int32 (7 ), 7 ),
19+ (np .float64 (1.25 ), 1.25 ),
20+ ((np .int64 (1 ), np .int64 (2 )), [1 , 2 ]),
21+ ],
22+ )
23+ def test_encode_leaf_value_normalizes_numpy_scalars_and_tuples (value , expected ):
24+ """NumPy scalar values and tuples are converted to JSON-compatible data."""
25+ assert encode_leaf_value (value ) == expected
26+
27+
28+ def test_encode_decode_leaf_value_roundtrips_bytes ():
29+ """Bytes values are encoded as base64 objects and decoded back to bytes."""
30+ value = b"CGNS bytes"
31+
32+ encoded = encode_leaf_value (value )
33+
34+ assert encoded ["kind" ] == "bytes"
35+ assert decode_leaf_value (encoded ) == value
36+
37+
38+ def test_decode_leaf_value_decodes_nested_lists ():
39+ """List payloads are decoded recursively."""
40+ encoded_bytes = encode_leaf_value (b"nested bytes" )
41+
42+ assert decode_leaf_value ([encoded_bytes , [1 , encoded_bytes ]]) == [
43+ b"nested bytes" ,
44+ [1 , b"nested bytes" ],
45+ ]
46+
47+
48+ def test_encode_leaf_value_rejects_unsupported_values ():
49+ """Unsupported value types raise a TypeError with a clear message."""
50+ with pytest .raises (TypeError , match = "Unsupported value type for JSON serialization" ):
51+ encode_leaf_value ({"not" : "a supported value" })
52+
53+
54+ def test_decode_leaf_value_leaves_unknown_dict_kind_unchanged ():
55+ """Unknown dictionary payloads are passed through unchanged."""
56+ value = {"kind" : "custom" , "data" : [1 , 2 , 3 ]}
57+
58+ assert decode_leaf_value (value ) is value
59+
60+
61+ def test_decode_array_rejects_unknown_encoding ():
62+ """Only JSON and base64 ndarray encodings are supported."""
63+ with pytest .raises (ValueError , match = "Unsupported ndarray encoding" ):
64+ _decode_array (
65+ {
66+ "encoding" : "unsupported" ,
67+ "dtype" : "<f8" ,
68+ "shape" : [0 ],
69+ "data" : "" ,
70+ }
71+ )
72+
73+
74+ def test_encode_decode_array_roundtrips_numeric_dtypes ():
75+ """Numeric NumPy arrays survive the base64 encode/decode roundtrip."""
76+ array = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]], dtype = np .float64 )
77+
78+ encoded = encode_leaf_value (array )
79+ decoded = decode_leaf_value (encoded )
80+
81+ assert encoded ["kind" ] == "ndarray"
82+ assert np .array_equal (decoded , array )
83+ assert decoded .dtype == array .dtype
84+
85+
86+ def test_encode_decode_array_roundtrips_unicode_dtype ():
87+ """Unicode arrays are encoded with the JSON encoding and restored."""
88+ array = np .array (["alpha" , "beta" ], dtype = "<U5" )
89+
90+ encoded = encode_leaf_value (array )
91+ decoded = decode_leaf_value (encoded )
92+
93+ assert encoded ["encoding" ] == "json"
94+ assert np .array_equal (decoded , array )
95+ assert decoded .dtype == array .dtype
96+
97+
98+ def test_encode_array_rejects_object_dtype ():
99+ """Object dtype arrays are intentionally not part of the portable schema."""
100+ array = np .array ([{"not" : "portable" }], dtype = object )
101+
102+ with pytest .raises (TypeError , match = "Object dtype arrays" ):
103+ encode_leaf_value (array )
104+
105+
106+ def test_encode_json_value_roundtrips_nested_structures ():
107+ """Nested dicts and lists with arrays survive the JSON value roundtrip."""
108+ value = {
109+ "scalar" : 3 ,
110+ "text" : "hello" ,
111+ "array" : np .array ([1 , 2 , 3 ], dtype = np .int64 ),
112+ "nested" : [np .float64 (1.5 ), {"bytes" : b"data" }],
113+ }
114+
115+ encoded = encode_json_value (value )
116+ decoded = decode_json_value (encoded )
117+
118+ assert decoded ["scalar" ] == 3
119+ assert decoded ["text" ] == "hello"
120+ assert np .array_equal (decoded ["array" ], value ["array" ])
121+ assert decoded ["nested" ][0 ] == 1.5
122+ assert decoded ["nested" ][1 ]["bytes" ] == b"data"
0 commit comments