# -*- coding: utf-8 -*- """ Unit-Tests für trixy_core.satellite. """ import json import pytest from datetime import datetime from unittest.mock import MagicMock, AsyncMock from pathlib import Path from trixy_core.satellite.satellite import Satellite, ConnectionState, SatelliteSocket from trixy_core.satellite.satellite_manager import SatelliteManager from trixy_core.satellite.registration_manager import RegistrationManager, PendingRegistration class TestConnectionState: """Tests für ConnectionState.""" def test_states_exist(self): """Testet, dass alle Zustände existieren.""" assert hasattr(ConnectionState, "DISCONNECTED") assert hasattr(ConnectionState, "CONNECTING") assert hasattr(ConnectionState, "CONNECTED") assert hasattr(ConnectionState, "AUTHENTICATED") assert hasattr(ConnectionState, "DISCONNECTING") assert hasattr(ConnectionState, "ERROR") class TestSatellite: """Tests für Satellite-Klasse.""" @pytest.fixture def satellite(self): """Erstellt einen Test-Satellite.""" return Satellite( satellite_id="sat-test-123", room_id="wohnzimmer", mac_address="AA:BB:CC:DD:EE:FF", alias="Test Echo", ip_address="192.168.1.100" ) def test_satellite_creation(self, satellite): """Testet die Satellite-Erstellung.""" assert satellite.id == "sat-test-123" assert satellite.room_id == "wohnzimmer" assert satellite.mac_address == "AA:BB:CC:DD:EE:FF" assert satellite.alias == "Test Echo" assert satellite.ip_address == "192.168.1.100" def test_satellite_auto_id(self): """Testet die automatische ID-Generierung.""" sat = Satellite(room_id="test") assert sat.id is not None assert len(sat.id) > 0 def test_satellite_initial_state(self, satellite): """Testet den initialen Zustand.""" assert satellite.state == ConnectionState.DISCONNECTED assert satellite.is_connected is False def test_satellite_is_connected(self, satellite): """Testet is_connected für verschiedene Zustände.""" satellite.state = ConnectionState.CONNECTED assert satellite.is_connected is True satellite.state = ConnectionState.AUTHENTICATED assert satellite.is_connected is True satellite.state = ConnectionState.DISCONNECTED assert satellite.is_connected is False def test_set_connected(self, satellite): """Testet set_connected.""" satellite.set_connected() assert satellite.state == ConnectionState.CONNECTED assert satellite.connected_at is not None assert satellite.last_heartbeat is not None def test_update_heartbeat(self, satellite): """Testet update_heartbeat.""" satellite.update_heartbeat() assert satellite.last_heartbeat is not None def test_conversation_id(self, satellite): """Testet conversation_id.""" assert satellite.conversation_id is None assert satellite.has_active_conversation is False satellite.conversation_id = "conv-123" assert satellite.conversation_id == "conv-123" assert satellite.has_active_conversation is True def test_version(self, satellite): """Testet version.""" satellite.version = "1.0.0" assert satellite.version == "1.0.0" def test_metadata(self, satellite): """Testet metadata.""" satellite.metadata["custom"] = "value" assert satellite.metadata["custom"] == "value" def test_to_dict(self, satellite): """Testet to_dict.""" satellite.set_connected() data = satellite.to_dict() assert data["id"] == "sat-test-123" assert data["room_id"] == "wohnzimmer" assert data["mac_address"] == "AA:BB:CC:DD:EE:FF" assert data["state"] == "CONNECTED" @pytest.mark.asyncio async def test_disconnect(self, satellite): """Testet disconnect.""" satellite.set_connected() satellite.conversation_id = "conv-123" await satellite.disconnect("Test reason") assert satellite.state == ConnectionState.DISCONNECTED assert satellite.conversation_id is None def test_repr(self, satellite): """Testet __repr__.""" repr_str = repr(satellite) assert "Satellite" in repr_str assert "sat-test-123" in repr_str class TestSatelliteSocket: """Tests für SatelliteSocket.""" def test_socket_initialization(self): """Testet die Socket-Initialisierung.""" sockets = SatelliteSocket() assert sockets.command is None assert sockets.audio_in is None assert sockets.audio_out is None assert sockets.music_out is None class TestSatelliteManager: """Tests für SatelliteManager.""" @pytest.fixture def mock_application(self): """Erstellt eine Mock-Anwendung.""" return MagicMock() @pytest.fixture def manager(self, mock_application): """Erstellt einen SatelliteManager.""" return SatelliteManager(mock_application) @pytest.fixture def satellite(self): """Erstellt einen Test-Satellite.""" return Satellite( satellite_id="sat-1", room_id="wohnzimmer", mac_address="AA:BB:CC:DD:EE:FF", alias="Echo 1" ) def test_manager_initialization(self, manager): """Testet die Manager-Initialisierung.""" assert manager.count == 0 assert manager.connected_count == 0 def test_add_satellite(self, manager, satellite): """Testet das Hinzufügen eines Satellites.""" manager.add(satellite) assert manager.count == 1 assert satellite.id in manager._satellites def test_remove_satellite(self, manager, satellite): """Testet das Entfernen eines Satellites.""" manager.add(satellite) removed = manager.remove(satellite.id) assert removed == satellite assert manager.count == 0 def test_remove_nonexistent(self, manager): """Testet das Entfernen eines nicht existierenden Satellites.""" assert manager.remove("nonexistent") is None def test_get_satellite(self, manager, satellite): """Testet get.""" manager.add(satellite) result = manager.get(satellite.id) assert result == satellite def test_get_by_mac(self, manager, satellite): """Testet get_by_mac.""" manager.add(satellite) result = manager.get_by_mac("AA:BB:CC:DD:EE:FF") assert result == satellite def test_get_by_room(self, manager, satellite): """Testet get_by_room.""" manager.add(satellite) result = manager.get_by_room("wohnzimmer") assert len(result) == 1 assert result[0] == satellite def test_get_connected(self, manager, satellite): """Testet get_connected.""" satellite.set_connected() manager.add(satellite) connected = manager.get_connected() assert len(connected) == 1 assert manager.connected_count == 1 def test_get_disconnected(self, manager, satellite): """Testet get_disconnected.""" manager.add(satellite) disconnected = manager.get_disconnected() assert len(disconnected) == 1 def test_getitem_by_index(self, manager, satellite): """Testet __getitem__ mit Index.""" manager.add(satellite) result = manager[0] assert result == satellite def test_getitem_by_id(self, manager, satellite): """Testet __getitem__ mit ID.""" manager.add(satellite) result = manager[satellite.id] assert result == satellite def test_getitem_by_room_selector(self, manager, satellite): """Testet __getitem__ mit Room-Selektor.""" manager.add(satellite) result = manager["room:wohnzimmer"] assert len(result) == 1 assert result[0] == satellite def test_getitem_by_mac_selector(self, manager, satellite): """Testet __getitem__ mit MAC-Selektor.""" manager.add(satellite) result = manager["mac:AA:BB:CC:DD:EE:FF"] assert result == satellite def test_getitem_raises_keyerror(self, manager): """Testet, dass __getitem__ KeyError wirft.""" with pytest.raises(KeyError): _ = manager["nonexistent"] def test_getitem_raises_indexerror(self, manager): """Testet, dass __getitem__ IndexError wirft.""" with pytest.raises(IndexError): _ = manager[0] def test_contains(self, manager, satellite): """Testet __contains__.""" manager.add(satellite) assert satellite.id in manager assert "AA:BB:CC:DD:EE:FF" in manager assert "nonexistent" not in manager def test_iter(self, manager, satellite): """Testet __iter__.""" manager.add(satellite) satellites = list(manager) assert len(satellites) == 1 assert satellites[0] == satellite def test_len(self, manager, satellite): """Testet __len__.""" assert len(manager) == 0 manager.add(satellite) assert len(manager) == 1 @pytest.mark.asyncio async def test_disconnect_satellite(self, manager, satellite): """Testet disconnect.""" satellite.set_connected() manager.add(satellite) result = await manager.disconnect(satellite.id, "Test") assert result is True assert satellite.state == ConnectionState.DISCONNECTED @pytest.mark.asyncio async def test_disconnect_all(self, manager, satellite): """Testet disconnect_all.""" satellite.set_connected() manager.add(satellite) count = await manager.disconnect_all() assert count == 1 def test_find(self, manager): """Testet find.""" sat1 = Satellite(room_id="wohnzimmer", alias="Echo 1") sat2 = Satellite(room_id="küche", alias="Echo 2") sat1.set_connected() manager.add(sat1) manager.add(sat2) # Nach Raum suchen result = manager.find(room="wohnzimmer") assert len(result) == 1 # Nach Alias suchen result = manager.find(alias="Echo") assert len(result) == 2 # Nach Verbindungsstatus suchen result = manager.find(connected=True) assert len(result) == 1 class TestPendingRegistration: """Tests für PendingRegistration.""" def test_pending_creation(self): """Testet die Erstellung einer ausstehenden Registrierung.""" pending = PendingRegistration( mac_address="AA:BB:CC:DD:EE:FF", room="wohnzimmer", alias="Echo 1" ) assert pending.mac_address == "AA:BB:CC:DD:EE:FF" assert pending.is_expired() is False class TestRegistrationManager: """Tests für RegistrationManager.""" @pytest.fixture def mock_application(self): """Erstellt eine Mock-Anwendung.""" return MagicMock() @pytest.fixture def manager(self, mock_application, temp_dir): """Erstellt einen RegistrationManager.""" return RegistrationManager(mock_application, directory=temp_dir) def test_manager_initialization(self, manager): """Testet die Manager-Initialisierung.""" assert manager.registration_mode is False assert manager.pending_count == 0 def test_enable_registration_mode(self, manager): """Testet das Aktivieren des Registrierungsmodus.""" manager.enable_registration_mode(timeout_seconds=60) assert manager.registration_mode is True def test_disable_registration_mode(self, manager): """Testet das Deaktivieren des Registrierungsmodus.""" manager.enable_registration_mode() manager.disable_registration_mode() assert manager.registration_mode is False def test_begin_registration_requires_mode(self, manager): """Testet, dass Registrierung Modus erfordert.""" result = manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="test", alias="Test" ) assert result is False def test_begin_registration(self, manager): """Testet das Starten einer Registrierung.""" manager.enable_registration_mode() result = manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="wohnzimmer", alias="Echo 1" ) assert result is True assert manager.pending_count == 1 def test_complete_registration(self, manager): """Testet das Abschließen einer Registrierung.""" manager.enable_registration_mode() manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="wohnzimmer", alias="Echo 1" ) satellite = manager.complete_registration("AA:BB:CC:DD:EE:FF") assert satellite is not None assert satellite.mac_address == "AA:BB:CC:DD:EE:FF" assert satellite.room_id == "wohnzimmer" assert manager.pending_count == 0 def test_complete_nonexistent_registration(self, manager): """Testet Abschluss einer nicht existierenden Registrierung.""" satellite = manager.complete_registration("XX:XX:XX:XX:XX:XX") assert satellite is None def test_cancel_registration(self, manager): """Testet das Abbrechen einer Registrierung.""" manager.enable_registration_mode() manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="test", alias="Test" ) result = manager.cancel_registration("AA:BB:CC:DD:EE:FF") assert result is True assert manager.pending_count == 0 def test_is_registered(self, manager): """Testet is_registered.""" manager.enable_registration_mode() manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="test", alias="Test" ) manager.complete_registration("AA:BB:CC:DD:EE:FF") assert manager.is_registered("AA:BB:CC:DD:EE:FF") is True assert manager.is_registered("XX:XX:XX:XX:XX:XX") is False def test_load_registered(self, manager): """Testet load_registered.""" # Registriere und speichere manager.enable_registration_mode() manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="wohnzimmer", alias="Echo 1" ) original = manager.complete_registration("AA:BB:CC:DD:EE:FF") # Lade erneut loaded = manager.load_registered("AA:BB:CC:DD:EE:FF") assert loaded is not None assert loaded.mac_address == original.mac_address assert loaded.room_id == original.room_id def test_load_all(self, manager): """Testet load_all.""" # Registriere zwei Satellites manager.enable_registration_mode() manager.begin_registration("AA:AA:AA:AA:AA:AA", "raum1", "Echo 1") manager.complete_registration("AA:AA:AA:AA:AA:AA") manager.begin_registration("BB:BB:BB:BB:BB:BB", "raum2", "Echo 2") manager.complete_registration("BB:BB:BB:BB:BB:BB") satellites = manager.load_all() assert len(satellites) == 2 def test_delete_registration(self, manager): """Testet delete_registration.""" manager.enable_registration_mode() manager.begin_registration( mac_address="AA:BB:CC:DD:EE:FF", room="test", alias="Test" ) manager.complete_registration("AA:BB:CC:DD:EE:FF") result = manager.delete_registration("AA:BB:CC:DD:EE:FF") assert result is True assert manager.is_registered("AA:BB:CC:DD:EE:FF") is False