"""Tests for sermon-clean.engine.""" from pathlib import Path import pytest from sermon_clean.engine import ( SermonClean, parse_timestamp, format_timestamp, ) class TestTimestampParsing: @pytest.mark.parametrize("s,expected", [ ("30", 30.0), ("90", 90.0), ("1:30", 90.0), ("01:30", 90.0), ("1:30.5", 90.5), ("1:00:30", 3630.0), ("02:01:30.5", 7290.5), ]) def test_valid(self, s, expected): assert parse_timestamp(s) == expected @pytest.mark.parametrize("s", ["", "abc", "1:60", "60:60", "1.2.3"]) def test_invalid(self, s): with pytest.raises(ValueError): parse_timestamp(s) class TestFormatTimestamp: @pytest.mark.parametrize("seconds,expected", [ (0.0, "00:00.000"), (59.999, "00:59.999"), (60.0, "01:00.000"), (90.5, "01:30.500"), (3600.0, "01:00:00.000"), (3661.5, "01:01:01.500"), (7290.5, "02:01:30.500"), ]) def test_round_trip(self, seconds, expected): assert format_timestamp(seconds) == expected def test_roundtrip_parse(self): for s in [0.0, 5.0, 59.999, 60.0, 3630.0, 7290.5]: assert parse_timestamp(format_timestamp(s)) == pytest.approx(s) class TestBadSegment: def test_valid(self): from sermon_clean.engine import BadSegment s = BadSegment(10.0, 12.0, reason="misspoke") assert s.duration == 2.0 assert s.start == 10.0 assert s.end == 12.0 def test_zero_length_rejected(self): from sermon_clean.engine import BadSegment with pytest.raises(ValueError): BadSegment(10.0, 10.0) def test_negative_rejected(self): from sermon_clean.engine import BadSegment with pytest.raises(ValueError): BadSegment(15.0, 10.0) def test_long_window_rejected(self): from sermon_clean.engine import BadSegment # 90s window by default > 60s cap with pytest.raises(ValueError, match="suspiciously long"): BadSegment(0.0, 90.0) def test_sermon_clean_basic(tmp_path): """Smoke test: load a real audio file from the krystie profile cache.""" # Find any audio file in the krystie cache cache = Path("/root/.hermes/profiles/krystie/cache/audio") if not cache.exists(): pytest.skip("krystie audio cache not present") audio_files = sorted(cache.glob("*.ogg")) + sorted(cache.glob("*.mp3")) if not audio_files: pytest.skip("no audio files in krystie cache") audio = audio_files[0] sc = SermonClean(audio, workdir=tmp_path) assert sc.duration > 0 assert sc.codec["codec_name"] assert sc.codec["sample_rate"] seg = sc.add_segment(0.5, 1.0, reason="test") assert seg.start == 0.5 assert len(sc.segments) == 1 segs = sc.trim_segments() assert len(segs) == 2 # before + after the 0.5s window assert all(p.exists() for p in segs) # Verify the trimmed segments from sermon_clean.engine import probe_duration total = sum(probe_duration(p) for p in segs) assert total == pytest.approx(sc.duration - 0.5, abs=0.1)