77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
|
|
from pathlib import Path
|
||
|
|
import sys
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||
|
|
|
||
|
|
from app.modules.sag.backend import solutions
|
||
|
|
|
||
|
|
|
||
|
|
def test_solution_payload_normalizes_legacy_quick_action_values():
|
||
|
|
payload = solutions._normalize_payload({
|
||
|
|
"title": " Outlook virker igen ",
|
||
|
|
"solution_type": "standard",
|
||
|
|
"result": "resolved",
|
||
|
|
"visibility": "general",
|
||
|
|
"tags": ["Outlook", " outlook ", "Microsoft 365"],
|
||
|
|
})
|
||
|
|
assert payload["title"] == "Outlook virker igen"
|
||
|
|
assert payload["solution_type"] == "Support"
|
||
|
|
assert payload["result"] == "Løst"
|
||
|
|
assert payload["tags"] == ["Outlook", "Microsoft 365"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_solution_payload_rejects_empty_title():
|
||
|
|
with pytest.raises(HTTPException) as exc:
|
||
|
|
solutions._normalize_payload({"title": " "})
|
||
|
|
assert exc.value.status_code == 422
|
||
|
|
|
||
|
|
|
||
|
|
def test_solution_payload_rejects_unknown_visibility():
|
||
|
|
with pytest.raises(HTTPException) as exc:
|
||
|
|
solutions._normalize_payload({"visibility": "public-internet"})
|
||
|
|
assert exc.value.status_code == 422
|
||
|
|
|
||
|
|
|
||
|
|
def test_ai_context_redacts_credentials_before_processing():
|
||
|
|
cleaned, warnings = solutions._redact_sensitive(
|
||
|
|
"Login til router: password=SuperSecret og Authorization: Bearer abc.def.ghi"
|
||
|
|
)
|
||
|
|
assert "SuperSecret" not in cleaned
|
||
|
|
assert "abc.def.ghi" not in cleaned
|
||
|
|
assert len(warnings) == 2
|
||
|
|
|
||
|
|
|
||
|
|
def test_knowledge_tokens_are_stable_and_remove_fill_words():
|
||
|
|
assert solutions._knowledge_tokens("Outlook kan ikke synkronisere Outlook med Microsoft 365") == [
|
||
|
|
"outlook", "synkronisere", "microsoft", "365"
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def test_ai_source_references_accept_common_model_variants():
|
||
|
|
refs = solutions._normalize_source_refs(
|
||
|
|
"[Sag #135], Kommentar #12; Artikel 4",
|
||
|
|
{"Sag 135", "Kommentar 12", "Artikel 4"},
|
||
|
|
135,
|
||
|
|
)
|
||
|
|
assert refs == ["Sag 135", "Kommentar 12", "Artikel 4"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_customer_articles_are_excluded_without_customer_scope(monkeypatch):
|
||
|
|
captured = []
|
||
|
|
|
||
|
|
def fake_single(query, params=None):
|
||
|
|
captured.append((query, params))
|
||
|
|
return {"total": 0}
|
||
|
|
|
||
|
|
monkeypatch.setattr(solutions, "execute_query_single", fake_single)
|
||
|
|
monkeypatch.setattr(solutions, "execute_query", lambda query, params=None: [])
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
result = asyncio.run(solutions.search_knowledge_articles(q="vpn", customer_id=None, _current_user={"id": 1}))
|
||
|
|
assert result["total"] == 0
|
||
|
|
assert "ka.visibility IN ('general','internal')" in captured[0][0]
|
||
|
|
assert "visibility='customer'" not in captured[0][0]
|