From 5996f3ddef14aa9d6fb5503c55b160adb77db9b3 Mon Sep 17 00:00:00 2001 From: waldo121 <17034517+waldo121@users.noreply.github.com> Date: Thu, 2 Jul 2026 11:19:54 -0400 Subject: [PATCH] fix: handle malformed compare JSON gracefully - Add error handling for invalid JSON and schema in compare files - Add tests for malformed JSON, invalid schema, and empty compare files --- oracletrace/cli.py | 18 +++++++++++++-- tests/test_cli.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/oracletrace/cli.py b/oracletrace/cli.py index 0b60018..e303f4b 100644 --- a/oracletrace/cli.py +++ b/oracletrace/cli.py @@ -11,6 +11,7 @@ from importlib.metadata import version from argparse import ArgumentParser, Namespace from typing import List, Dict, Any, Optional, Tuple +from json import JSONDecodeError from .reporters import generate_html_report from .compare import compare_traces, ComparisonData @@ -168,8 +169,21 @@ def _compare_and_fail( print(f"Compare file not found: {args.compare}", file=sys.stderr) return 1 - with open(args.compare, "r", encoding="utf-8") as f: - old_data: TracerData = TracerData.from_dict(json.load(f)) + try: + with open(args.compare, "r", encoding="utf-8") as f: + loaded_data = json.load(f) + except JSONDecodeError as e: + print(f"Compare file contains invalid JSON: {e}", file=sys.stderr) + return 1 + except OSError as e: + print(f"Compare file read error: {e}", file=sys.stderr) + return 1 + + try: + old_data: TracerData = TracerData.from_dict(loaded_data) + except (KeyError, TypeError, ValueError) as e: + print(f"Compare file has invalid schema: {e}", file=sys.stderr) + return 1 comparison_result: ComparisonData = compare_traces( old_data, diff --git a/tests/test_cli.py b/tests/test_cli.py index f38bdfe..aec4325 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -374,6 +374,63 @@ def test_main_returns_1_when_compare_file_not_found(monkeypatch, tmp_path, trace assert "Compare file not found:" in captured.err +def test_main_returns_1_when_compare_file_has_invalid_json(monkeypatch, tmp_path, trace_data, capsys): + target = tmp_path / "target.py" + target.write_text("print('hello')\n", encoding="utf-8") + compare_file = tmp_path / "baseline.json" + compare_file.write_text("{invalid json", encoding="utf-8") + + monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data)) + monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None) + + exit_code = _run_cli( + monkeypatch, + ["oracletrace", str(target), "--compare", str(compare_file)], + ) + + captured = capsys.readouterr() + assert exit_code == 1 + assert "Compare file contains invalid JSON:" in captured.err + + +def test_main_returns_1_when_compare_file_has_invalid_schema(monkeypatch, tmp_path, trace_data, capsys): + target = tmp_path / "target.py" + target.write_text("print('hello')\n", encoding="utf-8") + compare_file = tmp_path / "baseline.json" + compare_file.write_text('{"foo": "bar"}', encoding="utf-8") + + monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data)) + monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None) + + exit_code = _run_cli( + monkeypatch, + ["oracletrace", str(target), "--compare", str(compare_file)], + ) + + captured = capsys.readouterr() + assert exit_code == 1 + assert "Compare file has invalid schema:" in captured.err + + +def test_main_returns_1_when_compare_file_empty(monkeypatch, tmp_path, trace_data, capsys): + target = tmp_path / "target.py" + target.write_text("print('hello')\n", encoding="utf-8") + compare_file = tmp_path / "baseline.json" + compare_file.write_text("", encoding="utf-8") + + monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data)) + monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None) + + exit_code = _run_cli( + monkeypatch, + ["oracletrace", str(target), "--compare", str(compare_file)], + ) + + captured = capsys.readouterr() + assert exit_code == 1 + assert "Compare file contains invalid JSON" in captured.err + + def test_main_fails_with_exit_2_on_regression(monkeypatch, tmp_path, empty_trace_data, capsys): target = tmp_path / "target.py" target.write_text("print('hello')\n", encoding="utf-8")