"""An HLS artifact request names the session it belongs to, and gets nothing else. Sessions are keyed by rating_key ALONE, while a session's directory is per (key, offset, tag). So a seek-restart — or a second viewer of the same item — replaces the registered session, and the old viewer's next request for `seg_12.ts`, resolved by rating_key, was answered 200 with the NEW session's twelfth segment: bytes produced from a different `-ss` offset under `-copyts`, i.e. a timeline the client is not on. Wrong content with a success status is the one failure a player cannot notice, and the reclaim-on-404 added in round 4 could never fire on it (deep-review round 5). DB lane (`db` fixture) — skipped when no Postgres is reachable. """ import time import pytest from fastapi.testclient import TestClient import app.state as state from app.auth import current_user from app.db import get_db from app.main import app from app.models import PlexItem, PlexLibrary, User from app.plex import stream as plex_stream class _Proc: def poll(self): return 0 # finished: `wait_for` may not poll out its whole timeout on a missing file def terminate(self): pass def wait(self, timeout=None): return 0 class _Session: def __init__(self, session_id: str, directory): self.id = session_id self.dir = directory self.proc = _Proc() self.last_access = time.time() @pytest.fixture def sessions(monkeypatch): live: dict = {} monkeypatch.setattr(plex_stream, "_sessions", live) return live def _client(db, monkeypatch): monkeypatch.setattr(state, "_configured_cache", True) lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True) db.add(lib) db.flush() db.add( PlexItem( rating_key="m1", library_id=lib.id, kind="movie", title="A Movie", file_path="/media/a.mkv", playable="remux", ) ) user = User(email="hls@example.com") db.add(user) db.commit() app.dependency_overrides[get_db] = lambda: db app.dependency_overrides[current_user] = lambda: user return TestClient(app) def test_a_segment_is_served_from_the_session_the_url_names(db, monkeypatch, tmp_path, sessions): d = tmp_path / "m1_0_0_+0.00" d.mkdir() (d / "seg_12.ts").write_bytes(b"mine") sessions["m1"] = _Session("sid-a", d) client = _client(db, monkeypatch) try: r = client.get("/api/plex/stream/m1/hls/sid-a/seg_12.ts") finally: app.dependency_overrides.clear() assert r.status_code == 200, r.text assert r.content == b"mine" def test_a_segment_of_a_replaced_session_is_a_404_not_the_new_sessions_bytes( db, monkeypatch, tmp_path, sessions ): """Viewer A is at 0:00; viewer B seeks to 25:00 and their session takes the registry slot. A's next segment must fail honestly — the player reclaims at the playhead — not arrive as B's.""" theirs = tmp_path / "m1_1500_0_+0.00" theirs.mkdir() (theirs / "seg_12.ts").write_bytes(b"theirs") sessions["m1"] = _Session("sid-b", theirs) client = _client(db, monkeypatch) try: r = client.get("/api/plex/stream/m1/hls/sid-a/seg_12.ts") finally: app.dependency_overrides.clear() assert r.status_code == 404, r.text assert b"theirs" not in r.content def test_a_playlist_with_no_live_session_is_a_404_not_a_silent_restart_at_zero( db, monkeypatch, sessions ): """The entry playlist used to auto-start a session when none was registered — always at 0.0, silently throwing away the viewer's position. A 404 sends the player through its own reclaim, which restarts at the playhead.""" started: list[float] = [] monkeypatch.setattr( plex_stream, "start_session", lambda *a, **k: started.append(a[2] if len(a) > 2 else 0.0), ) client = _client(db, monkeypatch) try: r = client.get("/api/plex/stream/m1/hls/sid-gone/index.m3u8") finally: app.dependency_overrides.clear() assert r.status_code == 404, r.text assert started == [] def test_a_segment_that_will_never_arrive_fails_at_once(db, monkeypatch, tmp_path, sessions): """`stream_hls_file` is a SYNC route: it runs on the ~40-slot threadpool and holds a pooled DB connection while it waits. Waiting without the producer's handle polled out the whole 20 s even when that ffmpeg had already exited and no segment could ever appear — one ordinary seek past the produced region parked a worker and a connection for 20 s (deep-review round 5).""" d = tmp_path / "m1_0_0_+0.00" d.mkdir() sessions["m1"] = _Session("sid-a", d) # its _Proc.poll() reports a finished ffmpeg client = _client(db, monkeypatch) started = time.monotonic() try: r = client.get("/api/plex/stream/m1/hls/sid-a/seg_99999.ts") finally: app.dependency_overrides.clear() elapsed = time.monotonic() - started assert r.status_code == 404, r.text # The wait is 20 s; anything in that neighbourhood means the producer's exit was not consulted. assert elapsed < 2.0, f"waited {elapsed:.1f}s for a segment that can never arrive"