import asyncio from pathlib import Path import pytest from tutor.config import Settings from tutor.services.asr.base import ASRClient, ASRError, FakeASRClient, Transcription from tutor.services.asr.factory import create_asr_client def test_fake_satisfies_protocol_and_records_calls() -> None: fake = FakeASRClient(text="hello world") assert isinstance(fake, ASRClient) result = asyncio.run(fake.transcribe(Path("/tmp/x.wav"), language="en")) assert isinstance(result, Transcription) assert result.text == "hello world" assert result.language == "en" assert fake.calls == [Path("/tmp/x.wav")] def test_factory_returns_fake_by_default() -> None: client = create_asr_client(Settings(asr_provider="fake")) assert isinstance(client, FakeASRClient) def test_factory_builds_faster_whisper_without_loading_model() -> None: # Construction must be cheap: the model loads lazily on first transcribe, # so building the client must not download or load anything. client = create_asr_client( Settings(asr_provider="faster_whisper", asr_model="tiny", asr_cpu_threads=1) ) from tutor.services.asr.faster_whisper_client import FasterWhisperASRClient assert isinstance(client, FasterWhisperASRClient) assert client._model is None # not loaded yet assert client.model_size == "tiny" assert client.cpu_threads == 1 def test_factory_rejects_unknown_provider() -> None: settings = Settings(asr_provider="fake") object.__setattr__(settings, "asr_provider", "bogus") # bypass Literal validation with pytest.raises(ValueError, match="unknown ASR provider"): create_asr_client(settings) def test_faster_whisper_raises_on_missing_file() -> None: from tutor.services.asr.faster_whisper_client import FasterWhisperASRClient client = FasterWhisperASRClient(model_size="tiny") with pytest.raises(ASRError, match="audio file not found"): asyncio.run(client.transcribe(Path("/nonexistent/audio.wav")))