ytptube/app/tests/test_subtitle.py
2026-01-09 18:57:51 +03:00

113 lines
3.6 KiB
Python

from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
import pytest
from app.library.Subtitle import Subtitle, ms_to_timestamp
class TestMsToTimestamp:
def test_ms_to_timestamp_basic(self) -> None:
assert ms_to_timestamp(0) == "0:00:00.00"
assert ms_to_timestamp(9) == "0:00:00.00"
assert ms_to_timestamp(10) == "0:00:00.01"
assert ms_to_timestamp(12345) == "0:00:12.34"
assert ms_to_timestamp(3600000 + 120000 + 3000) == "1:02:03.00", "1 hour, 2 minutes, 3 seconds"
assert ms_to_timestamp(12 * 3600000 + 34 * 60000 + 56 * 1000 + 780) == "12:34:56.78", (
"Over 10 hours (SubStation limit is < 10h, our override must exceed)"
)
assert ms_to_timestamp(37 * 3600000 + 12 * 60000 + 34 * 1000 + 560) == "37:12:34.56", (
"Well over 36 hours to ensure no clamping at 9:59:59.99"
)
@pytest.mark.asyncio
async def test_make_unsupported_extension(tmp_path: Path) -> None:
srt = tmp_path / "sub.txt"
srt.write_text("not a subtitle")
sub = Subtitle()
with pytest.raises(Exception, match="subtitle type is not supported"):
await sub.make(srt)
@pytest.mark.asyncio
async def test_make_vtt_reads_file(tmp_path: Path) -> None:
vtt = tmp_path / "file.vtt"
content = "WEBVTT\n\n00:00:00.00 --> 00:00:01.00\nHello"
vtt.write_text(content)
sub = Subtitle()
out = await sub.make(vtt)
assert out == content
class _DummySubs:
def __init__(self, events):
self.events = events
self.snapshot = None
def to_string(self, fmt: str) -> str: # noqa: ARG002
# Record end times to verify pop behavior
self.snapshot = [e.end for e in self.events]
return "OUT"
@pytest.mark.asyncio
async def test_make_no_events_raises(tmp_path: Path) -> None:
srt = tmp_path / "sub.srt"
srt.write_text("dummy")
with patch("app.library.Subtitle.pysubs2.load") as mock_load:
mock_load.return_value = _DummySubs(events=[])
sub = Subtitle()
with pytest.raises(Exception, match="No subtitle events were found"):
await sub.make(srt)
@pytest.mark.asyncio
async def test_make_single_event_returns_vtt(tmp_path: Path) -> None:
srt = tmp_path / "sub.ass"
srt.write_text("dummy")
single = SimpleNamespace(end=1000)
d = _DummySubs(events=[single])
with patch("app.library.Subtitle.pysubs2.load", return_value=d):
sub = Subtitle()
out = await sub.make(srt)
assert out == "OUT"
assert d.snapshot == [1000], "Snapshot should contain the single event"
@pytest.mark.asyncio
async def test_make_two_events_pop_first_when_ends_equal(tmp_path: Path) -> None:
srt = tmp_path / "sub.srt"
srt.write_text("dummy")
e1 = SimpleNamespace(end=5000)
e2 = SimpleNamespace(end=5000)
d = _DummySubs(events=[e1, e2])
with patch("app.library.Subtitle.pysubs2.load", return_value=d):
sub = Subtitle()
out = await sub.make(srt)
assert out == "OUT"
assert d.snapshot == [5000], "Since ends are equal, first should be popped => only last remains"
@pytest.mark.asyncio
async def test_make_two_events_no_pop_when_different(tmp_path: Path) -> None:
srt = tmp_path / "sub.srt"
srt.write_text("dummy")
e1 = SimpleNamespace(end=5000)
e2 = SimpleNamespace(end=6000)
d = _DummySubs(events=[e1, e2])
with patch("app.library.Subtitle.pysubs2.load", return_value=d):
sub = Subtitle()
out = await sub.make(srt)
assert out == "OUT"
assert d.snapshot == [5000, 6000], "Both remain since ends differ"