"""Tests für Index-Bootstrap sowie Interaktions- und Bewertungsprotokoll.""" from __future__ import annotations import dataclasses import pytest from agent.audit import AuditStore from agent.bootstrap import ensure_index, index_is_ready from agent.config import Config def interaction_payload() -> dict: return { "api_version": "v1", "request_id": "test-request-1", "status": "answered", "question": "Was gilt?", "answer": "Belegte Antwort [lb-min-01].", "refused": False, "verified": True, "citations": ["lb-min-01"], "sources": [{"id": "lb-min-01", "title": "Quelle"}], "conflicts": [], "planned_queries": [{"text": "Was gilt?"}], "model": "test-model", "latency_ms": 42, "n_context": 1, "regenerations": 0, } def test_audit_persists_interaction_and_rating(tmp_path): cfg = Config( audit_enabled=True, audit_db_path=str(tmp_path / "audit.db"), audit_retention_days=30, ) store = AuditStore(cfg) try: store.record_interaction(interaction_payload()) store.record_rating("test-request-1", "down", "Quelle war nicht passend") first_comment_id = store.record_comment( "test-request-1", "Bitte mit einer anderen Quelle prüfen." ) second_comment_id = store.record_comment( "test-request-1", "Der Stand ist für mich besonders wichtig." ) # Retry mit derselben Request-ID aktualisiert die Interaktion, ohne ihre # bereits gespeicherten Bewertungen oder Kommentare zu löschen. store.record_interaction(interaction_payload()) rows = store.recent() finally: store.close() assert len(rows) == 1 assert rows[0]["question"] == "Was gilt?" assert rows[0]["answer"].startswith("Belegte Antwort") assert rows[0]["citations"] == ["lb-min-01"] assert rows[0]["rating"] == "down" assert rows[0]["feedback"] == "Quelle war nicht passend" assert [item["comment_id"] for item in rows[0]["comments"]] == [ first_comment_id, second_comment_id, ] assert rows[0]["comments"][0]["comment"].startswith("Bitte mit") def test_audit_can_omit_free_text_and_still_log_metadata(tmp_path, capsys): cfg = Config( audit_enabled=True, audit_db_path=str(tmp_path / "audit.db"), audit_log_content=False, audit_stdout=True, ) store = AuditStore(cfg) try: store.record_interaction(interaction_payload()) store.record_rating("test-request-1", "up", "soll nicht gespeichert werden") store.record_comment("test-request-1", "auch dieser Kommentar ist privat") row = store.recent()[0] finally: store.close() output = capsys.readouterr().out assert row["question"] is None assert row["answer"] is None assert row["feedback"] is None assert row["comments"][0]["comment"] is None assert "Was gilt?" not in output assert "soll nicht gespeichert werden" not in output assert "auch dieser Kommentar ist privat" not in output assert '"event": "agent_interaction"' in output assert '"event": "agent_rating"' in output assert '"event": "agent_comment"' in output def test_rating_requires_existing_interaction(tmp_path): store = AuditStore(Config(audit_db_path=str(tmp_path / "audit.db"))) try: with pytest.raises(KeyError): store.record_rating("missing", "up", None) with pytest.raises(KeyError): store.record_comment("missing", "Kommentar") finally: store.close() def test_bootstrap_recognizes_existing_mini_index(mini_index): assert index_is_ready(mini_index) is True dense_cfg = dataclasses.replace(mini_index, embed_off=False) assert index_is_ready(dense_cfg) is False def test_bootstrap_builds_missing_index(mini_cfg, tmp_path): cfg = dataclasses.replace(mini_cfg, db_path=str(tmp_path / "new-index.db")) assert index_is_ready(cfg) is False result = ensure_index(cfg) assert result["event"] == "index_bootstrap_completed" assert index_is_ready(cfg) is True assert ensure_index(cfg)["event"] == "index_ready"