| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- # -*- coding: utf-8 -*-
- """
- Tests für das DeepSpeech STT Plugin.
- """
- import pytest
- from pathlib import Path
- from unittest.mock import MagicMock, AsyncMock, patch
- import importlib.util
- # Plugin-main.py direkt laden
- plugin_path = Path(__file__).parent.parent / "main.py"
- spec = importlib.util.spec_from_file_location("deepspeech_plugin", plugin_path)
- deepspeech_module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(deepspeech_module)
- DeepSpeechSTTPlugin = deepspeech_module.DeepSpeechSTTPlugin
- DeepSpeechSTTProvider = deepspeech_module.DeepSpeechSTTProvider
- DEEPSPEECH_MODELS = deepspeech_module.DEEPSPEECH_MODELS
- from trixy_core.audio.stt import STTConfig, STTState
- class TestDeepSpeechModelsCatalog:
- """Tests für den DeepSpeech-Modell-Katalog."""
- def test_models_catalog_not_empty(self):
- """Prüft, dass der Modellkatalog nicht leer ist."""
- assert len(DEEPSPEECH_MODELS) > 0
- def test_models_have_required_fields(self):
- """Prüft, dass alle Modelle die erforderlichen Felder haben."""
- required_fields = ["name", "model_url", "scorer_url", "model_file", "scorer_file", "size", "description"]
- for model_id, model_info in DEEPSPEECH_MODELS.items():
- for field in required_fields:
- assert field in model_info, f"Modell {model_id} fehlt Feld '{field}'"
- def test_german_model_available(self):
- """Prüft, dass das deutsche Modell verfügbar ist."""
- assert "de" in DEEPSPEECH_MODELS
- def test_english_model_available(self):
- """Prüft, dass das englische Modell verfügbar ist."""
- assert "en" in DEEPSPEECH_MODELS
- def test_model_urls_are_valid(self):
- """Prüft, dass alle Modell-URLs gültige URLs sind."""
- for model_id, model_info in DEEPSPEECH_MODELS.items():
- model_url = model_info["model_url"]
- scorer_url = model_info["scorer_url"]
- assert model_url.startswith("https://"), f"Modell {model_id} hat ungültige Model-URL"
- assert scorer_url.startswith("https://"), f"Modell {model_id} hat ungültige Scorer-URL"
- class TestDeepSpeechSTTProvider:
- """Tests für den DeepSpeechSTTProvider."""
- @pytest.fixture
- def provider(self, tmp_path):
- """Erstellt einen Provider für Tests."""
- config = STTConfig(language="de-DE")
- return DeepSpeechSTTProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="de",
- auto_download=False,
- )
- def test_provider_name(self, provider):
- """Prüft den Provider-Namen."""
- assert provider.name == "deepspeech"
- def test_supported_languages(self, provider):
- """Prüft die unterstützten Sprachen."""
- languages = provider.supported_languages
- assert "de-DE" in languages
- assert "en-US" in languages
- def test_supports_streaming(self, provider):
- """Prüft Streaming-Unterstützung."""
- assert provider.supports_streaming is True
- def test_supports_word_timings(self, provider):
- """Prüft Wort-Timings-Unterstützung."""
- assert provider.supports_word_timings is True
- def test_initial_state(self, provider):
- """Prüft den initialen Zustand."""
- assert provider._model_loaded is False
- assert provider._model is None
- @pytest.mark.asyncio
- async def test_initialize_without_model_raises(self, provider):
- """Prüft, dass initialize ohne Modell fehlschlägt."""
- with pytest.raises(RuntimeError):
- await provider.initialize()
- @pytest.mark.asyncio
- async def test_transcribe_without_init_raises(self, provider, sample_audio_16khz):
- """Prüft, dass transcribe ohne Initialisierung fehlschlägt."""
- with pytest.raises(RuntimeError, match="DeepSpeech-Modell nicht geladen"):
- await provider.transcribe(sample_audio_16khz)
- class TestDeepSpeechSTTPlugin:
- """Tests für das DeepSpeechSTTPlugin."""
- @pytest.fixture
- def plugin(self, mock_application, tmp_path):
- """Erstellt ein Plugin für Tests."""
- plugin_path = tmp_path / "stt_deepspeech"
- plugin_path.mkdir()
- (plugin_path / "models").mkdir()
- config = {
- "name": "DeepSpeech STT",
- "enabled": True,
- "model": "de",
- "language": "de-DE",
- "auto_download": False,
- }
- return DeepSpeechSTTPlugin(mock_application, plugin_path, config)
- def test_plugin_initialization(self, plugin):
- """Prüft die Plugin-Initialisierung."""
- assert plugin._provider is None
- assert plugin.config.get("model") == "de"
- def test_plugin_path(self, plugin):
- """Prüft den Plugin-Pfad."""
- assert plugin.plugin_path.exists()
- @pytest.mark.asyncio
- async def test_on_unload_without_provider(self, plugin):
- """Prüft on_unload ohne initialisierten Provider."""
- await plugin.on_unload()
- assert plugin._provider is None
- class TestDeepSpeechDownload:
- """Tests für die Download-Funktionalität."""
- @pytest.fixture
- def mock_event_manager(self):
- """Erstellt einen Mock-EventManager."""
- em = MagicMock()
- em.emit = AsyncMock()
- return em
- @pytest.mark.asyncio
- async def test_download_unknown_model_raises(self, tmp_path):
- """Prüft, dass unbekannte Modelle einen Fehler werfen."""
- config = STTConfig(language="de-DE")
- provider = DeepSpeechSTTProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="unknown_model",
- auto_download=True,
- )
- # Entweder "Unbekanntes Modell" (wenn deepspeech installiert)
- # oder "nicht installiert" (wenn deepspeech fehlt)
- with pytest.raises(RuntimeError, match="(Unbekanntes Modell|nicht installiert)"):
- await provider.initialize()
- def test_model_paths_configured(self, tmp_path):
- """Prüft, dass Modellpfade korrekt konfiguriert werden."""
- config = STTConfig(language="de-DE")
- provider = DeepSpeechSTTProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="de",
- auto_download=False,
- )
- # Nach dem Setzen der model_name sollten die Pfade noch None sein
- # bis initialize() aufgerufen wird
- assert provider._model_path is None
- assert provider._scorer_path is None
- class TestDeepSpeechEventHandling:
- """Tests für die Event-Verarbeitung."""
- @pytest.fixture
- def plugin_with_provider(self, mock_application, tmp_path):
- """Erstellt ein Plugin mit Mock-Provider."""
- plugin_path = tmp_path / "stt_deepspeech"
- plugin_path.mkdir()
- (plugin_path / "models").mkdir()
- config = {
- "name": "DeepSpeech STT",
- "enabled": True,
- "model": "de",
- "language": "de-DE",
- "auto_download": False,
- }
- plugin = DeepSpeechSTTPlugin(mock_application, plugin_path, config)
- # Mock-Provider setzen
- mock_provider = MagicMock()
- mock_provider.is_ready = True
- mock_provider.transcribe = AsyncMock(return_value=MagicMock(
- text="test text",
- confidence=0.95,
- language="de-DE",
- duration_seconds=1.0,
- processing_time_ms=100,
- word_timings=[],
- ))
- plugin._provider = mock_provider
- return plugin
- # Integration Tests (benötigen echte Installation)
- @pytest.mark.integration
- class TestDeepSpeechIntegration:
- """Integrationstests für DeepSpeech STT (benötigen Installation)."""
- @pytest.fixture(autouse=True)
- def skip_if_not_installed(self):
- """Überspringt Tests wenn deepspeech nicht installiert ist."""
- pytest.importorskip("deepspeech", reason="deepspeech nicht installiert")
- @pytest.fixture
- def provider_with_model(self, tmp_path):
- """Erstellt einen Provider mit echtem Modell."""
- config = STTConfig(language="de-DE")
- return DeepSpeechSTTProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="de",
- auto_download=True,
- )
- @pytest.mark.asyncio
- @pytest.mark.slow
- async def test_full_transcription_flow(self, provider_with_model, sample_audio_16khz):
- """Testet den vollständigen Transkriptions-Flow."""
- await provider_with_model.initialize()
- result = await provider_with_model.transcribe(sample_audio_16khz)
- assert result.text is not None
- assert result.provider == "deepspeech"
- assert result.processing_time_ms > 0
- await provider_with_model.shutdown()
|