#!/usr/bin/env python3 """Tests for pr_triage.py — issue #659.""" import json import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts")) from pr_triage import categorize, refs, find_dupes, find_stale, to_markdown, to_json class TestCategorize: def test_training_data_pairs(self): assert categorize("feat: 500 emotional weather pairs (#603)") == "training_data" def test_training_data_scene(self): assert categorize("feat: 100 jazz scene descriptions (#612)") == "training_data" def test_training_data_corpus(self): assert categorize("Add crisis manipulation corpus (#598)") == "training_data" def test_bug_fix(self): assert categorize("fix: broken import in cli.py") == "bug_fix" def test_bug_resolve(self): assert categorize("resolve: memory leak in session store") == "bug_fix" def test_feature(self): assert categorize("feat: add token budget tracker") == "feature" def test_feature_new(self): assert categorize("new: nightly pipeline scheduler") == "feature" def test_docs(self): assert categorize("docs: update README config format") == "docs" def test_ops(self): assert categorize("ops: deploy config to Ezra VPS") == "ops" def test_ops_ci(self): assert categorize("ci: add smoke test workflow") == "ops" def test_security(self): assert categorize("security: fix XSS in gallery panel") == "security" def test_other(self): assert categorize("chore: cleanup whitespace") == "other" def test_empty(self): assert categorize("") == "other" def test_none(self): assert categorize(None) == "other" def test_case_insensitive(self): assert categorize("FIX: Resolve import error") == "bug_fix" class TestRefs: def test_single(self): assert refs({"title": "Fix #123", "body": ""}) == [123] def test_multiple(self): assert refs({"title": "#10", "body": "Related to #20 and #30"}) == [10, 20, 30] def test_dedup(self): assert refs({"title": "#100", "body": "Closes #100"}) == [100] def test_none(self): assert refs({"title": "No refs", "body": ""}) == [] def test_body_only(self): assert refs({"title": "Fix", "body": "Closes #42"}) == [42] def test_null_body(self): assert refs({"title": "#7", "body": None}) == [7] class TestFindDupes: def test_no_dupes(self): prs = [{"number": 1, "title": "#10", "body": ""}, {"number": 2, "title": "#11", "body": ""}] assert find_dupes(prs) == {} def test_duplicate(self): prs = [{"number": 1, "title": "#10", "body": ""}, {"number": 2, "title": "#10", "body": ""}] d = find_dupes(prs) assert d[10] == [1, 2] def test_triple(self): prs = [{"number": i, "title": "#42", "body": ""} for i in range(1, 4)] d = find_dupes(prs) assert len(d[42]) == 3 def test_partial_overlap(self): prs = [{"number": 1, "title": "#10 #20", "body": ""}, {"number": 2, "title": "#10", "body": ""}] d = find_dupes(prs) assert 10 in d assert 20 not in d class TestFindStale: def test_clean(self): prs = [{"number": 1, "title": "#10", "body": ""}] assert find_stale(prs, set()) == [] def test_stale(self): prs = [{"number": 1, "title": "#10", "body": ""}] s = find_stale(prs, {10}) assert len(s) == 1 assert s[0]["stale_refs"] == [10] def test_mixed(self): prs = [{"number": 1, "title": "#10 #20", "body": ""}] s = find_stale(prs, {10}) assert s[0]["stale_refs"] == [10] def test_multiple_prs(self): prs = [ {"number": 1, "title": "#10", "body": ""}, {"number": 2, "title": "#20", "body": ""}, ] s = find_stale(prs, {10, 20}) assert len(s) == 2 class TestToMarkdown: def test_basic_structure(self): a = { "repo": "test/repo", "total_open": 3, "total_files_changed": 10, "total_additions": 100, "total_deletions": 20, "categories": {"feature": 2, "bug_fix": 1}, "category_details": { "feature": [{"number": 1, "title": "feat: x", "refs": [], "head": "f1", "files": 2, "created": "2026-04-01"}], "bug_fix": [], }, "duplicates": {}, "stale_prs": [], "closed_issues_checked": 50, "safe_merge_candidates": 0, "timestamp": "2026-04-14T12:00:00Z", } md = to_markdown(a) assert "test/repo" in md assert "3" in md assert "feature" in md assert "## PR Triage Report" in md def test_duplicates_section(self): a = {"repo": "x", "total_open": 2, "total_files_changed": 0, "total_additions": 0, "total_deletions": 0, "categories": {}, "category_details": {}, "duplicates": {42: [1, 2]}, "stale_prs": [], "closed_issues_checked": 0, "safe_merge_candidates": 0, "timestamp": "2026-01-01"} md = to_markdown(a) assert "Duplicate" in md assert "#42" in md def test_stale_section(self): a = {"repo": "x", "total_open": 1, "total_files_changed": 0, "total_additions": 0, "total_deletions": 0, "categories": {}, "category_details": {}, "duplicates": {}, "stale_prs": [{"pr": 5, "title": "old fix", "stale_refs": [10]}], "closed_issues_checked": 50, "safe_merge_candidates": 0, "timestamp": "2026-01-01"} md = to_markdown(a) assert "#5" in md assert "Stale" in md class TestToJson: def test_roundtrip(self): a = {"repo": "test", "total_open": 0} out = to_json(a) assert json.loads(out)["repo"] == "test" def test_complex(self): a = {"repo": "x", "duplicates": {1: [2, 3]}, "stale_prs": []} out = to_json(a) d = json.loads(out) assert d["duplicates"]["1"] == [2, 3]