| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- # -*- 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()
|