From 95276dd1b1ac02cc191a01729c803add6c7411cf Mon Sep 17 00:00:00 2001 From: Jean-Pierre Martineau Date: Sun, 6 Sep 2026 01:10:55 -0400 Subject: [PATCH] The map opens without being told how much of the mesh to frame `meshterm map` with no flags crashed on its own bounds check: --fraction defaults to None so the preference can answer for it, and the guard compared that None against 0.0. The one invocation nobody qualifies was the only one that could not run. The guard now only speaks when a fraction was actually passed. It still earns its place next to Typer's own min/max, which admit exactly 0.0 -- a fraction that frames nothing. The command had no test at all, so there is a seam now: the CLI is registered onto a throwaway Typer app with run_tool_command stubbed, and the params the command builds are read back. Bare, zero, and both ends of the range. Co-Authored-By: Claude Opus 5 (1M context) --- meshterm/tools/map.py | 2 +- tests/test_map.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/meshterm/tools/map.py b/meshterm/tools/map.py index 8c81746..93c8150 100644 --- a/meshterm/tools/map.py +++ b/meshterm/tools/map.py @@ -181,7 +181,7 @@ class MapTool(Tool): True, "--basemap/--no-basemap", help="Draw the OpenStreetMap street basemap" ), ) -> None: - if not 0.0 < fraction <= 1.0: + if fraction is not None and not 0.0 < fraction <= 1.0: raise typer.BadParameter("--fraction must be greater than 0 and at most 1") params: dict[str, Any] = { "static": True, "basemap": basemap, "fraction": fraction, diff --git a/tests/test_map.py b/tests/test_map.py index 6d3bfc8..908b9b4 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -2414,3 +2414,45 @@ def test_each_road_class_keeps_its_own_colour_through_the_per_tile_style_memo() else: expected = _ROAD_STYLE.get(cls or "", _ROAD_DEFAULT) assert mark_rgb(expected[0]) in painted, f"{cls} lost its own colour" + + +def _run_map_cli(monkeypatch, *args: str): + """Invoke the ``map`` subcommand with the tool itself stubbed out. + + Returns the params dict the command would have handed :func:`run_tool_command`, + or ``None`` if the invocation failed before getting there. + """ + import typer + from typer.testing import CliRunner + + from meshterm import cli + + captured: dict = {} + monkeypatch.setattr( + cli, "run_tool_command", lambda tool, params: captured.update(params) + ) + app = typer.Typer() + MapTool().register_cli(app) + result = CliRunner().invoke(app, list(args)) + return result, (captured or None) + + +def test_map_cli_fraction_defaults_to_the_preference(monkeypatch) -> None: + """No ``--fraction`` means *ask the preferences*, which is a ``None`` to pass on. + + The bounds check used to compare that ``None`` against 0.0, so the plain + ``meshterm map`` — the one invocation nobody passes a flag to — was the one that + raised. + """ + result, params = _run_map_cli(monkeypatch) + assert result.exit_code == 0, result.output + assert params is not None and params["fraction"] is None + + +@pytest.mark.parametrize("value,ok", [("0", False), ("0.5", True), ("1.0", True)]) +def test_map_cli_fraction_bounds(monkeypatch, value: str, ok: bool) -> None: + """A fraction of zero frames nothing, so it is refused; anything up to 1.0 stands.""" + result, params = _run_map_cli(monkeypatch, "--fraction", value) + assert (result.exit_code == 0) is ok, result.output + if ok: + assert params is not None and params["fraction"] == float(value)