@@ -153,16 +153,59 @@ def test_vindex(data: st.DataObject) -> None:
153153
154154
155155@given (store = stores , meta = array_metadata ()) # type: ignore[misc]
156- async def test_roundtrip_array_metadata (
156+ async def test_roundtrip_array_metadata_from_store (
157157 store : Store , meta : ArrayV2Metadata | ArrayV3Metadata
158158) -> None :
159+ """
160+ Verify that the I/O for metadata in a store are lossless.
161+
162+ This test serializes an ArrayV2Metadata or ArrayV3Metadata object to a dict
163+ of buffers via `to_buffer_dict`, writes each buffer to a store under keys
164+ prefixed with "0/", and then reads them back. The test asserts that each
165+ retrieved buffer exactly matches the original buffer.
166+ """
159167 asdict = meta .to_buffer_dict (prototype = default_buffer_prototype ())
160168 for key , expected in asdict .items ():
161169 await store .set (f"0/{ key } " , expected )
162170 actual = await store .get (f"0/{ key } " , prototype = default_buffer_prototype ())
163171 assert actual == expected
164172
165173
174+ @given (data = st .data (), zarr_format = zarr_formats )
175+ def test_roundtrip_array_metadata_from_json (data : st .DataObject , zarr_format : int ) -> None :
176+ """
177+ Verify that JSON serialization and deserialization of metadata is lossless.
178+
179+ For Zarr v2:
180+ - The metadata is split into two JSON documents (one for array data and one
181+ for attributes). The test merges the attributes back before deserialization.
182+ For Zarr v3:
183+ - All metadata is stored in a single JSON document. No manual merger is necessary.
184+
185+ The test then converts both the original and round-tripped metadata objects
186+ into dictionaries using `dataclasses.asdict` and uses a deep equality check
187+ to verify that the roundtrip has preserved all fields (including special
188+ cases like NaN, Infinity, complex numbers, and datetime values).
189+ """
190+ metadata = data .draw (array_metadata (zarr_formats = st .just (zarr_format )))
191+ buffer_dict = metadata .to_buffer_dict (prototype = default_buffer_prototype ())
192+
193+ if zarr_format == 2 :
194+ zarray_dict = json .loads (buffer_dict [ZARRAY_JSON ].to_bytes ().decode ())
195+ zattrs_dict = json .loads (buffer_dict [ZATTRS_JSON ].to_bytes ().decode ())
196+ # zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
197+ zarray_dict ["attributes" ] = zattrs_dict
198+ metadata_roundtripped = ArrayV2Metadata .from_dict (zarray_dict )
199+ else :
200+ zarray_dict = json .loads (buffer_dict [ZARR_JSON ].to_bytes ().decode ())
201+ metadata_roundtripped = ArrayV3Metadata .from_dict (zarray_dict )
202+
203+ orig = dataclasses .asdict (metadata )
204+ rt = dataclasses .asdict (metadata_roundtripped )
205+
206+ assert deep_equal (orig , rt ), f"Roundtrip mismatch:\n Original: { orig } \n Roundtripped: { rt } "
207+
208+
166209# @st.composite
167210# def advanced_indices(draw, *, shape):
168211# basic_idxr = draw(
@@ -187,27 +230,6 @@ async def test_roundtrip_array_metadata(
187230# assert_array_equal(nparray, zarray[:])
188231
189232
190- @given (data = st .data (), zarr_format = zarr_formats )
191- def test_meta_roundtrip (data : st .DataObject , zarr_format : int ) -> None :
192- metadata = data .draw (array_metadata (zarr_formats = st .just (zarr_format )))
193- buffer_dict = metadata .to_buffer_dict (prototype = default_buffer_prototype ())
194-
195- if zarr_format == 2 :
196- zarray_dict = json .loads (buffer_dict [ZARRAY_JSON ].to_bytes ().decode ())
197- zattrs_dict = json .loads (buffer_dict [ZATTRS_JSON ].to_bytes ().decode ())
198- # zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
199- zarray_dict ["attributes" ] = zattrs_dict
200- metadata_roundtripped = ArrayV2Metadata .from_dict (zarray_dict )
201- else :
202- zarray_dict = json .loads (buffer_dict [ZARR_JSON ].to_bytes ().decode ())
203- metadata_roundtripped = ArrayV3Metadata .from_dict (zarray_dict )
204-
205- orig = dataclasses .asdict (metadata )
206- rt = dataclasses .asdict (metadata_roundtripped )
207-
208- assert deep_equal (orig , rt ), f"Roundtrip mismatch:\n Original: { orig } \n Roundtripped: { rt } "
209-
210-
211233@given (npst .from_dtype (dtype = np .dtype ("float64" ), allow_nan = True , allow_infinity = True ))
212234def test_v2meta_nan_and_infinity (fill_value ):
213235 metadata = ArrayV2Metadata (
0 commit comments