""" The design record has to survive being the only thing left. A part on the bench, a text file beside it, and five years. If the record cannot regenerate the design from that, it is decoration. These tests are mostly about exactness and about what must NOT change an identity. """ from __future__ import annotations import pytest from mechcomp.design_record import ( FORMAT_VERSION, VERIFY_KEYS, DesignRecord, canonical_params, canonical_value, parse_value, record_for, resolve, toolchain_versions, ) from mechcomp.profiles.three_x import FAMILY as THREE_X def _record(**over): base = dict( family="3x", profile="Y", defaults=THREE_X.defaults, params={"fit_clearance_mm": 0.25}, code_revision="abc1234", generator_revision="8.0.0", ) base.update(over) return record_for(**base) # --------------------------------------------------------------------------- # Exactness -- the record is a recipe, not a description # --------------------------------------------------------------------------- def test_floats_round_trip_exactly(): """ Every float must survive render -> parse unchanged, bit for bit. This is the property the whole record rests on. ``%g`` at six significant figures -- what the report emits -- would lose bits here and turn the record into a description of the part rather than a means of rebuilding it. """ hostile = [0.1, 0.2, 0.30000000000000004, 1 / 3, 15.875, 0.508, 13.4, 0.79, 1e-9, 1.7976931348623157e308, 5e-324, 2.2250738585072014e-308, 123456.789012345, 0.1 + 0.2] for value in hostile: assert float(canonical_value(value)) == value, value assert parse_value(canonical_value(value)) == value, value rec = _record(params={"fit_clearance_mm": 0.1 + 0.2, "strap_width_mm": 1 / 3}) back = DesignRecord.parse(rec.render()) assert back.params["fit_clearance_mm"] == 0.1 + 0.2 assert back.params["strap_width_mm"] == 1 / 3 def test_render_parse_round_trip_preserves_identity(): """A record written out and read back is the same design.""" rec = _record() back = DesignRecord.parse(rec.render()) assert back.family == rec.family assert back.profile == rec.profile assert back.params == rec.params assert back.input_id == rec.input_id assert back.code_revision == rec.code_revision assert back.generator_revision == rec.generator_revision assert back.toolchain == rec.toolchain assert back.build_id == rec.build_id def test_resolve_matches_the_assemble_merge(): """ ``resolve`` reproduces ``_common.assemble``'s ``{**defaults, **params}``. Duplicated deliberately, to keep this module off the build path. Duplication is only safe while it is checked, so it is checked. """ params = {"fit_clearance_mm": 0.4, "y_junction_web_mm": 2.0} got = resolve(THREE_X.defaults, params) want = {**THREE_X.defaults, **params} assert got == want assert got["fit_clearance_mm"] == 0.4 assert got["strap_width_mm"] == THREE_X.defaults["strap_width_mm"] assert set(got) == set(THREE_X.defaults) # --------------------------------------------------------------------------- # Identity -- what must and must not change it # --------------------------------------------------------------------------- def test_identical_designs_have_identical_ids(): assert _record().input_id == _record().input_id assert _record().build_id == _record().build_id def test_parameter_order_does_not_change_identity(): """Dict ordering is an accident of how a caller built the mapping.""" a = record_for(family="3x", profile="Y", defaults={}, params={"a": 1.0, "b": 2.0, "c": 3.0}) b = record_for(family="3x", profile="Y", defaults={}, params={"c": 3.0, "b": 2.0, "a": 1.0}) assert a.input_id == b.input_id assert canonical_params(a.params) == canonical_params(b.params) def test_any_parameter_change_changes_the_input_id(): """The tuning loop must never silently reuse an identity.""" base = _record() seen = {base.input_id} for clearance in (0.15, 0.2, 0.24, 0.2500001, 0.26, 0.0, -0.05): other = _record(params={"fit_clearance_mm": clearance}) assert other.input_id != base.input_id, clearance assert other.input_id not in seen, "collision at %r" % clearance seen.add(other.input_id) assert _record(profile="T").input_id != base.input_id assert _record(family="4x").input_id != base.input_id def test_code_revision_changes_build_id_but_not_input_id(): """ The distinction that makes the pair useful: same intent, different bytes possible. That is the moment to check whether the geometry moved, and the record is what tells you to look. """ a = _record(code_revision="aaaaaaa") b = _record(code_revision="bbbbbbb") assert a.input_id == b.input_id assert a.build_id != b.build_id c = _record(generator_revision="8.0.1") assert c.input_id == a.input_id assert c.build_id != a.build_id def test_timestamp_and_note_are_excluded_from_both_ids(): """ A design regenerated tomorrow is the same design. If the clock entered the identity, nothing would ever reproduce. """ a = _record(generated="2026-08-23T10:00:00Z", note="first attempt") b = _record(generated="2027-01-01T00:00:00Z", note="reprint for Dave") assert a.input_id == b.input_id assert a.build_id == b.build_id assert "2026-08-23" in a.render() assert "first attempt" in a.render() def test_verification_values_do_not_affect_identity(): """ Reported values are outputs. If they entered the hash, a part would be identified by what it turned out to be rather than by what was asked for -- and two runs of the same design on different GEOS builds could then claim to be different designs. """ report = {k: 1.0 for k in VERIFY_KEYS} other = {k: 2.0 for k in VERIFY_KEYS} a = _record(report=report) b = _record(report=other) c = _record() assert a.input_id == b.input_id == c.input_id assert a.build_id == b.build_id == c.build_id assert a.verification != b.verification # --------------------------------------------------------------------------- # The rendered artifact # --------------------------------------------------------------------------- def test_render_is_deterministic(): assert _record().render() == _record().render() def test_render_is_readable_without_this_software(): """ Plain text, one fact per line, no framing that needs a parser. A record that only this program can open is not a record. """ rec = _record(report={k: 12.3456 for k in VERIFY_KEYS}, stock=["PET strap 15.875 x 0.508 -- unverified: supplied at design time"], fit=["clearance 0.2500 mm per face"], generated="2026-08-23T10:00:00Z") text = rec.render() assert text.startswith("MECHCOMP DESIGN RECORD %s" % FORMAT_VERSION) assert rec.input_id in text and rec.build_id in text assert "3x" in text and "Y" in text assert "PET strap" in text and "unverified" in text assert "clearance 0.2500 mm per face" in text assert "fit_clearance_mm=0.25" in text assert "code_revision=abc1234" in text assert "generator_revision=8.0.0" in text assert "ENVELOPE_X_MM" in text assert text.endswith("\n") # Every resolved parameter reaches the file. A record that carries only the # overrides cannot rebuild anything, because the defaults it silently relied # on will have moved by then. for key in THREE_X.defaults: assert key in text, "parameter %s missing from the record" % key def test_record_carries_every_resolved_parameter_not_just_overrides(): rec = _record(params={"fit_clearance_mm": 0.25}) assert set(rec.params) == set(THREE_X.defaults) assert len(rec.params) > 20 def test_unrecorded_stock_and_fit_say_so_rather_than_being_blank(): text = _record().render() assert "(none recorded)" in text def test_toolchain_is_captured_and_reaches_the_build_id(): """ GEOS can change boolean results on near-degenerate geometry -- the F-034 mechanism exactly. A record omitting it cannot explain why the same parameters produced a different part, so its absence must be a failure and not a quiet blank. """ shapely = pytest.importorskip( "shapely", reason="the toolchain assertion is about capturing the real GEOS " "version; without shapely there is nothing to capture") tools = toolchain_versions() assert "shapely" in tools, "shapely version not captured" assert tools["shapely"] == shapely.__version__ assert tools["shapely"] != "unavailable", "shapely version lookup failed" rec = _record() assert rec.toolchain == tools assert rec.toolchain.get("shapely") text = rec.render() assert "shapely=%s" % tools["shapely"] in text # A different toolchain is a different build, even at identical intent. same_intent = DesignRecord( family=rec.family, profile=rec.profile, params=dict(rec.params), code_revision=rec.code_revision, generator_revision=rec.generator_revision, toolchain={**tools, "shapely": "0.0.0-not-real"}) assert same_intent.input_id == rec.input_id assert same_intent.build_id != rec.build_id empty = DesignRecord(family=rec.family, profile=rec.profile, params=dict(rec.params), code_revision=rec.code_revision, generator_revision=rec.generator_revision, toolchain={}) assert empty.build_id != rec.build_id def test_missing_report_keys_are_omitted_not_invented(): rec = _record(report={"ENVELOPE_X_MM": 20.5}) assert rec.verification == {"ENVELOPE_X_MM": 20.5} assert "MASS_G" not in rec.verification def test_bools_survive_the_round_trip(): """``length_view`` is a string and some profile extras are bools.""" assert canonical_value(True) == "true" assert parse_value("true") is True assert parse_value("false") is False rec = record_for(family="3x", profile="Y", defaults={}, params={"flag": True, "off": False, "name": "Preview"}) back = DesignRecord.parse(rec.render()) assert back.params["flag"] is True assert back.params["off"] is False assert back.params["name"] == "Preview"