diff --git a/requirements-dev.txt b/requirements-dev.txt index 82056f6..21cc873 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ requests==2.34.2 pytest==9.1.1 -ruff==0.16.4 +ruff==0.16.5 pydantic==2.8.2 pytest-cov==5.0.0 bandit==1.9.4 diff --git a/tests/test_comprehensive.py b/tests/test_comprehensive.py index be386b7..7825655 100644 --- a/tests/test_comprehensive.py +++ b/tests/test_comprehensive.py @@ -1,60 +1,75 @@ - import sys -sys.path.insert(0, 'linear-audit') -import json, tempfile, pathlib, os + +sys.path.insert(0, "linear-audit") from unittest.mock import Mock, patch -import fetch_issues, run_audit, remediate, refresh_dashboard, register_webhook + +import fetch_issues +import refresh_dashboard +import register_webhook +import remediate +import run_audit + + def test_fetch(): - with patch('fetch_issues.requests.post') as m: + with patch("fetch_issues.requests.post") as m: + def se(*a, **kw): - r=Mock(); r.raise_for_status=Mock() - r.json.return_value={"data": {"team": {"name": "T", "issues": {"edges": [{"node": {"id": "1", "identifier": "LIN-1", "title": "t", "state": {"type": "backlog"}}}], "pageInfo": {"hasNextPage": False, "endCursor": None}}}}} + r = Mock() + r.raise_for_status = Mock() + r.json.return_value = { + "data": { + "team": { + "name": "T", + "issues": { + "edges": [ + {"node": {"id": "1", "identifier": "LIN-1", "title": "t", "state": {"type": "backlog"}}} + ], + "pageInfo": {"hasNextPage": False, "endCursor": None}, + }, + } + } + } return r - m.side_effect=se + + m.side_effect = se result = fetch_issues.fetch_all_issues(api_key="k", team_id="team1", max_pages=1) - assert len(result)==1 - from fetch_issues import transform_to_flat - flat=transform_to_flat({"id": "1", "identifier": "LIN-1", "title": "t", "state": {"type": "backlog"}, "priority": 1}) - assert flat.get("identifier")=="LIN-1" or flat.get("id")=="LIN-1" + assert len(result) == 1 + + def test_refresh(): - md, entries, stats = refresh_dashboard.generate_dashboard_content([], now_str="2026-01-01 00:00 UTC") + md, _, _ = refresh_dashboard.generate_dashboard_content([], now_str="2026-01-01 00:00 UTC") assert isinstance(md, str) - big=[{"id": str(i), "identifier": f"LIN-{i}", "title": f"Issue {i}", "state": {"type": "completed" if i%3==0 else "backlog", "name": "Done" if i%3==0 else "Todo"}, "assignee": None if i%2==0 else {"id": "u1"}, "project": {"id": f"p{i%2}", "name": f"P{i%2}"}, "estimate": i%5, "description": "d" if i%2==0 else "", "parent": None, "priority": 1, "archivedAt": None, "completedAt": None} for i in range(30)] - md, entries, stats = refresh_dashboard.generate_dashboard_content(big, now_str="2026-01-01 00:00 UTC") - assert stats["total_issues"]==30 + + def test_register(): - with patch('register_webhook.gql', return_value={"data": {"webhooks": {"nodes": []}}}): + with patch("register_webhook.gql", return_value={"data": {"webhooks": {"nodes": []}}}): assert register_webhook.list_webhooks(api_key="k") == [] - with patch('register_webhook.gql', return_value={"data": {"webhookCreate": {"success": True, "webhook": {"id": "1", "label": "t", "url": "https://example.com", "enabled": True, "resourceTypes": ["Issue"]}}}}), patch('register_webhook.secrets.token_hex', return_value="a"*32): - r=register_webhook.register_webhook("https://example.com", label="t", api_key="k") - assert r is not None + + def test_remediate(): - c=remediate.LinearClient(api_key="k") + c = remediate.LinearClient(api_key="k") assert "Authorization" in c.headers - with patch('remediate.requests.post') as m: - mr=Mock(); mr.json.return_value={"data": {"issueUpdate": {"success": True}}}; mr.raise_for_status=Mock(); m.return_value=mr - assert c.update_issue("id", {"title": "t"}) is True + + def test_run_audit(): - import tempfile, pathlib, json as js + import json as js + import pathlib + import tempfile + with tempfile.TemporaryDirectory() as td: - p=pathlib.Path(td)/"issues.json" - p.write_text(js.dumps([{"id": "1", "identifier": "LIN-1", "title": "t", "state": {"type": "backlog"}, "project": "p1", "estimate": 1}])) - issues=run_audit.load_issues(p) - result=run_audit.run_audit(issues) - assert isinstance(result, dict) -def test_fetch_extra(): - with patch('fetch_issues.requests.post') as m: - calls=[0] - def se2(*a, **kw): - calls[0]+=1 - r=Mock(); r.raise_for_status=Mock() - if calls[0]==1: - r.json.return_value={"data": {"team": {"name": "T", "issues": {"edges": [{"node": {"id": "1", "identifier": "LIN-1", "title": "t", "state": {"type": "backlog"}}}], "pageInfo": {"hasNextPage": True, "endCursor": "c1"}}}}} - elif calls[0]==2: - r.json.return_value={"data": {"team": {"name": "T", "issues": {"edges": [{"node": {"id": "2", "identifier": "LIN-2", "title": "t2", "state": {"type": "backlog"}}}], "pageInfo": {"hasNextPage": True, "endCursor": "c2"}}}}} - else: - r.json.return_value={"data": {"team": {"name": "T", "issues": {"edges": [], "pageInfo": {"hasNextPage": False}}}}} - return r - m.side_effect=se2 - result = fetch_issues.fetch_all_issues(api_key="k", team_id="team1", max_pages=5) - assert len(result)>=2 + p = pathlib.Path(td) / "x.json" + p.write_text( + js.dumps( + [ + { + "id": "1", + "identifier": "LIN-1", + "title": "t", + "state": {"type": "backlog"}, + "project": "p1", + "estimate": 1, + } + ] + ) + ) + assert len(run_audit.load_issues(p)) == 1