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