# -*- coding: utf-8 -*- """ Tests für Satellite Capability System. """ import asyncio import pytest from datetime import datetime from trixy_core.satellite.capability import ( CapabilityDiscovery, DiscoveryConfig, CapabilityProbe, CapabilityRegistry, Capability, CapabilityInfo, CapabilityState, ) from trixy_core.satellite.capability.discovery import SimpleProbe, ProbeResult class TestCapabilityInfo: """Tests für CapabilityInfo.""" def test_info_creation(self): """Testet Info-Erstellung.""" info = CapabilityInfo( id="audio.input", name="Mikrofon", description="Audio-Eingang", category="audio", ) assert info.id == "audio.input" assert info.name == "Mikrofon" assert info.category == "audio" def test_info_defaults(self): """Testet Standard-Werte.""" info = CapabilityInfo(id="test", name="Test") assert info.description == "" assert info.category == "" assert info.required_features == [] class TestCapability: """Tests für Capability.""" @pytest.fixture def cap_info(self): """Erstellt CapabilityInfo.""" return CapabilityInfo( id="audio.input", name="Mikrofon", category="audio", ) def test_capability_creation(self, cap_info): """Testet Capability-Erstellung.""" cap = Capability( info=cap_info, state=CapabilityState.AVAILABLE, ) assert cap.id == "audio.input" assert cap.state == CapabilityState.AVAILABLE assert cap.is_available def test_capability_states(self, cap_info): """Testet Capability-Zustände.""" cap = Capability(info=cap_info) cap.state = CapabilityState.UNKNOWN assert not cap.is_available assert not cap.is_degraded cap.state = CapabilityState.AVAILABLE assert cap.is_available cap.state = CapabilityState.DEGRADED assert cap.is_degraded def test_update_state(self, cap_info): """Testet State-Update.""" cap = Capability(info=cap_info) cap.update_state(CapabilityState.UNAVAILABLE, "Gerät nicht gefunden") assert cap.state == CapabilityState.UNAVAILABLE assert cap.error_message == "Gerät nicht gefunden" assert cap.last_checked is not None def test_to_dict(self, cap_info): """Testet Dictionary-Konvertierung.""" cap = Capability( info=cap_info, state=CapabilityState.AVAILABLE, satellite_id="sat-001", ) d = cap.to_dict() assert d["id"] == "audio.input" assert d["state"] == "AVAILABLE" assert d["satellite_id"] == "sat-001" class TestCapabilityRegistry: """Tests für CapabilityRegistry.""" @pytest.fixture def registry(self): """Erstellt Registry.""" return CapabilityRegistry() @pytest.fixture def audio_info(self): """Erstellt Audio-CapabilityInfo.""" return CapabilityInfo( id="audio.input", name="Mikrofon", category="audio", ) def test_define_capability(self, registry, audio_info): """Testet Capability-Definition.""" registry.define_capability(audio_info) assert registry.capability_count == 1 assert registry.get_definition("audio.input") == audio_info def test_register_capability(self, registry, audio_info): """Testet Capability-Registrierung.""" cap = Capability(info=audio_info, state=CapabilityState.AVAILABLE) registry.register("sat-001", cap) assert registry.satellite_count == 1 assert registry.has_capability("sat-001", "audio.input") def test_unregister_capability(self, registry, audio_info): """Testet Capability-Entfernung.""" cap = Capability(info=audio_info) registry.register("sat-001", cap) assert registry.unregister("sat-001", "audio.input") assert not registry.has_capability("sat-001", "audio.input") def test_unregister_all_satellite(self, registry, audio_info): """Testet Entfernung aller Capabilities eines Satellites.""" cap = Capability(info=audio_info) registry.register("sat-001", cap) assert registry.unregister("sat-001") assert "sat-001" not in registry def test_update_state(self, registry, audio_info): """Testet State-Update über Registry.""" cap = Capability(info=audio_info) registry.register("sat-001", cap) assert registry.update_state( "sat-001", "audio.input", CapabilityState.UNAVAILABLE, "Fehler", ) updated = registry.get("sat-001", "audio.input") assert updated.state == CapabilityState.UNAVAILABLE def test_get_capability(self, registry, audio_info): """Testet Capability-Abruf.""" cap = Capability(info=audio_info, state=CapabilityState.AVAILABLE) registry.register("sat-001", cap) result = registry.get("sat-001", "audio.input") assert result == cap def test_get_all_capabilities(self, registry, audio_info): """Testet Abruf aller Capabilities.""" cap1 = Capability( info=audio_info, state=CapabilityState.AVAILABLE, ) cap2 = Capability( info=CapabilityInfo("display.screen", "Bildschirm"), state=CapabilityState.AVAILABLE, ) registry.register("sat-001", cap1) registry.register("sat-001", cap2) caps = registry.get_all("sat-001") assert len(caps) == 2 def test_get_available_capabilities(self, registry, audio_info): """Testet Abruf verfügbarer Capabilities.""" cap1 = Capability(info=audio_info, state=CapabilityState.AVAILABLE) cap2 = Capability( info=CapabilityInfo("display.screen", "Bildschirm"), state=CapabilityState.UNAVAILABLE, ) registry.register("sat-001", cap1) registry.register("sat-001", cap2) available = registry.get_available("sat-001") assert len(available) == 1 assert available[0].id == "audio.input" def test_has_capability(self, registry, audio_info): """Testet Capability-Prüfung.""" cap = Capability(info=audio_info, state=CapabilityState.AVAILABLE) registry.register("sat-001", cap) assert registry.has_capability("sat-001", "audio.input") assert not registry.has_capability("sat-001", "unknown") assert not registry.has_capability("unknown", "audio.input") def test_has_capability_require_available(self, registry, audio_info): """Testet Capability-Prüfung mit Verfügbarkeit.""" cap = Capability(info=audio_info, state=CapabilityState.UNAVAILABLE) registry.register("sat-001", cap) # Mit require_available=True assert not registry.has_capability("sat-001", "audio.input", True) # Mit require_available=False assert registry.has_capability("sat-001", "audio.input", False) def test_has_all_capabilities(self, registry): """Testet Prüfung mehrerer Capabilities.""" cap1 = Capability( info=CapabilityInfo("cap1", "Cap 1"), state=CapabilityState.AVAILABLE, ) cap2 = Capability( info=CapabilityInfo("cap2", "Cap 2"), state=CapabilityState.AVAILABLE, ) registry.register("sat-001", cap1) registry.register("sat-001", cap2) assert registry.has_all_capabilities("sat-001", ["cap1", "cap2"]) assert not registry.has_all_capabilities("sat-001", ["cap1", "cap3"]) def test_has_any_capability(self, registry): """Testet Prüfung auf mindestens eine Capability.""" cap = Capability( info=CapabilityInfo("cap1", "Cap 1"), state=CapabilityState.AVAILABLE, ) registry.register("sat-001", cap) assert registry.has_any_capability("sat-001", ["cap1", "cap2"]) assert not registry.has_any_capability("sat-001", ["cap3", "cap4"]) def test_find_satellites_with(self, registry, audio_info): """Testet Satellite-Suche nach Capability.""" cap = Capability(info=audio_info, state=CapabilityState.AVAILABLE) registry.register("sat-001", cap) registry.register("sat-002", cap) satellites = registry.find_satellites_with("audio.input") assert len(satellites) == 2 assert "sat-001" in satellites assert "sat-002" in satellites def test_find_satellites_with_all(self, registry): """Testet Satellite-Suche nach mehreren Capabilities.""" cap1 = Capability( info=CapabilityInfo("cap1", "Cap 1"), state=CapabilityState.AVAILABLE, ) cap2 = Capability( info=CapabilityInfo("cap2", "Cap 2"), state=CapabilityState.AVAILABLE, ) registry.register("sat-001", cap1) registry.register("sat-001", cap2) registry.register("sat-002", cap1) # Nur cap1 satellites = registry.find_satellites_with_all(["cap1", "cap2"]) assert satellites == ["sat-001"] def test_get_by_category(self, registry): """Testet Abruf nach Kategorie.""" cap1 = Capability( info=CapabilityInfo("audio.in", "In", category="audio"), ) cap2 = Capability( info=CapabilityInfo("audio.out", "Out", category="audio"), ) cap3 = Capability( info=CapabilityInfo("display", "Display", category="display"), ) registry.register("sat-001", cap1) registry.register("sat-001", cap2) registry.register("sat-001", cap3) audio_caps = registry.get_by_category("sat-001", "audio") assert len(audio_caps) == 2 def test_on_change_callback(self, registry, audio_info): """Testet Änderungs-Callback.""" changes = [] def on_change(sat_id, cap): changes.append((sat_id, cap.id)) registry.on_change(on_change) cap = Capability(info=audio_info) registry.register("sat-001", cap) assert ("sat-001", "audio.input") in changes def test_get_summary(self, registry, audio_info): """Testet Zusammenfassung.""" cap = Capability(info=audio_info, state=CapabilityState.AVAILABLE) registry.register("sat-001", cap) summary = registry.get_summary() assert summary["satellite_count"] == 1 assert summary["total_capabilities"] == 1 assert summary["available_capabilities"] == 1 class TestDiscoveryConfig: """Tests für DiscoveryConfig.""" def test_default_config(self): """Testet Standard-Konfiguration.""" config = DiscoveryConfig() assert config.probe_timeout == 10.0 assert config.probe_concurrency == 5 assert config.auto_discover is True class TestProbeResult: """Tests für ProbeResult.""" def test_result_creation(self): """Testet Result-Erstellung.""" result = ProbeResult( capability_id="audio.input", success=True, state=CapabilityState.AVAILABLE, ) assert result.capability_id == "audio.input" assert result.success assert result.state == CapabilityState.AVAILABLE class TestCapabilityDiscovery: """Tests für CapabilityDiscovery.""" @pytest.fixture def registry(self): """Erstellt Registry.""" return CapabilityRegistry() @pytest.fixture def send_func(self): """Erstellt Mock-Send-Funktion.""" responses = {} async def send(sat_id, message): return responses.get(sat_id, {"available": False}) send.responses = responses return send @pytest.fixture def discovery(self, registry, send_func): """Erstellt Discovery-Instanz.""" config = DiscoveryConfig( probe_timeout=1.0, probe_concurrency=2, ) return CapabilityDiscovery( registry=registry, send_func=send_func, config=config, ) def test_register_probe(self, discovery): """Testet Probe-Registrierung.""" info = CapabilityInfo("test", "Test") probe = SimpleProbe(info) discovery.register_probe(probe) assert discovery.probe_count == 1 assert discovery.registry.get_definition("test") is not None def test_unregister_probe(self, discovery): """Testet Probe-Entfernung.""" info = CapabilityInfo("test", "Test") probe = SimpleProbe(info) discovery.register_probe(probe) assert discovery.unregister_probe("test") assert discovery.probe_count == 0 @pytest.mark.asyncio async def test_discover_capability(self, discovery, send_func): """Testet einzelne Capability-Discovery.""" info = CapabilityInfo("audio.input", "Mikrofon") probe = SimpleProbe(info) discovery.register_probe(probe) send_func.responses["sat-001"] = { "available": True, "parameters": {"channels": 2}, } result = await discovery.discover_capability("sat-001", "audio.input") assert result is not None assert result.success assert result.state == CapabilityState.AVAILABLE @pytest.mark.asyncio async def test_discover_satellite(self, discovery, send_func): """Testet vollständige Satellite-Discovery.""" info1 = CapabilityInfo("cap1", "Cap 1") info2 = CapabilityInfo("cap2", "Cap 2") discovery.register_probe(SimpleProbe(info1)) discovery.register_probe(SimpleProbe(info2)) send_func.responses["sat-001"] = {"available": True} results = await discovery.discover_satellite("sat-001") assert len(results) == 2 assert all(r.success for r in results) @pytest.mark.asyncio async def test_discover_specific_capabilities(self, discovery, send_func): """Testet Discovery spezifischer Capabilities.""" info1 = CapabilityInfo("cap1", "Cap 1") info2 = CapabilityInfo("cap2", "Cap 2") discovery.register_probe(SimpleProbe(info1)) discovery.register_probe(SimpleProbe(info2)) send_func.responses["sat-001"] = {"available": True} results = await discovery.discover_satellite("sat-001", ["cap1"]) assert len(results) == 1 assert results[0].capability_id == "cap1" @pytest.mark.asyncio async def test_discover_all(self, discovery, send_func): """Testet Discovery für mehrere Satellites.""" info = CapabilityInfo("cap1", "Cap 1") discovery.register_probe(SimpleProbe(info)) send_func.responses["sat-001"] = {"available": True} send_func.responses["sat-002"] = {"available": False} results = await discovery.discover_all(["sat-001", "sat-002"]) assert "sat-001" in results assert "sat-002" in results def test_needs_rediscovery(self, discovery): """Testet Rediscovery-Prüfung.""" assert discovery.needs_rediscovery("sat-001") discovery._last_discovery["sat-001"] = datetime.now() assert not discovery.needs_rediscovery("sat-001") def test_get_stats(self, discovery): """Testet Statistik-Abruf.""" info = CapabilityInfo("test", "Test") discovery.register_probe(SimpleProbe(info)) stats = discovery.get_stats() assert stats["probe_count"] == 1 assert "config" in stats