"""Tests for backend/services/ffmpeg_utils.py — Phase 1 Wave 3 (issue #76). Covers :func:`resolve_ffprobe`'s env-first / PATH-fallback cascade and the legacy-name alias (``FFPROBE_PATH`` still works, ``OMNIVOICE_FFPROBE_PATH`` takes precedence). """ import os import pytest @pytest.fixture(autouse=True) def _clean_env(monkeypatch): """Strip both ffprobe env vars before each test so we control the cascade.""" monkeypatch.delenv("OMNIVOICE_FFPROBE_PATH", raising=False) monkeypatch.delenv("FFPROBE_PATH", raising=False) def test_resolve_ffprobe_prefers_omnivoice_env_var(monkeypatch, tmp_path): """OMNIVOICE_FFPROBE_PATH set to a real file → that path wins.""" from services import ffmpeg_utils fake = tmp_path / "ffprobe" fake.write_text("#!/bin/sh\necho 1\n") fake.chmod(0o755) monkeypatch.setenv("OMNIVOICE_FFPROBE_PATH", str(fake)) assert ffmpeg_utils.resolve_ffprobe() == str(fake) def test_resolve_ffprobe_falls_back_to_legacy_FFPROBE_PATH(monkeypatch, tmp_path): """OMNIVOICE_FFPROBE_PATH absent but legacy FFPROBE_PATH set → legacy used.""" from services import ffmpeg_utils fake = tmp_path / "ffprobe-legacy" fake.write_text("#!/bin/sh\necho 1\n") fake.chmod(0o755) monkeypatch.setenv("FFPROBE_PATH", str(fake)) assert ffmpeg_utils.resolve_ffprobe() == str(fake) def test_resolve_ffprobe_omnivoice_path_takes_precedence_over_legacy( monkeypatch, tmp_path, ): """Both env vars set → OMNIVOICE_FFPROBE_PATH wins.""" from services import ffmpeg_utils canonical = tmp_path / "ffprobe-canonical" legacy = tmp_path / "ffprobe-legacy" for f in (canonical, legacy): f.write_text("#!/bin/sh\necho 1\n") f.chmod(0o755) monkeypatch.setenv("OMNIVOICE_FFPROBE_PATH", str(canonical)) monkeypatch.setenv("FFPROBE_PATH", str(legacy)) assert ffmpeg_utils.resolve_ffprobe() == str(canonical) def test_resolve_ffprobe_falls_back_to_PATH(monkeypatch, tmp_path): """No env var → shutil.which result is returned.""" from services import ffmpeg_utils # Stub shutil.which inside the module so we do not depend on the host PATH. fake = "/fake/path/from/system/ffprobe" def _fake_which(name): if name == "ffprobe": return fake return None monkeypatch.setattr(ffmpeg_utils, "shutil", _ShutilStub(_fake_which)) assert ffmpeg_utils.resolve_ffprobe() == fake def test_resolve_ffprobe_returns_None_when_nothing_resolves(monkeypatch): """No env, no PATH → returns None (no crash).""" from services import ffmpeg_utils monkeypatch.setattr(ffmpeg_utils, "shutil", _ShutilStub(lambda _name: None)) assert ffmpeg_utils.resolve_ffprobe() is None def test_resolve_ffprobe_env_var_with_command_name_resolves_via_which( monkeypatch, tmp_path, ): """Legacy shape: env var set to a bare command name (not a path) — resolve via shutil.which so older Tauri shells that set FFPROBE_PATH=ffprobe still work.""" from services import ffmpeg_utils fake = tmp_path / "ffprobe" fake.write_text("#!/bin/sh\necho 1\n") fake.chmod(0o755) def _fake_which(name): if name == "ffprobe": return str(fake) return None monkeypatch.setattr(ffmpeg_utils, "shutil", _ShutilStub(_fake_which)) monkeypatch.setenv("OMNIVOICE_FFPROBE_PATH", "ffprobe") assert ffmpeg_utils.resolve_ffprobe() == str(fake) class _ShutilStub: """A tiny shim that mimics the parts of shutil ffmpeg_utils touches. monkeypatching the whole module avoids pulling in real PATH lookups while keeping the existing `import shutil` call sites untouched. """ def __init__(self, which_impl): self._which = which_impl def which(self, name): return self._which(name)