A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
297 lines
9.4 KiB
Python
297 lines
9.4 KiB
Python
"""
|
|
Tests für RelevanceProfile Model.
|
|
"""
|
|
|
|
import pytest
|
|
from datetime import datetime
|
|
|
|
from alerts_agent.models.relevance_profile import RelevanceProfile, PriorityItem
|
|
|
|
|
|
class TestPriorityItem:
|
|
"""Tests für PriorityItem."""
|
|
|
|
def test_create_minimal(self):
|
|
"""Test minimale Erstellung."""
|
|
item = PriorityItem(label="Test Topic")
|
|
|
|
assert item.label == "Test Topic"
|
|
assert item.weight == 0.5 # Default
|
|
assert item.keywords == []
|
|
assert item.description is None
|
|
|
|
def test_create_full(self):
|
|
"""Test vollständige Erstellung."""
|
|
item = PriorityItem(
|
|
label="Inklusion",
|
|
weight=0.9,
|
|
keywords=["inklusiv", "Förderbedarf"],
|
|
description="Inklusive Bildung in Schulen",
|
|
)
|
|
|
|
assert item.label == "Inklusion"
|
|
assert item.weight == 0.9
|
|
assert "inklusiv" in item.keywords
|
|
assert item.description is not None
|
|
|
|
def test_to_dict(self):
|
|
"""Test Serialisierung."""
|
|
item = PriorityItem(label="Test", weight=0.8, keywords=["kw1", "kw2"])
|
|
data = item.to_dict()
|
|
|
|
assert data["label"] == "Test"
|
|
assert data["weight"] == 0.8
|
|
assert data["keywords"] == ["kw1", "kw2"]
|
|
|
|
def test_from_dict(self):
|
|
"""Test Deserialisierung."""
|
|
data = {"label": "Test", "weight": 0.7, "keywords": ["test"]}
|
|
item = PriorityItem.from_dict(data)
|
|
|
|
assert item.label == "Test"
|
|
assert item.weight == 0.7
|
|
|
|
|
|
class TestRelevanceProfile:
|
|
"""Tests für RelevanceProfile."""
|
|
|
|
def test_create_empty(self):
|
|
"""Test leeres Profil."""
|
|
profile = RelevanceProfile()
|
|
|
|
assert profile.id is not None
|
|
assert profile.priorities == []
|
|
assert profile.exclusions == []
|
|
assert profile.positive_examples == []
|
|
assert profile.negative_examples == []
|
|
|
|
def test_add_priority(self):
|
|
"""Test Priorität hinzufügen."""
|
|
profile = RelevanceProfile()
|
|
profile.add_priority("Datenschutz", weight=0.85)
|
|
|
|
assert len(profile.priorities) == 1
|
|
assert profile.priorities[0].label == "Datenschutz"
|
|
assert profile.priorities[0].weight == 0.85
|
|
|
|
def test_add_exclusion(self):
|
|
"""Test Ausschluss hinzufügen."""
|
|
profile = RelevanceProfile()
|
|
profile.add_exclusion("Stellenanzeige")
|
|
profile.add_exclusion("Werbung")
|
|
|
|
assert len(profile.exclusions) == 2
|
|
assert "Stellenanzeige" in profile.exclusions
|
|
assert "Werbung" in profile.exclusions
|
|
|
|
def test_no_duplicate_exclusions(self):
|
|
"""Test keine doppelten Ausschlüsse."""
|
|
profile = RelevanceProfile()
|
|
profile.add_exclusion("Test")
|
|
profile.add_exclusion("Test")
|
|
|
|
assert len(profile.exclusions) == 1
|
|
|
|
def test_add_positive_example(self):
|
|
"""Test positives Beispiel hinzufügen."""
|
|
profile = RelevanceProfile()
|
|
profile.add_positive_example(
|
|
title="Gutes Beispiel",
|
|
url="https://example.com",
|
|
reason="Relevant für Thema X",
|
|
)
|
|
|
|
assert len(profile.positive_examples) == 1
|
|
assert profile.positive_examples[0]["title"] == "Gutes Beispiel"
|
|
assert profile.positive_examples[0]["reason"] == "Relevant für Thema X"
|
|
|
|
def test_add_negative_example(self):
|
|
"""Test negatives Beispiel hinzufügen."""
|
|
profile = RelevanceProfile()
|
|
profile.add_negative_example(
|
|
title="Schlechtes Beispiel",
|
|
url="https://example.com",
|
|
reason="Werbung",
|
|
)
|
|
|
|
assert len(profile.negative_examples) == 1
|
|
|
|
def test_examples_limited_to_20(self):
|
|
"""Test Beispiele auf 20 begrenzt."""
|
|
profile = RelevanceProfile()
|
|
|
|
for i in range(25):
|
|
profile.add_positive_example(
|
|
title=f"Example {i}",
|
|
url=f"https://example.com/{i}",
|
|
)
|
|
|
|
assert len(profile.positive_examples) == 20
|
|
# Sollte die neuesten behalten
|
|
assert "Example 24" in profile.positive_examples[-1]["title"]
|
|
|
|
def test_update_from_feedback_positive(self):
|
|
"""Test Feedback Update (positiv)."""
|
|
profile = RelevanceProfile()
|
|
profile.update_from_feedback(
|
|
alert_title="Relevant Article",
|
|
alert_url="https://example.com",
|
|
is_relevant=True,
|
|
reason="Sehr relevant",
|
|
)
|
|
|
|
assert len(profile.positive_examples) == 1
|
|
assert profile.total_kept == 1
|
|
assert profile.total_scored == 1
|
|
|
|
def test_update_from_feedback_negative(self):
|
|
"""Test Feedback Update (negativ)."""
|
|
profile = RelevanceProfile()
|
|
profile.update_from_feedback(
|
|
alert_title="Irrelevant Article",
|
|
alert_url="https://example.com",
|
|
is_relevant=False,
|
|
reason="Werbung",
|
|
)
|
|
|
|
assert len(profile.negative_examples) == 1
|
|
assert profile.total_dropped == 1
|
|
assert profile.total_scored == 1
|
|
|
|
|
|
class TestPromptContext:
|
|
"""Tests für Prompt-Kontext Generierung."""
|
|
|
|
def test_empty_profile_prompt(self):
|
|
"""Test Prompt für leeres Profil."""
|
|
profile = RelevanceProfile()
|
|
context = profile.get_prompt_context()
|
|
|
|
assert "Relevanzprofil" in context
|
|
# Leeres Profil hat keine Prioritäten/Ausschlüsse
|
|
assert "Prioritäten" not in context
|
|
|
|
def test_priorities_in_prompt(self):
|
|
"""Test Prioritäten im Prompt."""
|
|
profile = RelevanceProfile()
|
|
profile.add_priority("Inklusion", weight=0.9, description="Sehr wichtig")
|
|
|
|
context = profile.get_prompt_context()
|
|
|
|
assert "Inklusion" in context
|
|
assert "Sehr wichtig" in context
|
|
|
|
def test_exclusions_in_prompt(self):
|
|
"""Test Ausschlüsse im Prompt."""
|
|
profile = RelevanceProfile()
|
|
profile.add_exclusion("Stellenanzeige")
|
|
profile.add_exclusion("Werbung")
|
|
|
|
context = profile.get_prompt_context()
|
|
|
|
assert "Stellenanzeige" in context
|
|
assert "Werbung" in context
|
|
assert "Ausschlüsse" in context
|
|
|
|
def test_examples_in_prompt(self):
|
|
"""Test Beispiele im Prompt."""
|
|
profile = RelevanceProfile()
|
|
profile.add_positive_example(
|
|
title="Gutes Beispiel",
|
|
url="https://example.com",
|
|
reason="Relevant",
|
|
)
|
|
|
|
context = profile.get_prompt_context()
|
|
|
|
assert "Gutes Beispiel" in context
|
|
assert "relevante Alerts" in context
|
|
|
|
|
|
class TestDefaultEducationProfile:
|
|
"""Tests für das Standard-Bildungsprofil."""
|
|
|
|
def test_create_default_profile(self):
|
|
"""Test Default-Profil Erstellung."""
|
|
profile = RelevanceProfile.create_default_education_profile()
|
|
|
|
assert len(profile.priorities) > 0
|
|
assert len(profile.exclusions) > 0
|
|
assert len(profile.policies) > 0
|
|
|
|
def test_default_priorities(self):
|
|
"""Test Default-Prioritäten enthalten Bildungsthemen."""
|
|
profile = RelevanceProfile.create_default_education_profile()
|
|
|
|
labels = [p.label for p in profile.priorities]
|
|
|
|
assert "Inklusion" in labels
|
|
assert "Datenschutz Schule" in labels
|
|
assert "Schulrecht Bayern" in labels
|
|
|
|
def test_default_exclusions(self):
|
|
"""Test Default-Ausschlüsse."""
|
|
profile = RelevanceProfile.create_default_education_profile()
|
|
|
|
assert "Stellenanzeige" in profile.exclusions
|
|
assert "Werbung" in profile.exclusions
|
|
|
|
def test_default_policies(self):
|
|
"""Test Default-Policies."""
|
|
profile = RelevanceProfile.create_default_education_profile()
|
|
|
|
assert profile.policies.get("prefer_german_sources") is True
|
|
assert "max_age_days" in profile.policies
|
|
|
|
|
|
class TestSerialization:
|
|
"""Tests für Serialisierung."""
|
|
|
|
def test_to_dict(self):
|
|
"""Test Konvertierung zu Dict."""
|
|
profile = RelevanceProfile()
|
|
profile.add_priority("Test", weight=0.7)
|
|
profile.add_exclusion("Exclude")
|
|
|
|
data = profile.to_dict()
|
|
|
|
assert "id" in data
|
|
assert len(data["priorities"]) == 1
|
|
assert "Exclude" in data["exclusions"]
|
|
assert "created_at" in data
|
|
|
|
def test_from_dict(self):
|
|
"""Test Erstellung aus Dict."""
|
|
data = {
|
|
"id": "test-id",
|
|
"priorities": [{"label": "Test", "weight": 0.8, "keywords": [], "description": None}],
|
|
"exclusions": ["Exclude"],
|
|
"positive_examples": [],
|
|
"negative_examples": [],
|
|
"policies": {"key": "value"},
|
|
"created_at": "2024-01-15T10:00:00",
|
|
"updated_at": "2024-01-15T10:00:00",
|
|
"total_scored": 100,
|
|
"total_kept": 60,
|
|
"total_dropped": 40,
|
|
"accuracy_estimate": None,
|
|
}
|
|
|
|
profile = RelevanceProfile.from_dict(data)
|
|
|
|
assert profile.id == "test-id"
|
|
assert len(profile.priorities) == 1
|
|
assert profile.total_scored == 100
|
|
|
|
def test_round_trip(self):
|
|
"""Test Serialisierung/Deserialisierung Roundtrip."""
|
|
original = RelevanceProfile.create_default_education_profile()
|
|
original.add_positive_example("Test", "https://test.com")
|
|
|
|
data = original.to_dict()
|
|
restored = RelevanceProfile.from_dict(data)
|
|
|
|
assert restored.id == original.id
|
|
assert len(restored.priorities) == len(original.priorities)
|
|
assert len(restored.positive_examples) == len(original.positive_examples)
|