diff --git a/examples/diagrams/analyze_bi.py b/examples/diagrams/analyze_bi.py index 837b62e5..d6c1b76b 100644 --- a/examples/diagrams/analyze_bi.py +++ b/examples/diagrams/analyze_bi.py @@ -8,7 +8,6 @@ import json import re -import nbformat from collections import defaultdict from pathlib import Path from datetime import datetime @@ -61,8 +60,10 @@ def parse_all(): with open(lf) as f: for line in f: if line.strip(): - try: events.append(json.loads(line)) - except: pass + try: + events.append(json.loads(line)) + except json.JSONDecodeError: + pass if not events: continue @@ -134,6 +135,8 @@ def parse_all(): # Build notebook # --------------------------------------------------------------------------- def build_notebook(records): + import nbformat # optional dependency, only needed for notebook generation + nb = nbformat.v4.new_notebook() data_json = json.dumps(records, indent=2) diff --git a/tests/test_diagrams_analyze_bi.py b/tests/test_diagrams_analyze_bi.py new file mode 100644 index 00000000..0a653d03 --- /dev/null +++ b/tests/test_diagrams_analyze_bi.py @@ -0,0 +1,56 @@ +"""Regression tests for the BI analysis diagram script.""" + +import importlib.util +import json +import sys +from pathlib import Path + +import pytest + + +def _load_analyze_bi(): + spec = importlib.util.spec_from_file_location( + "analyze_bi", + Path(__file__).parent.parent / "examples" / "diagrams" / "analyze_bi.py", + ) + module = importlib.util.module_from_spec(spec) + sys.modules["analyze_bi"] = module + spec.loader.exec_module(module) + return module + + +def test_parse_all_skips_invalid_jsonl_lines(tmp_path, monkeypatch): + """Malformed JSON lines should be ignored instead of crashing the parser.""" + module = _load_analyze_bi() + + log_file = tmp_path / "cat-issue-iter1.jsonl" + log_file.write_text( + json.dumps({"event": "start", "ts": 1.0, "model": "test"}) + "\n" + + "this is not valid json\n" + + json.dumps({"event": "end", "ts": 2.0}) + "\n" + ) + monkeypatch.setattr(module, "LOG_DIR", tmp_path) + + records = module.parse_all() + + assert len(records) == 1 + assert records[0]["model"] == "test" + + +def test_parse_all_propagates_non_json_exceptions(tmp_path, monkeypatch): + """Non-JSON exceptions must propagate, not be swallowed by bare except.""" + module = _load_analyze_bi() + + log_file = tmp_path / "cat-issue-iter1.jsonl" + log_file.write_text( + json.dumps({"event": "start", "ts": 1.0, "model": "test"}) + "\n" + ) + monkeypatch.setattr(module, "LOG_DIR", tmp_path) + + def raise_value_error(*args, **kwargs): + raise ValueError("simulated non-JSON failure") + + monkeypatch.setattr(module.json, "loads", raise_value_error) + + with pytest.raises(ValueError, match="simulated non-JSON failure"): + module.parse_all()