"""Error-body mining into the digest (WP-0006 T01).""" import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from session_memory.core.digest import ( # noqa: E402 _error_fingerprint, _error_snippets, build_digest, ) from session_memory.core.schema import SCHEMA_VERSION, Session, SessionEvent # noqa: E402 def _ev(seq, kind, **kw): return SessionEvent(session_uid="claude:s", seq=seq, kind=kind, **kw) def test_fingerprint_normalizes_paths_numbers_ids(): a = _error_fingerprint("ModuleNotFoundError: No module named 'foo' at /home/x/a.py:42") b = _error_fingerprint("ModuleNotFoundError: No module named 'foo' at /srv/y/b.py:9991") assert a == b # paths + line numbers stripped -> same fingerprint assert "" in a and "" in a def test_fingerprint_uuid_and_addr(): fp = _error_fingerprint("connection 0xDEADBEEF to 1972d1d9-fc35-4912-8126-1fe64cc51425 failed") assert "" in fp and "" in fp def test_snippets_dedup_and_count(): blobs = {"b1": "Traceback...\nValueError: bad thing at /p/x.py:10", "b2": "Traceback...\nValueError: bad thing at /q/y.py:99", "b3": "KeyError: 'id'"} events = [ _ev(0, "error", payload_ref="b1"), _ev(1, "error", payload_ref="b2"), # same fingerprint as b1 _ev(2, "error", payload_ref="b3"), ] snips = _error_snippets(events, blobs) assert len(snips) == 2 top = snips[0] assert top["count"] == 2 # the ValueError collapsed assert "ValueError" in top["sample"] def test_failed_tool_result_mined(): blobs = {"b1": "npm ERR! something failed with non-zero exit"} events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")] snips = _error_snippets(events, blobs) assert len(snips) == 1 assert snips[0]["tool"] == "Bash" def test_clean_tool_result_not_mined(): blobs = {"b1": "6 passed in 0.4s"} events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")] assert _error_snippets(events, blobs) == [] def test_build_digest_includes_error_snippets_and_v2(): s = Session(session_uid="claude:s", flavor="claude", native_session_id="s", repo="r") events = [_ev(0, "user_msg"), _ev(1, "error", payload_ref="b1"), _ev(2, "assistant_msg")] d = build_digest(s, events, {"b1": "RuntimeError: kaboom at /a/b.py:3"}) assert d["schema_version"] == SCHEMA_VERSION == 2 assert d["error_snippets"][0]["count"] == 1 assert "RuntimeError" in d["error_snippets"][0]["sample"] def test_no_errors_empty_list(): s = Session(session_uid="claude:s", flavor="claude", native_session_id="s", repo="r") d = build_digest(s, [_ev(0, "user_msg"), _ev(1, "assistant_msg")]) assert d["error_snippets"] == []