gabriel / muse public
test_schema_supercharge.py python
457 lines 20.3 KB
Raw
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 142 days ago
1 """Seven-tier tests for ``muse/core/schema.py``.
2
3 All types are TypedDicts — no runtime logic, so the tiers focus on:
4
5 Unit — field presence/types on every TypedDict, Literal constraints.
6 Integration — ElementSchema union membership, MapSchema recursive nesting,
7 DomainSchema round-trips through json.dumps / json.loads.
8 End-to-end — schema instances accepted by functions that consume DomainSchema.
9 Stress — 10 000 construction cycles; deeply-nested MapSchema.
10 Data integrity — field values survive JSON round-trip unchanged.
11 Security — hostile strings in str fields do not cause crashes.
12 Performance — 10 000 constructions under 1 s.
13 """
14
15 from __future__ import annotations
16
17 import json
18 import time
19 import typing
20
21 import pytest
22
23
24 # ──────────────────────────────────────────────────────────────────────────────
25 # Helpers
26 # ──────────────────────────────────────────────────────────────────────────────
27
28
29 def _seq(**kw) -> dict:
30 base = dict(
31 kind="sequence",
32 element_type="note",
33 identity="by_position",
34 diff_algorithm="lcs",
35 alphabet=None,
36 )
37 base.update(kw)
38 return base
39
40
41 def _tree(**kw) -> dict:
42 base = dict(kind="tree", node_type="ast_node", diff_algorithm="zhang_shasha")
43 base.update(kw)
44 return base
45
46
47 def _tensor(**kw) -> dict:
48 base = dict(
49 kind="tensor", dtype="float32", rank=2, epsilon=1e-6, diff_mode="sparse"
50 )
51 base.update(kw)
52 return base
53
54
55 def _set(**kw) -> dict:
56 base = dict(kind="set", element_type="file_id", identity="by_id")
57 base.update(kw)
58 return base
59
60
61 def _map(value_schema: dict | None = None, **kw) -> dict:
62 base = dict(
63 kind="map",
64 key_type="str",
65 value_schema=value_schema or _seq(),
66 identity="by_key",
67 )
68 base.update(kw)
69 return base
70
71
72 def _dim(**kw) -> dict:
73 base = dict(
74 name="notes",
75 description="MIDI note events",
76 schema=_seq(),
77 independent_merge=True,
78 )
79 base.update(kw)
80 return base
81
82
83 def _crdt_dim(**kw) -> dict:
84 base = dict(
85 name="tempo",
86 description="BPM value",
87 crdt_type="lww_register",
88 independent_merge=True,
89 )
90 base.update(kw)
91 return base
92
93
94 def _domain(**kw) -> dict:
95 base = dict(
96 domain="midi",
97 description="MIDI music domain",
98 dimensions=[_dim()],
99 top_level=_set(),
100 merge_mode="three_way",
101 schema_version="0.1.0",
102 )
103 base.update(kw)
104 return base
105
106
107 # ──────────────────────────────────────────────────────────────────────────────
108 # Unit — SequenceSchema
109 # ──────────────────────────────────────────────────────────────────────────────
110
111
112 class TestSequenceSchema:
113 def test_required_keys(self) -> None:
114 from muse.core.schema import SequenceSchema
115 hints = typing.get_type_hints(SequenceSchema)
116 assert {"kind", "element_type", "identity", "diff_algorithm", "alphabet"} <= set(hints)
117
118 def test_kind_literal_is_sequence(self) -> None:
119 from muse.core.schema import SequenceSchema
120 hints = typing.get_type_hints(SequenceSchema)
121 args = typing.get_args(hints["kind"])
122 assert "sequence" in args
123
124 def test_valid_diff_algorithms(self) -> None:
125 from muse.core.schema import SequenceSchema
126 hints = typing.get_type_hints(SequenceSchema)
127 algos = set(typing.get_args(hints["diff_algorithm"]))
128 assert algos == {"lcs", "myers", "patience"}
129
130 def test_valid_identity_values(self) -> None:
131 from muse.core.schema import SequenceSchema
132 hints = typing.get_type_hints(SequenceSchema)
133 ids = set(typing.get_args(hints["identity"]))
134 assert ids == {"by_id", "by_position", "by_content"}
135
136 def test_alphabet_is_optional_list(self) -> None:
137 from muse.core.schema import SequenceSchema
138 hints = typing.get_type_hints(SequenceSchema)
139 # Should be list[str] | None
140 args = typing.get_args(hints["alphabet"])
141 assert type(None) in args
142
143
144 # ──────────────────────────────────────────────────────────────────────────────
145 # Unit — TreeSchema
146 # ──────────────────────────────────────────────────────────────────────────────
147
148
149 class TestTreeSchema:
150 def test_required_keys(self) -> None:
151 from muse.core.schema import TreeSchema
152 hints = typing.get_type_hints(TreeSchema)
153 assert {"kind", "node_type", "diff_algorithm"} <= set(hints)
154
155 def test_valid_diff_algorithms(self) -> None:
156 from muse.core.schema import TreeSchema
157 hints = typing.get_type_hints(TreeSchema)
158 algos = set(typing.get_args(hints["diff_algorithm"]))
159 assert algos == {"zhang_shasha", "gumtree"}
160
161
162 # ──────────────────────────────────────────────────────────────────────────────
163 # Unit — TensorSchema
164 # ──────────────────────────────────────────────────────────────────────────────
165
166
167 class TestTensorSchema:
168 def test_required_keys(self) -> None:
169 from muse.core.schema import TensorSchema
170 hints = typing.get_type_hints(TensorSchema)
171 assert {"kind", "dtype", "rank", "epsilon", "diff_mode"} <= set(hints)
172
173 def test_valid_dtypes(self) -> None:
174 from muse.core.schema import TensorSchema
175 hints = typing.get_type_hints(TensorSchema)
176 dtypes = set(typing.get_args(hints["dtype"]))
177 assert dtypes == {"float32", "float64", "int8", "int16", "int32", "int64"}
178
179 def test_valid_diff_modes(self) -> None:
180 from muse.core.schema import TensorSchema
181 hints = typing.get_type_hints(TensorSchema)
182 modes = set(typing.get_args(hints["diff_mode"]))
183 assert modes == {"sparse", "block", "full"}
184
185
186 # ──────────────────────────────────────────────────────────────────────────────
187 # Unit — SetSchema
188 # ──────────────────────────────────────────────────────────────────────────────
189
190
191 class TestSetSchema:
192 def test_required_keys(self) -> None:
193 from muse.core.schema import SetSchema
194 hints = typing.get_type_hints(SetSchema)
195 assert {"kind", "element_type", "identity"} <= set(hints)
196
197 def test_valid_identity_values(self) -> None:
198 from muse.core.schema import SetSchema
199 hints = typing.get_type_hints(SetSchema)
200 ids = set(typing.get_args(hints["identity"]))
201 assert ids == {"by_content", "by_id"}
202
203
204 # ──────────────────────────────────────────────────────────────────────────────
205 # Unit — MapSchema
206 # ──────────────────────────────────────────────────────────────────────────────
207
208
209 class TestMapSchema:
210 def test_required_keys(self) -> None:
211 from muse.core.schema import MapSchema
212 hints = typing.get_type_hints(MapSchema)
213 assert {"kind", "key_type", "value_schema", "identity"} <= set(hints)
214
215 def test_identity_is_by_key(self) -> None:
216 from muse.core.schema import MapSchema
217 hints = typing.get_type_hints(MapSchema)
218 args = typing.get_args(hints["identity"])
219 assert "by_key" in args
220
221
222 # ──────────────────────────────────────────────────────────────────────────────
223 # Unit — DimensionSpec
224 # ──────────────────────────────────────────────────────────────────────────────
225
226
227 class TestDimensionSpec:
228 def test_required_keys(self) -> None:
229 from muse.core.schema import DimensionSpec
230 hints = typing.get_type_hints(DimensionSpec)
231 assert {"name", "description", "schema", "independent_merge"} <= set(hints)
232
233 def test_independent_merge_is_bool(self) -> None:
234 from muse.core.schema import DimensionSpec
235 hints = typing.get_type_hints(DimensionSpec)
236 assert hints["independent_merge"] is bool
237
238
239 # ──────────────────────────────────────────────────────────────────────────────
240 # Unit — CRDTDimensionSpec
241 # ──────────────────────────────────────────────────────────────────────────────
242
243
244 class TestCRDTDimensionSpec:
245 def test_required_keys(self) -> None:
246 from muse.core.schema import CRDTDimensionSpec
247 hints = typing.get_type_hints(CRDTDimensionSpec)
248 assert {"name", "description", "crdt_type", "independent_merge"} <= set(hints)
249
250 def test_valid_crdt_types(self) -> None:
251 from muse.core.schema import CRDTPrimitive
252 args = set(typing.get_args(CRDTPrimitive))
253 assert args == {"lww_register", "or_set", "rga", "aw_map", "g_counter"}
254
255
256 # ──────────────────────────────────────────────────────────────────────────────
257 # Unit — DomainSchema
258 # ──────────────────────────────────────────────────────────────────────────────
259
260
261 class TestDomainSchema:
262 def test_required_keys(self) -> None:
263 from muse.core.schema import DomainSchema
264 hints = typing.get_type_hints(DomainSchema)
265 assert {"domain", "description", "dimensions", "top_level", "merge_mode", "schema_version"} <= set(hints)
266
267 def test_valid_merge_modes(self) -> None:
268 from muse.core.schema import DomainSchema
269 hints = typing.get_type_hints(DomainSchema)
270 modes = set(typing.get_args(hints["merge_mode"]))
271 assert modes == {"three_way", "crdt"}
272
273
274 # ──────────────────────────────────────────────────────────────────────────────
275 # Integration — ElementSchema union, recursive nesting, JSON round-trip
276 # ──────────────────────────────────────────────────────────────────────────────
277
278
279 class TestIntegration:
280 def test_element_schema_includes_all_five_types(self) -> None:
281 from muse.core.schema import (
282 ElementSchema, MapSchema, SequenceSchema,
283 SetSchema, TensorSchema, TreeSchema,
284 )
285 members = typing.get_args(ElementSchema)
286 assert SequenceSchema in members
287 assert TreeSchema in members
288 assert TensorSchema in members
289 assert SetSchema in members
290 assert MapSchema in members
291
292 def test_map_schema_recursive_nesting(self) -> None:
293 """MapSchema.value_schema can itself be a MapSchema — recursive."""
294 inner = _map(value_schema=_seq())
295 outer = _map(value_schema=inner)
296 # Should be JSON-serialisable without error.
297 json.dumps(outer)
298
299 def test_domain_schema_json_round_trip(self) -> None:
300 schema = _domain()
301 raw = json.dumps(schema)
302 back = json.loads(raw)
303 assert back == schema
304
305 def test_dimension_spec_json_round_trip(self) -> None:
306 dim = _dim()
307 assert json.loads(json.dumps(dim)) == dim
308
309 def test_crdt_dimension_spec_json_round_trip(self) -> None:
310 cdim = _crdt_dim()
311 assert json.loads(json.dumps(cdim)) == cdim
312
313 def test_all_element_schema_types_json_serialisable(self) -> None:
314 for schema in [_seq(), _tree(), _tensor(), _set(), _map()]:
315 json.dumps(schema) # must not raise
316
317 def test_domain_with_crdt_merge_mode(self) -> None:
318 schema = _domain(merge_mode="crdt")
319 assert json.loads(json.dumps(schema))["merge_mode"] == "crdt"
320
321 def test_multiple_dimensions_in_domain(self) -> None:
322 schema = _domain(dimensions=[_dim(name="notes"), _dim(name="tempo")])
323 raw = json.dumps(schema)
324 back = json.loads(raw)
325 assert len(back["dimensions"]) == 2
326
327
328 # ──────────────────────────────────────────────────────────────────────────────
329 # End-to-end — schema used as plugin contract
330 # ──────────────────────────────────────────────────────────────────────────────
331
332
333 class TestEndToEnd:
334 def test_schema_importable_from_public_path(self) -> None:
335 from muse.core.schema import DomainSchema # noqa: F401
336
337 def test_element_schema_importable(self) -> None:
338 from muse.core.schema import ElementSchema # noqa: F401
339
340 def test_crdt_primitive_importable(self) -> None:
341 from muse.core.schema import CRDTPrimitive # noqa: F401
342
343 def test_domain_schema_dict_passable_to_json_dumps(self) -> None:
344 schema = _domain()
345 result = json.dumps(schema, sort_keys=True)
346 assert '"domain": "midi"' in result
347
348 def test_sequence_schema_with_alphabet(self) -> None:
349 seq = _seq(alphabet=["C", "D", "E", "F", "G", "A", "B"])
350 assert json.loads(json.dumps(seq))["alphabet"] == ["C", "D", "E", "F", "G", "A", "B"]
351
352
353 # ──────────────────────────────────────────────────────────────────────────────
354 # Stress
355 # ──────────────────────────────────────────────────────────────────────────────
356
357
358 class TestStress:
359 def test_10000_domain_schema_constructions(self) -> None:
360 for i in range(10_000):
361 schema = _domain(domain=f"domain_{i}", schema_version=f"0.{i}.0")
362 assert schema["domain"] == f"domain_{i}"
363
364 def test_deeply_nested_map_schema(self) -> None:
365 """MapSchema.value_schema is recursive — 50 levels deep must not crash."""
366 schema: dict = _seq()
367 for _ in range(50):
368 schema = _map(value_schema=schema)
369 # Must be JSON-serialisable regardless of depth.
370 json.dumps(schema)
371
372 def test_domain_with_100_dimensions(self) -> None:
373 dims = [_dim(name=f"dim_{i}") for i in range(100)]
374 schema = _domain(dimensions=dims)
375 raw = json.loads(json.dumps(schema))
376 assert len(raw["dimensions"]) == 100
377
378
379 # ──────────────────────────────────────────────────────────────────────────────
380 # Data integrity
381 # ──────────────────────────────────────────────────────────────────────────────
382
383
384 class TestDataIntegrity:
385 def test_tensor_epsilon_survives_json_round_trip(self) -> None:
386 t = _tensor(epsilon=1e-9)
387 back = json.loads(json.dumps(t))
388 assert abs(back["epsilon"] - 1e-9) < 1e-20
389
390 def test_tensor_rank_survives_json_round_trip(self) -> None:
391 t = _tensor(rank=4)
392 assert json.loads(json.dumps(t))["rank"] == 4
393
394 def test_independent_merge_bool_survives_round_trip(self) -> None:
395 dim = _dim(independent_merge=False)
396 back = json.loads(json.dumps(dim))
397 assert back["independent_merge"] is False
398
399 def test_domain_schema_version_string_preserved(self) -> None:
400 schema = _domain(schema_version="1.2.3")
401 back = json.loads(json.dumps(schema))
402 assert back["schema_version"] == "1.2.3"
403
404 def test_set_element_type_preserved(self) -> None:
405 s = _set(element_type="track_id")
406 assert json.loads(json.dumps(s))["element_type"] == "track_id"
407
408
409 # ──────────────────────────────────────────────────────────────────────────────
410 # Security
411 # ──────────────────────────────────────────────────────────────────────────────
412
413
414 class TestSecurity:
415 def test_hostile_string_in_domain_name_survives_json(self) -> None:
416 evil = '"; DROP TABLE domains; --'
417 schema = _domain(domain=evil)
418 back = json.loads(json.dumps(schema))
419 assert back["domain"] == evil
420
421 def test_ansi_in_description_survives_json(self) -> None:
422 desc = "\x1b[31mevil\x1b[0m"
423 schema = _domain(description=desc)
424 back = json.loads(json.dumps(schema))
425 assert back["description"] == desc
426
427 def test_null_byte_in_element_type_survives_json(self) -> None:
428 s = _seq(element_type="note\x00evil")
429 back = json.loads(json.dumps(s))
430 assert back["element_type"] == "note\x00evil"
431
432 def test_unicode_in_dimension_name_survives_json(self) -> None:
433 dim = _dim(name="音符")
434 back = json.loads(json.dumps(dim))
435 assert back["name"] == "音符"
436
437
438 # ──────────────────────────────────────────────────────────────────────────────
439 # Performance
440 # ──────────────────────────────────────────────────────────────────────────────
441
442
443 class TestPerformance:
444 def test_10000_constructions_under_1s(self) -> None:
445 start = time.perf_counter()
446 for i in range(10_000):
447 _domain(schema_version=f"0.{i}.0")
448 elapsed = time.perf_counter() - start
449 assert elapsed < 1.0
450
451 def test_json_round_trip_10000_times_under_2s(self) -> None:
452 schema = _domain()
453 start = time.perf_counter()
454 for _ in range(10_000):
455 json.loads(json.dumps(schema))
456 elapsed = time.perf_counter() - start
457 assert elapsed < 2.0
File History 1 commit
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 142 days ago