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>
204 lines
5.7 KiB
Python
204 lines
5.7 KiB
Python
"""
|
|
Tests for Task Router
|
|
|
|
Tests cover:
|
|
- Intent-based routing
|
|
- Routing rules
|
|
- Fallback handling
|
|
- Routing statistics
|
|
"""
|
|
|
|
import pytest
|
|
import asyncio
|
|
from unittest.mock import MagicMock
|
|
|
|
import sys
|
|
sys.path.insert(0, str(__file__).rsplit('/tests/', 1)[0])
|
|
|
|
from orchestrator.task_router import (
|
|
TaskRouter,
|
|
RoutingRule,
|
|
RoutingResult,
|
|
RoutingStrategy,
|
|
)
|
|
|
|
|
|
class TestRoutingRule:
|
|
"""Tests for RoutingRule dataclass"""
|
|
|
|
def test_rule_creation(self):
|
|
"""Rule should be created correctly"""
|
|
rule = RoutingRule(
|
|
intent_pattern="learning_*",
|
|
agent_type="tutor-agent",
|
|
priority=10
|
|
)
|
|
|
|
assert rule.intent_pattern == "learning_*"
|
|
assert rule.agent_type == "tutor-agent"
|
|
assert rule.priority == 10
|
|
|
|
def test_rule_matches_exact(self):
|
|
"""Rule should match exact intent"""
|
|
rule = RoutingRule(
|
|
intent_pattern="grade_exam",
|
|
agent_type="grader-agent"
|
|
)
|
|
|
|
assert rule.matches("grade_exam", {}) is True
|
|
assert rule.matches("grade_quiz", {}) is False
|
|
|
|
def test_rule_matches_wildcard(self):
|
|
"""Rule should match wildcard patterns"""
|
|
rule = RoutingRule(
|
|
intent_pattern="learning_*",
|
|
agent_type="tutor-agent"
|
|
)
|
|
|
|
assert rule.matches("learning_math", {}) is True
|
|
assert rule.matches("learning_english", {}) is True
|
|
assert rule.matches("grading_math", {}) is False
|
|
|
|
def test_rule_matches_conditions(self):
|
|
"""Rule should check conditions"""
|
|
rule = RoutingRule(
|
|
intent_pattern="*",
|
|
agent_type="vip-agent",
|
|
conditions={"is_vip": True}
|
|
)
|
|
|
|
assert rule.matches("any_intent", {"is_vip": True}) is True
|
|
assert rule.matches("any_intent", {"is_vip": False}) is False
|
|
assert rule.matches("any_intent", {}) is False
|
|
|
|
|
|
class TestRoutingResult:
|
|
"""Tests for RoutingResult dataclass"""
|
|
|
|
def test_successful_result(self):
|
|
"""Should create successful routing result"""
|
|
result = RoutingResult(
|
|
success=True,
|
|
agent_id="tutor-1",
|
|
agent_type="tutor-agent",
|
|
reason="Primary agent selected"
|
|
)
|
|
|
|
assert result.success is True
|
|
assert result.agent_id == "tutor-1"
|
|
assert result.is_fallback is False
|
|
|
|
def test_fallback_result(self):
|
|
"""Should indicate fallback routing"""
|
|
result = RoutingResult(
|
|
success=True,
|
|
agent_id="backup-1",
|
|
agent_type="backup-agent",
|
|
is_fallback=True,
|
|
reason="Fallback used"
|
|
)
|
|
|
|
assert result.success is True
|
|
assert result.is_fallback is True
|
|
|
|
def test_failed_result(self):
|
|
"""Should create failed routing result"""
|
|
result = RoutingResult(
|
|
success=False,
|
|
reason="No agents available"
|
|
)
|
|
|
|
assert result.success is False
|
|
assert result.agent_id is None
|
|
|
|
|
|
class TestTaskRouter:
|
|
"""Tests for TaskRouter"""
|
|
|
|
@pytest.fixture
|
|
def router(self):
|
|
"""Create a task router without supervisor"""
|
|
return TaskRouter(supervisor=None)
|
|
|
|
def test_default_rules_exist(self, router):
|
|
"""Router should have default rules"""
|
|
rules = router.get_rules()
|
|
assert len(rules) > 0
|
|
|
|
def test_add_rule(self, router):
|
|
"""Should add new routing rule"""
|
|
original_count = len(router.rules)
|
|
|
|
router.add_rule(RoutingRule(
|
|
intent_pattern="custom_*",
|
|
agent_type="custom-agent",
|
|
priority=100
|
|
))
|
|
|
|
assert len(router.rules) == original_count + 1
|
|
|
|
def test_rules_sorted_by_priority(self, router):
|
|
"""Rules should be sorted by priority (high first)"""
|
|
router.add_rule(RoutingRule(
|
|
intent_pattern="low_*",
|
|
agent_type="low-agent",
|
|
priority=1
|
|
))
|
|
router.add_rule(RoutingRule(
|
|
intent_pattern="high_*",
|
|
agent_type="high-agent",
|
|
priority=100
|
|
))
|
|
|
|
# Highest priority should be first
|
|
assert router.rules[0].priority >= router.rules[-1].priority
|
|
|
|
def test_remove_rule(self, router):
|
|
"""Should remove routing rule"""
|
|
router.add_rule(RoutingRule(
|
|
intent_pattern="removable_*",
|
|
agent_type="temp-agent"
|
|
))
|
|
|
|
result = router.remove_rule("removable_*")
|
|
assert result is True
|
|
|
|
def test_find_matching_rules(self, router):
|
|
"""Should find rules matching intent"""
|
|
matching = router.find_matching_rules("learning_math")
|
|
|
|
assert len(matching) > 0
|
|
assert any(r["agent_type"] == "tutor-agent" for r in matching)
|
|
|
|
def test_get_routing_stats_empty(self, router):
|
|
"""Should return empty stats initially"""
|
|
stats = router.get_routing_stats()
|
|
|
|
assert stats["total_routes"] == 0
|
|
|
|
def test_set_default_route(self, router):
|
|
"""Should set default agent for type"""
|
|
router.set_default_route("tutor-agent", "tutor-primary")
|
|
|
|
assert router._default_routes["tutor-agent"] == "tutor-primary"
|
|
|
|
def test_clear_history(self, router):
|
|
"""Should clear routing history"""
|
|
# Add some history
|
|
router._routing_history.append({"test": "data"})
|
|
|
|
router.clear_history()
|
|
|
|
assert len(router._routing_history) == 0
|
|
|
|
|
|
class TestRoutingStrategy:
|
|
"""Tests for RoutingStrategy enum"""
|
|
|
|
def test_strategy_values(self):
|
|
"""Strategies should have expected values"""
|
|
assert RoutingStrategy.DIRECT.value == "direct"
|
|
assert RoutingStrategy.ROUND_ROBIN.value == "round_robin"
|
|
assert RoutingStrategy.LEAST_LOADED.value == "least_loaded"
|
|
assert RoutingStrategy.PRIORITY.value == "priority"
|