""" Enumerated parameters, and the length control that motivated them. WHAT THIS IS ACTUALLY GUARDING ``model_length_mm`` compares ``length_view`` against the literal "Full Length" and falls through to the preview length for anything else -- no rejection, no error. The reference behaved the same way, so the port is right to keep it. That makes the composer the only place the mistake can be caught. Before this, ``length_view`` was not in COMMON_GROUPS at all, so the Full Length branch was unreachable and every model was a 100 mm preview whatever ``member_length_ft`` said. Exposing it as free text would have replaced an unreachable control with a silent one: "Full Length " with a trailing space builds a preview and says nothing. So the claims under test are: the branch is reachable, it moves the length it is supposed to move, and a value outside the declared set cannot reach the build. """ from __future__ import annotations import pytest app = pytest.importorskip("mechcomp.web.app") def payload(**overrides): return app.build_payload("3x", "Y", dict(overrides)) def length_of(**overrides): p = payload(**overrides) assert p["ok"], p.get("message") return p["report"]["LENGTH_MM"] # --------------------------------------------------------------------------- # The declaration # --------------------------------------------------------------------------- def test_length_view_is_declared_with_exactly_its_two_values(): assert app.ENUM_PARAMS["length_view"] == ["Preview", "Full Length"] def test_every_enumerated_parameter_is_a_real_family_parameter(): """ A declared choice for a parameter no family has would render a control that does nothing -- the failure COMMON_GROUPS has a comment about. """ for family in app.families().values(): for key in app.ENUM_PARAMS: assert key in family.defaults, key def test_every_declared_default_is_one_of_its_own_choices(): """ If a family's default were outside the list, the select would open showing a value it cannot represent and the first interaction would silently move the model. """ for family in app.families().values(): for key, allowed in app.ENUM_PARAMS.items(): assert family.defaults[key] in allowed # --------------------------------------------------------------------------- # Reachability -- the defect that started this # --------------------------------------------------------------------------- def test_length_view_is_reachable_from_the_composer(): for name, family in app.families().items(): exposed = {k for _, keys in app.profile_params(family, "Y" if name == "3x" else "Cross") for k in keys} assert "length_view" in exposed, name def test_the_browser_is_told_the_choices(): assert payload()["choices"]["length_view"] == ["Preview", "Full Length"] # --------------------------------------------------------------------------- # It moves the length it claims to move # --------------------------------------------------------------------------- def test_preview_is_the_preview_length(): assert length_of(length_view="Preview", preview_length_mm=100) == 100 def test_full_length_is_feet_times_304_8(): assert length_of(length_view="Full Length", member_length_ft=10) == 3048 def test_full_length_follows_member_length_ft(): """ The positive control for the pair above: a length that ignored its input would satisfy either test alone by returning a constant. """ assert length_of(length_view="Full Length", member_length_ft=20) == 6096 def test_preview_ignores_member_length_ft(): assert length_of(length_view="Preview", member_length_ft=40) == 100 # --------------------------------------------------------------------------- # A value outside the set never reaches the build # --------------------------------------------------------------------------- @pytest.mark.parametrize("bad", ["Full Length ", "full length", "FULL LENGTH", "", "Fully Lengthed", "Preview\n"]) def test_an_unrecognised_value_falls_back_to_the_default(bad): """ Not a rejection -- a fallback. The family default is a valid model, and the composer is a viewer rather than a validator. What must not happen is the string reaching ``model_length_mm`` and being silently ignored there, which is how a trailing space becomes a 100 mm export of a 10 ft member. """ p = payload(length_view=bad, member_length_ft=10) assert p["ok"] assert p["values"]["length_view"] == "Preview" assert p["report"]["LENGTH_MM"] == 100 def test_the_unrecognised_value_is_not_merely_unused_but_absent(): """ Asserts the mechanism rather than its effect. If the string were passed through and happened to be ignored downstream, the test above would still pass and a later change to ``model_length_mm`` would turn it into a defect. """ p = payload(length_view="Full Length ", member_length_ft=10) assert "Full Length " not in p["values"].values() def test_a_recognised_value_does_reach_the_build(): """The positive control: the fallback is not simply dropping everything.""" p = payload(length_view="Full Length", member_length_ft=10) assert p["values"]["length_view"] == "Full Length" assert p["report"]["LENGTH_MM"] == 3048 # --------------------------------------------------------------------------- # The length control does not disturb identity # --------------------------------------------------------------------------- def test_length_view_changes_the_design_id(): """ It is a build parameter, so it belongs in the hash -- unlike the author. Two members of different lengths are different designs. """ preview = payload(length_view="Preview")["input_id"] full = payload(length_view="Full Length")["input_id"] assert preview != full def test_an_unrecognised_value_yields_the_default_design_id(): """Follows from the fallback, and is the property someone would rely on.""" assert payload(length_view="Full Length ")["input_id"] == payload()["input_id"]