"""Tests for the new heuristics added in scripts/audit_exception_handling.py. Each test creates a fixture file with a specific pattern and runs the audit script against it, verifying the pattern is classified correctly. The new heuristics (added by result_migration_review_pass_20260617): 1. list.index with ValueError fallback to default index (INTERNAL_COMPLIANT) 2. dict lookup (KeyError) with default value (INTERNAL_COMPLIANT) 3. datetime.fromisoformat(s) with ValueError: None (INTERNAL_COMPLIANT) 4. Path.resolve(strict=True) with OSError/ValueError fallback (INTERNAL_COMPLIANT) 5. Path.relative_to with ValueError: pass (INTERNAL_COMPLIANT) 6. asyncio.get_running_loop() with RuntimeError: asyncio.run() (INTERNAL_COMPLIANT) 7. Narrow except (ImportError, AttributeError) + fallback stub (INTERNAL_COMPLIANT) 8. raise NotImplementedError() as entire function body (INTERNAL_PROGRAMMER_RAISE) 9. if None: raise ImportError() validation pattern (INTERNAL_PROGRAMMER_RAISE) 10. try/except (json.JSONDecodeError, KeyError) around JSON parse + print + return (INTERNAL_COMPLIANT) """ from __future__ import annotations import json import subprocess import sys import textwrap from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts" / "audit_exception_handling.py" def _run_audit_on_fixture(source: str) -> dict: """Run the audit script on a fixture and return the parsed JSON output.""" # Use a temp dir outside tests/artifacts/ to avoid the audit's # default artifacts-exclusion filter. import tempfile tmpdir = Path(tempfile.mkdtemp(prefix="audit_fixture_")) fixture = tmpdir / "audit_heuristic_fixture.py" fixture.write_text(textwrap.dedent(source), encoding="utf-8") try: result = subprocess.run( [sys.executable, str(SCRIPT), "--json", "--src", str(tmpdir), "--verbose"], capture_output=True, text=True, check=False, cwd=str(ROOT), ) finally: if fixture.exists(): fixture.unlink() if tmpdir.exists(): tmpdir.rmdir() if result.returncode not in (0, 1): raise RuntimeError(f"audit failed: {result.stderr}") return json.loads(result.stdout) def _classifications_for_file(data: dict, filename_suffix: str) -> list[dict]: """Return all findings for files whose path ends with `filename_suffix`.""" return [ f for file_info in data.get("files", []) for f in file_info.get("findings", []) if file_info["filename"].endswith(filename_suffix) ] # --------------------------------------------------------------------------- # Heuristic 1: list.index with ValueError fallback to default index # --------------------------------------------------------------------------- def test_list_index_valueerror_fallback_is_compliant(): """try: list.index(x); except ValueError: idx = N is compliant.""" src = ''' def func(items, target): try: idx = items.index(target) except ValueError: idx = 0 return idx ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1, f"expected 1 except, got {len(excepts)}: {excepts}" assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"list.index+ValueError fallback should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 2: KeyError lookup with default value # --------------------------------------------------------------------------- def test_keyerror_lookup_with_default_is_compliant(): """try: dict[x]; except KeyError: val = default is compliant.""" src = ''' def func(d, key): try: val = d[key] except KeyError: val = "default" return val ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"KeyError+default should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 3: datetime.fromisoformat(s) with ValueError: None # --------------------------------------------------------------------------- def test_fromisoformat_valueerror_none_is_compliant(): """try: datetime.fromisoformat(s); except ValueError: x = None is compliant.""" src = ''' import datetime def func(s): try: d = datetime.datetime.fromisoformat(s) except ValueError: d = None return d ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"fromisoformat+ValueError:None should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 4: Path.resolve(strict=True) with OSError/ValueError fallback # --------------------------------------------------------------------------- def test_path_resolve_strict_fallback_is_compliant(): """try: Path(p).resolve(strict=True); except (OSError, ValueError): Path(p).resolve() is compliant.""" src = ''' from pathlib import Path def func(p): try: rp = Path(p).resolve(strict=True) except (OSError, ValueError): rp = Path(p).resolve() return rp ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"Path.resolve(strict=True)+fallback should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 5: Path.relative_to with ValueError: pass # --------------------------------------------------------------------------- def test_path_relative_to_valueerror_is_compliant(): """try: rp.relative_to(base); except ValueError: pass is compliant (canonical subpath check).""" src = ''' from pathlib import Path def is_subpath(rp, base): try: rp.relative_to(base) return True except ValueError: return False ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"Path.relative_to+ValueError should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 6: asyncio.get_running_loop() with RuntimeError: asyncio.run() # --------------------------------------------------------------------------- def test_asyncio_get_running_loop_fallback_is_compliant(): """try: get_running_loop(); except RuntimeError: asyncio.run() is compliant.""" src = ''' import asyncio def bridge(coro): try: loop = asyncio.get_running_loop() return loop except RuntimeError: return asyncio.run(coro) ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"get_running_loop+RuntimeError fallback should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 7: narrow except (ImportError, AttributeError) + fallback stub # --------------------------------------------------------------------------- def test_import_attr_fallback_stub_is_compliant(): """narrow except (ImportError, AttributeError) with fallback attribute assignment is compliant.""" src = ''' class Stub: available = False def get_thing(): try: import real_module return real_module except (ImportError, ModuleNotFoundError, AttributeError): return Stub() ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 1 assert excepts[0]["category"] == "INTERNAL_COMPLIANT", ( f"narrow except + fallback stub should be INTERNAL_COMPLIANT, got {excepts[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 8: raise NotImplementedError() as entire function body # --------------------------------------------------------------------------- def test_raise_notimplemented_entire_body_is_programmer_error(): """raise NotImplementedError() as the entire function body is INTERNAL_PROGRAMMER_RAISE.""" src = ''' class Base: def abstract_method(self): raise NotImplementedError() ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") raises = [f for f in findings if f["kind"] == "RAISE"] assert len(raises) == 1 assert raises[0]["category"] == "INTERNAL_PROGRAMMER_RAISE", ( f"raise NotImplementedError() in entire body should be INTERNAL_PROGRAMMER_RAISE, got {raises[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 9: if None: raise ImportError() validation # --------------------------------------------------------------------------- def test_validation_raise_is_programmer_error(): """if is None: raise ImportError() is INTERNAL_PROGRAMMER_RAISE.""" src = ''' def func(dep): if dep is None: raise ImportError("dependency missing") return dep ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") raises = [f for f in findings if f["kind"] == "RAISE"] assert len(raises) == 1 assert raises[0]["category"] == "INTERNAL_PROGRAMMER_RAISE", ( f"validation raise should be INTERNAL_PROGRAMMER_RAISE, got {raises[0]['category']}" ) # --------------------------------------------------------------------------- # Heuristic 10: try/except (json.JSONDecodeError, KeyError) + print + return # --------------------------------------------------------------------------- def test_json_parse_with_print_is_compliant(): """try/except (json.JSONDecodeError, KeyError) around JSON parse with print is compliant.""" src = ''' import json def parse(s): try: data = json.loads(s) if not isinstance(data, list): print("not a list") return return data except json.JSONDecodeError as e: print(f"json error: {e}") except KeyError as e: print(f"missing key: {e}") ''' data = _run_audit_on_fixture(src) findings = _classifications_for_file(data, "audit_heuristic_fixture.py") excepts = [f for f in findings if f["kind"] == "EXCEPT"] assert len(excepts) == 2 for e in excepts: assert e["category"] == "INTERNAL_COMPLIANT", ( f"json parse with print should be INTERNAL_COMPLIANT, got {e['category']}" )