# -*- coding: utf-8 -*- """ Unit-Tests für trixy_core.application. """ import asyncio import json import pytest from pathlib import Path from unittest.mock import MagicMock, AsyncMock, patch from trixy_core.application import IApplication from trixy_core.server import ServerApplication from trixy_core.client import ClientApplication from trixy_core.standalone import StandaloneApplication from trixy_core.trainer import TrainerApplication, TrainingTarget from trixy_core.service.service_container import ServiceContainer from trixy_core.events.eventmanager import EventManager from trixy_core.config.config_manager import ConfigManager class ConcreteApplication(IApplication): """Konkrete Anwendungsimplementierung für Tests.""" def __init__(self, debug=False): super().__init__(debug) self.initialized = False self.started = False self.stopped = False async def initialize(self): self.initialized = True async def start(self): self.started = True async def stop(self): self.stopped = True class TestIApplication: """Tests für IApplication-Basisklasse.""" @pytest.fixture def app(self): """Erstellt eine Test-Anwendung.""" return ConcreteApplication(debug=True) def test_app_initialization(self, app): """Testet die Anwendungs-Initialisierung.""" assert app.debug is True assert app.running is False assert isinstance(app.services, ServiceContainer) assert isinstance(app.events, EventManager) assert isinstance(app.config_manager, ConfigManager) def test_config_alias(self, app): """Testet den config-Alias.""" assert app.config == app.config_manager def test_base_path(self, app): """Testet den Basis-Pfad.""" assert isinstance(app.base_path, Path) def test_get_path(self, app): """Testet get_path.""" path = app.get_path("subdir", "file.txt") assert path.name == "file.txt" assert "subdir" in str(path) def test_shutdown(self, app): """Testet shutdown.""" app.shutdown() assert app._shutdown_event.is_set() class TestServerApplication: """Tests für ServerApplication.""" @pytest.fixture def config_file(self, temp_dir): """Erstellt eine Server-Konfigurationsdatei.""" config = { "network": { "command_port": 0, # Ephemeral port "audio_input_port": 0, "audio_output_port": 0, "music_port": 0 }, "plugins": {"enabled": False}, "satellites_directory": str(temp_dir / "satellites") } path = temp_dir / "server_config.json" path.write_text(json.dumps(config)) return path @pytest.fixture def server(self, config_file): """Erstellt eine ServerApplication.""" return ServerApplication(config_path=config_file, debug=True) @pytest.mark.asyncio async def test_server_initialization(self, server): """Testet die Server-Initialisierung.""" await server.initialize() assert server._server_config is not None assert server.satellites is not None assert server.registration is not None assert server.plugins is not None @pytest.mark.asyncio async def test_server_config_access(self, server): """Testet den Konfigurationszugriff.""" await server.initialize() # Port 0 = ephemeral, wird vom System zugewiesen assert server.server_config.network.command_port == 0 @pytest.mark.asyncio async def test_server_config_not_initialized(self, server): """Testet Zugriff vor Initialisierung.""" with pytest.raises(RuntimeError, match="nicht initialisiert"): _ = server.server_config @pytest.mark.asyncio async def test_server_start(self, server): """Testet den Server-Start.""" await server.initialize() await server.start() # Services sollten gestartet sein @pytest.mark.asyncio async def test_server_stop(self, server): """Testet das Server-Stoppen.""" await server.initialize() await server.start() await server.stop() # Sollte ohne Fehler durchlaufen class TestClientApplication: """Tests für ClientApplication.""" @pytest.fixture def config_file(self, temp_dir): """Erstellt eine Client-Konfigurationsdatei.""" config = { "server": {"host": "localhost", "port": 2101}, "identity": {"room": "test", "alias": "TestClient"} } path = temp_dir / "client_config.json" path.write_text(json.dumps(config)) return path @pytest.fixture def client(self, config_file): """Erstellt eine ClientApplication.""" return ClientApplication( host="192.168.1.100", port=3000, room="wohnzimmer", alias="TestEcho", config_path=config_file, debug=True ) @pytest.mark.asyncio async def test_client_initialization(self, client): """Testet die Client-Initialisierung.""" await client.initialize() assert client._client_config is not None def test_client_cli_params_override(self, client): """Testet CLI-Parameter-Überschreibung.""" assert client.server_host == "192.168.1.100" assert client.server_port == 3000 assert client.room == "wohnzimmer" assert client.alias == "TestEcho" @pytest.mark.asyncio async def test_client_config_access(self, client): """Testet den Konfigurationszugriff.""" await client.initialize() assert client.client_config is not None @pytest.mark.asyncio async def test_client_start(self, client): """Testet den Client-Start (ohne echten Server).""" await client.initialize() await client.start() # Ohne echten Server bleibt is_connected False # Der eigentliche Verbindungstest ist in test_network_transport.py # _running wird nur in run() gesetzt, nicht in start() assert client.is_connected is False # Kein Server erreichbar @pytest.mark.asyncio async def test_client_stop(self, client): """Testet das Client-Stoppen.""" await client.initialize() await client.start() await client.stop() assert client.is_connected is False class TestStandaloneApplication: """Tests für StandaloneApplication.""" @pytest.fixture def config_file(self, temp_dir): """Erstellt eine Standalone-Konfigurationsdatei.""" config = { "room": "standalone_test", "alias": "TrixyTest", "plugins": {"enabled": False, "plugin_directory": str(temp_dir / "plugins")} } path = temp_dir / "standalone_config.json" path.write_text(json.dumps(config)) return path @pytest.fixture def standalone(self, config_file): """Erstellt eine StandaloneApplication.""" return StandaloneApplication(config_path=config_file, debug=True) @pytest.mark.asyncio async def test_standalone_initialization(self, standalone): """Testet die Standalone-Initialisierung.""" await standalone.initialize() assert standalone._standalone_config is not None assert standalone.plugins is not None @pytest.mark.asyncio async def test_standalone_config_access(self, standalone): """Testet den Konfigurationszugriff.""" await standalone.initialize() assert standalone.standalone_config.room == "standalone_test" assert standalone.standalone_config.alias == "TrixyTest" def test_standalone_room_alias(self, standalone): """Testet room und alias Eigenschaften.""" # Vor Initialisierung assert standalone.room == "standalone" # Default assert standalone.alias == "Trixy" # Default @pytest.mark.asyncio async def test_standalone_start(self, standalone): """Testet den Standalone-Start.""" await standalone.initialize() await standalone.start() @pytest.mark.asyncio async def test_standalone_stop(self, standalone): """Testet das Standalone-Stoppen.""" await standalone.initialize() await standalone.start() await standalone.stop() class TestTrainerApplication: """Tests für TrainerApplication.""" @pytest.fixture def config_file(self, temp_dir): """Erstellt eine Trainer-Konfigurationsdatei.""" config = { "wakeword": {"model_name": "test", "epochs": 10}, "data": { "raw_data_directory": str(temp_dir / "data"), "processed_data_directory": str(temp_dir / "processed"), "cache_directory": str(temp_dir / "cache") }, "output": {"models_directory": str(temp_dir / "models")} } path = temp_dir / "trainer_config.json" path.write_text(json.dumps(config)) return path @pytest.fixture def trainer(self, config_file): """Erstellt eine TrainerApplication.""" return TrainerApplication( target=TrainingTarget.WAKEWORD, config_path=config_file, debug=True ) def test_training_targets(self): """Testet TrainingTarget-Enum.""" assert hasattr(TrainingTarget, "WAKEWORD") assert hasattr(TrainingTarget, "VOICE_RECOGNITION") assert hasattr(TrainingTarget, "ALL") @pytest.mark.asyncio async def test_trainer_initialization(self, trainer): """Testet die Trainer-Initialisierung.""" await trainer.initialize() assert trainer._trainer_config is not None @pytest.mark.asyncio async def test_trainer_config_access(self, trainer): """Testet den Konfigurationszugriff.""" await trainer.initialize() assert trainer.trainer_config.wakeword.model_name == "test" assert trainer.trainer_config.wakeword.epochs == 10 def test_trainer_target(self, trainer): """Testet die target-Eigenschaft.""" assert trainer.target == TrainingTarget.WAKEWORD @pytest.mark.asyncio async def test_trainer_start_creates_directories(self, trainer, temp_dir): """Testet, dass Start Verzeichnisse erstellt.""" await trainer.initialize() await trainer.start() # Verzeichnisse sollten erstellt worden sein assert (temp_dir / "data").exists() assert (temp_dir / "processed").exists() assert (temp_dir / "cache").exists() assert (temp_dir / "models").exists() @pytest.mark.asyncio async def test_trainer_stop(self, trainer): """Testet das Trainer-Stoppen.""" await trainer.initialize() await trainer.stop() class TestApplicationIntegration: """Integrationstests für Anwendungsklassen.""" @pytest.fixture def config_dir(self, temp_dir): """Erstellt ein Konfigurations-Verzeichnis.""" config_dir = temp_dir / "config" config_dir.mkdir() # Server-Konfiguration (config_dir / "server_config.json").write_text(json.dumps({ "network": {"command_port": 2101}, "plugins": {"enabled": False}, "satellites_directory": str(temp_dir / "satellites") })) # Client-Konfiguration (config_dir / "client_config.json").write_text(json.dumps({ "server": {"host": "localhost", "port": 2101} })) # Standalone-Konfiguration (config_dir / "standalone_config.json").write_text(json.dumps({ "plugins": {"enabled": False} })) return config_dir @pytest.mark.asyncio async def test_server_client_config_compatibility(self, config_dir): """Testet Konfigurationskompatibilität zwischen Server und Client.""" server = ServerApplication( config_path=config_dir / "server_config.json", debug=True ) await server.initialize() client = ClientApplication( host="localhost", port=server.server_config.network.command_port, room="test", alias="test", config_path=config_dir / "client_config.json", debug=True ) await client.initialize() assert client.server_port == server.server_config.network.command_port await server.stop() await client.stop()