"""Tests for sermon_clean.denoise + sermon_clean.batch.""" from pathlib import Path import pytest from sermon_clean.denoise import denoise, DenoiseResult from sermon_clean.batch import run_batch, _expand_globs, _make_output_path, BATCH_SUBCOMMANDS def _make_wav(path: Path, *, freq: float = 440.0, duration: float = 5.0) -> Path: import subprocess subprocess.run([ "ffmpeg", "-y", "-v", "error", "-f", "lavfi", "-i", f"sine=frequency={freq}:duration={duration}", str(path), ], check=True) return path # --------------------------------------------------------------------------- # denoise # --------------------------------------------------------------------------- class TestDenoise: def test_produces_output_file(self, tmp_path): src = _make_wav(tmp_path / "src.wav") out = tmp_path / "out.wav" result = denoise(src, out) assert isinstance(result, DenoiseResult) assert out.exists() assert out.stat().st_size > 1000 def test_custom_reduction_db(self, tmp_path): src = _make_wav(tmp_path / "src.wav") out = tmp_path / "out.wav" result = denoise(src, out, noise_reduction_db=20.0, noise_floor_db=-40.0) assert result.noise_reduction_db == 20.0 assert result.noise_floor_db == -40.0 # --------------------------------------------------------------------------- # batch helpers # --------------------------------------------------------------------------- class TestBatchHelpers: def test_expand_globs_dedupes(self, tmp_path): (tmp_path / "a.wav").write_bytes(b"x") (tmp_path / "b.wav").write_bytes(b"x") result = _expand_globs([str(tmp_path / "*.wav"), str(tmp_path / "a.wav")]) # Should be 2 unique files, not 3 assert len(result) == 2 names = sorted(p.name for p in result) assert names == ["a.wav", "b.wav"] def test_expand_globs_recursive(self, tmp_path): sub = tmp_path / "sub" sub.mkdir() (sub / "c.wav").write_bytes(b"x") (tmp_path / "a.wav").write_bytes(b"x") result = _expand_globs([str(tmp_path / "**" / "*.wav")]) assert len(result) == 2 def test_make_output_path_with_suffix(self, tmp_path): src = Path("/some/where/sermon.ogg") out = _make_output_path(src, tmp_path, "-normalized", None) assert out == tmp_path / "sermon-normalized.ogg" def test_make_output_path_with_extension_override(self, tmp_path): src = Path("/some/where/sermon.ogg") out = _make_output_path(src, tmp_path, "-normalized", ".wav") assert out == tmp_path / "sermon-normalized.wav" def test_batch_subcommands_includes_new_ones(self): for cmd in ["normalize", "denoise", "silence-stats", "threshold-tune"]: assert cmd in BATCH_SUBCOMMANDS # batch itself is NOT in BATCH_SUBCOMMANDS — recursive batch would be weird. assert "batch" not in BATCH_SUBCOMMANDS # --------------------------------------------------------------------------- # batch.run_batch end-to-end # --------------------------------------------------------------------------- class TestRunBatch: def test_batch_normalize_two_files(self, tmp_path): a = _make_wav(tmp_path / "sermon-a.wav", duration=3.0) b = _make_wav(tmp_path / "sermon-b.wav", duration=3.0) out_dir = tmp_path / "out" result = run_batch( "normalize", [a, b], out_dir, output_suffix="-normalized", ) assert result.n_ok == 2 assert result.n_failed == 0 assert (out_dir / "sermon-a-normalized.wav").exists() assert (out_dir / "sermon-b-normalized.wav").exists() def test_batch_denoise_with_extension_override(self, tmp_path): a = _make_wav(tmp_path / "sermon.wav", duration=3.0) out_dir = tmp_path / "out" result = run_batch( "denoise", [a], out_dir, output_suffix="-dn", output_extension=".wav", ) assert result.n_ok == 1 assert (out_dir / "sermon-dn.wav").exists() def test_batch_unknown_subcommand_raises(self, tmp_path): with pytest.raises(ValueError, match="unknown subcommand"): run_batch("nonexistent", [], tmp_path) def test_batch_one_failure_does_not_stop_batch(self, tmp_path): """One bad file shouldn't kill the batch — failures are collected.""" a = _make_wav(tmp_path / "good.wav", duration=3.0) bad = tmp_path / "does-not-exist.wav" # doesn't exist out_dir = tmp_path / "out" result = run_batch("normalize", [a, bad], out_dir, output_suffix="-n") assert result.n_ok == 1 assert result.n_failed == 1 assert (out_dir / "good-n.wav").exists() assert not (out_dir / "does-not-exist-n.wav").exists()