# -*- coding: utf-8 -*- """ Mock-Objekte für Plugin-Tests. Bietet Mock-Implementierungen der Trixy-Kernkomponenten. """ from __future__ import annotations import asyncio from dataclasses import dataclass, field from datetime import datetime from typing import Any, Callable from enum import Enum, auto class MockServiceState(Enum): """Zustand eines Mock-Services.""" STOPPED = auto() STARTING = auto() RUNNING = auto() STOPPING = auto() @dataclass class MockEvent: """Ein Mock-Event.""" name: str data: dict[str, Any] timestamp: datetime = field(default_factory=datetime.now) cancelled: bool = False def cancel(self) -> None: """Bricht das Event ab.""" self.cancelled = True class MockEventManager: """ Mock-Implementierung des EventManagers. Zeichnet alle Events auf und ermöglicht das Testen von Event-basierten Interaktionen. Example: manager = MockEventManager() # Callback für Tests registrieren manager.set_emit_callback(my_callback) # Events werden aufgezeichnet manager.emit("test_event", {"key": "value"}) # Events abrufen events = manager.get_events("test_event") """ def __init__(self) -> None: """Initialisiert den Mock-EventManager.""" self._events: list[MockEvent] = [] self._handlers: dict[str, list[Callable]] = {} self._emit_callback: Callable[[str, dict], None] | None = None self._running = False @property def events(self) -> list[MockEvent]: """Alle aufgezeichneten Events.""" return self._events.copy() def set_emit_callback( self, callback: Callable[[str, dict], None], ) -> None: """Setzt einen Callback für emittierte Events.""" self._emit_callback = callback def emit( self, name: str, data: dict[str, Any] | None = None, ) -> MockEvent: """ Emittiert ein Event. Args: name: Event-Name data: Event-Daten Returns: Das erstellte Event """ event = MockEvent(name=name, data=data or {}) self._events.append(event) # Callback benachrichtigen if self._emit_callback: self._emit_callback(name, data or {}) # Handler aufrufen if name in self._handlers: for handler in self._handlers[name]: try: handler(name, data or {}) except Exception: pass return event async def emit_async( self, name: str, data: dict[str, Any] | None = None, ) -> MockEvent: """Emittiert ein Event asynchron.""" return self.emit(name, data) def subscribe( self, event_name: str, handler: Callable, ) -> None: """Registriert einen Event-Handler.""" if event_name not in self._handlers: self._handlers[event_name] = [] self._handlers[event_name].append(handler) def unsubscribe( self, event_name: str, handler: Callable, ) -> bool: """Entfernt einen Event-Handler.""" if event_name in self._handlers: try: self._handlers[event_name].remove(handler) return True except ValueError: pass return False def get_events(self, name: str | None = None) -> list[MockEvent]: """ Gibt Events zurück. Args: name: Optionaler Filter nach Event-Name Returns: Liste der Events """ if name is None: return self._events.copy() return [e for e in self._events if e.name == name] def clear_events(self) -> None: """Löscht alle aufgezeichneten Events.""" self._events.clear() def has_event(self, name: str) -> bool: """Prüft, ob ein Event existiert.""" return any(e.name == name for e in self._events) def event_count(self, name: str | None = None) -> int: """Gibt die Anzahl der Events zurück.""" return len(self.get_events(name)) class MockSatelliteManager: """ Mock-Implementierung des SatelliteManagers. Simuliert Satellite-Verbindungen und -Operationen. Example: manager = MockSatelliteManager() # Satellites hinzufügen manager.add_satellite("living_room", {"alias": "Wohnzimmer"}) # Operationen testen satellite = manager.get("living_room") """ def __init__(self) -> None: """Initialisiert den Mock-SatelliteManager.""" self._satellites: dict[str, dict[str, Any]] = {} self._connected: set[str] = set() def add_satellite( self, room_id: str, data: dict[str, Any] | None = None, ) -> None: """Fügt einen Mock-Satellite hinzu.""" self._satellites[room_id] = { "room_id": room_id, "connected": False, **(data or {}), } def remove_satellite(self, room_id: str) -> bool: """Entfernt einen Satellite.""" if room_id in self._satellites: del self._satellites[room_id] self._connected.discard(room_id) return True return False def get(self, room_id: str) -> dict[str, Any] | None: """Gibt einen Satellite zurück.""" return self._satellites.get(room_id) def get_all(self) -> list[dict[str, Any]]: """Gibt alle Satellites zurück.""" return list(self._satellites.values()) def connect(self, room_id: str) -> bool: """Simuliert eine Verbindung.""" if room_id in self._satellites: self._connected.add(room_id) self._satellites[room_id]["connected"] = True return True return False def disconnect(self, room_id: str) -> bool: """Simuliert eine Trennung.""" if room_id in self._connected: self._connected.discard(room_id) if room_id in self._satellites: self._satellites[room_id]["connected"] = False return True return False def is_connected(self, room_id: str) -> bool: """Prüft, ob ein Satellite verbunden ist.""" return room_id in self._connected def get_connected(self) -> list[str]: """Gibt alle verbundenen Satellites zurück.""" return list(self._connected) def clear(self) -> None: """Entfernt alle Satellites.""" self._satellites.clear() self._connected.clear() class MockApplication: """ Mock-Implementierung der Trixy-Application. Bietet Zugriff auf alle Mock-Komponenten. Example: app = MockApplication() # Services verwenden event_manager = app.event_manager satellite_manager = app.satellite_manager # Konfiguration setzen app.set_config("key", "value") """ def __init__( self, event_manager: MockEventManager | None = None, satellite_manager: MockSatelliteManager | None = None, config: dict[str, Any] | None = None, ) -> None: """ Initialisiert die Mock-Application. Args: event_manager: Optionaler EventManager satellite_manager: Optionaler SatelliteManager config: Optionale Konfiguration """ self._event_manager = event_manager or MockEventManager() self._satellite_manager = satellite_manager or MockSatelliteManager() self._config = config or {} self._services: dict[str, Any] = {} self._state = MockServiceState.STOPPED @property def event_manager(self) -> MockEventManager: """Der EventManager.""" return self._event_manager @property def satellite_manager(self) -> MockSatelliteManager: """Der SatelliteManager.""" return self._satellite_manager @property def config(self) -> dict[str, Any]: """Die Konfiguration.""" return self._config @property def state(self) -> MockServiceState: """Der Zustand der Application.""" return self._state @property def is_running(self) -> bool: """Läuft die Application?""" return self._state == MockServiceState.RUNNING def set_config(self, key: str, value: Any) -> None: """Setzt einen Konfigurationswert.""" self._config[key] = value def get_config(self, key: str, default: Any = None) -> Any: """Gibt einen Konfigurationswert zurück.""" return self._config.get(key, default) def register_service(self, name: str, service: Any) -> None: """Registriert einen Service.""" self._services[name] = service def get_service(self, name: str) -> Any: """Gibt einen Service zurück.""" return self._services.get(name) def has_service(self, name: str) -> bool: """Prüft, ob ein Service existiert.""" return name in self._services async def start(self) -> None: """Startet die Application.""" self._state = MockServiceState.STARTING await asyncio.sleep(0) # Simuliert async-Start self._state = MockServiceState.RUNNING async def stop(self) -> None: """Stoppt die Application.""" self._state = MockServiceState.STOPPING await asyncio.sleep(0) # Simuliert async-Stop self._state = MockServiceState.STOPPED def emit(self, name: str, data: dict[str, Any] | None = None) -> MockEvent: """Emittiert ein Event.""" return self._event_manager.emit(name, data) class MockPlugin: """ Mock-Implementierung eines Plugins. Kann als Basis für Test-Plugins oder zum Testen von Plugin-Interaktionen verwendet werden. Example: plugin = MockPlugin("test_plugin") # Lifecycle simulieren await plugin.on_start() # Methoden aufrufen plugin.set_state("key", "value") # Aufräumen await plugin.on_stop() """ # Plugin-Metadaten NAME = "mock_plugin" VERSION = "1.0.0" DESCRIPTION = "Ein Mock-Plugin für Tests" AUTHOR = "Test" def __init__( self, name: str | None = None, application: MockApplication | None = None, config: dict[str, Any] | None = None, ) -> None: """ Initialisiert das Mock-Plugin. Args: name: Plugin-Name application: Application-Referenz config: Plugin-Konfiguration """ self._name = name or self.NAME self._application = application self._config = config or {} self._enabled = True self._state: dict[str, Any] = {} self._event_handlers: dict[str, list[Callable]] = {} self._started = False @property def name(self) -> str: """Plugin-Name.""" return self._name @property def application(self) -> MockApplication | None: """Application-Referenz.""" return self._application @property def config(self) -> dict[str, Any]: """Plugin-Konfiguration.""" return self._config @config.setter def config(self, value: dict[str, Any]) -> None: """Setzt die Konfiguration.""" self._config = value @property def enabled(self) -> bool: """Plugin aktiviert?""" return self._enabled @enabled.setter def enabled(self, value: bool) -> None: """Aktiviert/Deaktiviert das Plugin.""" self._enabled = value @property def is_started(self) -> bool: """Wurde das Plugin gestartet?""" return self._started def get_state(self) -> dict[str, Any]: """Gibt den Plugin-Zustand zurück.""" return self._state.copy() def set_state(self, state: dict[str, Any] | str, value: Any = None) -> None: """ Setzt den Plugin-Zustand. Args: state: Dictionary oder einzelner Key value: Wert wenn state ein Key ist """ if isinstance(state, dict): self._state.update(state) else: self._state[state] = value def register_event_handler( self, event_name: str, handler: Callable, ) -> None: """Registriert einen Event-Handler.""" if event_name not in self._event_handlers: self._event_handlers[event_name] = [] self._event_handlers[event_name].append(handler) async def on_pre_start(self) -> None: """Pre-Start Hook.""" pass async def on_start(self) -> None: """Start Hook.""" self._started = True async def on_post_start(self) -> None: """Post-Start Hook.""" pass async def on_pre_stop(self) -> None: """Pre-Stop Hook.""" pass async def on_stop(self) -> None: """Stop Hook.""" self._started = False async def on_post_stop(self) -> None: """Post-Stop Hook.""" pass def emit(self, name: str, data: dict[str, Any] | None = None) -> None: """Emittiert ein Event.""" if self._application: self._application.emit(name, data) def reload_config(self) -> None: """Lädt die Konfiguration neu (Mock).""" pass def save_config(self) -> None: """Speichert die Konfiguration (Mock).""" pass class MockConfigManager: """ Mock-Implementierung des ConfigManagers. Example: config = MockConfigManager({"key": "value"}) value = config.get("key") """ def __init__(self, data: dict[str, Any] | None = None) -> None: """Initialisiert den Mock-ConfigManager.""" self._data = data or {} def get(self, key: str, default: Any = None) -> Any: """Gibt einen Wert zurück.""" keys = key.split(".") value = self._data for k in keys: if isinstance(value, dict) and k in value: value = value[k] else: return default return value def set(self, key: str, value: Any) -> None: """Setzt einen Wert.""" keys = key.split(".") data = self._data for k in keys[:-1]: if k not in data: data[k] = {} data = data[k] data[keys[-1]] = value def has(self, key: str) -> bool: """Prüft, ob ein Key existiert.""" return self.get(key) is not None def to_dict(self) -> dict[str, Any]: """Gibt alle Daten als Dict zurück.""" return self._data.copy() class MockScheduler: """ Mock-Implementierung des Schedulers. Example: scheduler = MockScheduler() scheduler.schedule("task", cron="0 * * * *") """ def __init__(self) -> None: """Initialisiert den Mock-Scheduler.""" self._tasks: dict[str, dict[str, Any]] = {} self._triggered: list[str] = [] def schedule( self, name: str, cron: str | None = None, interval: float | None = None, handler: Callable | None = None, ) -> None: """Plant eine Aufgabe.""" self._tasks[name] = { "cron": cron, "interval": interval, "handler": handler, } def unschedule(self, name: str) -> bool: """Entfernt eine Aufgabe.""" if name in self._tasks: del self._tasks[name] return True return False def trigger(self, name: str) -> bool: """Triggert eine Aufgabe manuell.""" if name in self._tasks: self._triggered.append(name) handler = self._tasks[name].get("handler") if handler: try: handler() except Exception: pass return True return False def get_tasks(self) -> list[str]: """Gibt alle Task-Namen zurück.""" return list(self._tasks.keys()) def get_triggered(self) -> list[str]: """Gibt alle getriggerten Tasks zurück.""" return self._triggered.copy() def clear_triggered(self) -> None: """Löscht die Trigger-Historie.""" self._triggered.clear()