From 9bf8677bb92173339ee0e60c9865de3fcad8cba9 Mon Sep 17 00:00:00 2001 From: OneNobleSoul <75739931+OneNobleSoul@users.noreply.github.com> Date: Fri, 17 Jul 2026 06:04:23 +0200 Subject: [PATCH] catch config errors in run command instead of crashing cmd_check and cmd_send already wrap load_config in try/except and print a clean 'config error: ...' message, but cmd_run called load_config directly. a missing or malformed config file at 'hookrelay run -c ...' time (the normal way this thing gets started, e.g. from systemd) blew up with a raw ConfigError traceback instead of the same clean stderr message the other subcommands give. --- src/hookrelay/cli.py | 6 +++++- tests/test_cli.py | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/test_cli.py diff --git a/src/hookrelay/cli.py b/src/hookrelay/cli.py index 2300f2a..98f5278 100644 --- a/src/hookrelay/cli.py +++ b/src/hookrelay/cli.py @@ -25,7 +25,11 @@ def _read_data(spec: str | None) -> str: def cmd_run(args: argparse.Namespace) -> int: - config = load_config(args.config) + try: + config = load_config(args.config) + except ConfigError as e: + print(f"config error: {e}", file=sys.stderr) + return 1 logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s" ) diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..9f70c13 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,48 @@ +import json + +import hookrelay.cli as cli + +BASE_CONFIG = { + "routes": [ + { + "name": "r1", + "match": {"path": "/a"}, + "sinks": [{"type": "generic", "url": "http://example.invalid"}], + } + ] +} + + +def _write_config(tmp_path, data): + p = tmp_path / "config.json" + p.write_text(json.dumps(data), encoding="utf-8") + return str(p) + + +def test_run_missing_config_prints_clean_error(tmp_path, capsys): + missing = str(tmp_path / "does-not-exist.json") + rc = cli.main(["run", "-c", missing]) + assert rc == 1 + err = capsys.readouterr().err + assert "config error:" in err + assert "Traceback" not in err + + +def test_run_invalid_config_prints_clean_error(tmp_path, capsys): + # missing required 'routes' key + path = _write_config(tmp_path, {}) + rc = cli.main(["run", "-c", path]) + assert rc == 1 + err = capsys.readouterr().err + assert "config error:" in err + assert "Traceback" not in err + + +def test_run_valid_config_starts_server(tmp_path, monkeypatch): + path = _write_config(tmp_path, BASE_CONFIG) + calls = [] + monkeypatch.setattr(cli, "serve", lambda config: calls.append(config)) + rc = cli.main(["run", "-c", path]) + assert rc == 0 + assert len(calls) == 1 + assert calls[0].routes[0].name == "r1"