diff --git a/backend/ai.py b/backend/ai.py index d16befa..53f8115 100644 --- a/backend/ai.py +++ b/backend/ai.py @@ -125,7 +125,8 @@ def _extract_json(text: str) -> dict[str, Any]: def suggest_for_ticket(ticket_id: str) -> dict[str, Any]: ticket = _fetch_ticket(ticket_id) notes = _fetch_notes(ticket_id) - history = _fetch_recent_resolved(30) + # A Resolved/Closed ticket must not be offered as a "similar past ticket" to itself. + history = [t for t in _fetch_recent_resolved(30) if t.get("id") != ticket.get("id")] if not history: return {"matches": [], "note": "No resolved tickets available for comparison."} diff --git a/backend/test_ai_suggest.py b/backend/test_ai_suggest.py new file mode 100644 index 0000000..fed1ab2 --- /dev/null +++ b/backend/test_ai_suggest.py @@ -0,0 +1,53 @@ +"""Tests for ai.suggest_for_ticket (the real ai module, not the stub used in test_main).""" + +import importlib +import sys +from unittest.mock import MagicMock + +import pytest + + +@pytest.fixture +def ai(monkeypatch): + sys.modules.pop("ai", None) + module = importlib.import_module("ai") + yield module + sys.modules.pop("ai", None) + + +def test_suggest_excludes_the_ticket_itself(ai, monkeypatch): + target = {"id": "t1", "title": "Printer jam", "status": "Closed"} + other = {"id": "t2", "title": "Paper jam", "status": "Resolved"} + monkeypatch.setattr(ai, "_fetch_ticket", lambda _id: target) + monkeypatch.setattr(ai, "_fetch_notes", lambda _id: []) + monkeypatch.setattr(ai, "_fetch_recent_resolved", lambda limit=30: [dict(target), dict(other)]) + + captured = {} + + def fake_build(ticket, notes, history): + captured["history"] = history + return "prompt" + + monkeypatch.setattr(ai, "_build_prompt", fake_build) + client = MagicMock() + client.messages.create.return_value = MagicMock( + content=[MagicMock(type="text", text='{"matches": []}')] + ) + monkeypatch.setattr(ai, "_get_client", lambda: client) + + ai.suggest_for_ticket("t1") + + assert [t["id"] for t in captured["history"]] == ["t2"] + + +def test_suggest_with_only_itself_in_history_returns_note(ai, monkeypatch): + target = {"id": "t1", "title": "Printer jam", "status": "Closed"} + monkeypatch.setattr(ai, "_fetch_ticket", lambda _id: target) + monkeypatch.setattr(ai, "_fetch_notes", lambda _id: []) + monkeypatch.setattr(ai, "_fetch_recent_resolved", lambda limit=30: [dict(target)]) + monkeypatch.setattr(ai, "_get_client", lambda: pytest.fail("model must not be called")) + + result = ai.suggest_for_ticket("t1") + + assert result["matches"] == [] + assert "No resolved tickets" in result["note"]