backend/tests/test_audio_tracks.py
Rama master · réplica de lectura del repositorio original.
"""Tests for `list_audio_tracks` service + GET /api/v1/audio/tracks route.
Plan B8 Task 1 — exposes the audio-track picker that the WaveformEditor
uses to switch between language tracks (Center for Dialog vs.
international audio).
"""
from __future__ import annotations
import os
from unittest.mock import patch
# ─── Service: list_audio_tracks ────────────────────────────────────────────
SAMPLE_PROBE = {
"streams": [
{
"index": 0,
"codec_type": "video",
"codec_name": "h264",
"tags": {},
"disposition": {},
},
{
"index": 1,
"codec_type": "audio",
"codec_name": "aac",
"channels": 2,
"tags": {"language": "jpn", "title": "Stereo"},
"disposition": {"default": 1, "forced": 0},
},
{
"index": 2,
"codec_type": "audio",
"codec_name": "ac3",
"channels": 6,
"tags": {"language": "eng", "title": "5.1 Surround"},
"disposition": {"default": 0, "forced": 0},
},
{
"index": 3,
"codec_type": "subtitle",
"codec_name": "ass",
"tags": {"language": "jpn"},
"disposition": {},
},
]
}
class TestListAudioTracks:
def test_returns_only_audio_streams(self):
"""Video and subtitle streams are filtered out."""
from services.audio_visualizer import list_audio_tracks
with patch("services.audio_visualizer.get_media_streams", return_value=SAMPLE_PROBE):
tracks = list_audio_tracks("/media/ep.mkv")
assert len(tracks) == 2
assert all(t["codec_type"] == "audio" for t in tracks)
def test_normalizes_track_shape(self):
"""Each track exposes index, codec, channels, language, title, default, forced."""
from services.audio_visualizer import list_audio_tracks
with patch("services.audio_visualizer.get_media_streams", return_value=SAMPLE_PROBE):
tracks = list_audio_tracks("/media/ep.mkv")
first = tracks[0]
assert first["index"] == 1
assert first["codec"] == "aac"
assert first["channels"] == 2
assert first["language"] == "jpn"
assert first["title"] == "Stereo"
assert first["default"] is True
assert first["forced"] is False
def test_handles_missing_tags_and_disposition(self):
"""Streams without tags/disposition return empty strings + False flags."""
from services.audio_visualizer import list_audio_tracks
sparse = {
"streams": [
{"index": 0, "codec_type": "audio", "codec_name": "opus", "channels": 1},
]
}
with patch("services.audio_visualizer.get_media_streams", return_value=sparse):
tracks = list_audio_tracks("/media/ep.mkv")
assert len(tracks) == 1
track = tracks[0]
assert track["language"] == ""
assert track["title"] == ""
assert track["default"] is False
assert track["forced"] is False
assert track["channels"] == 1
def test_empty_probe_returns_empty_list(self):
from services.audio_visualizer import list_audio_tracks
with patch("services.audio_visualizer.get_media_streams", return_value={"streams": []}):
tracks = list_audio_tracks("/media/ep.mkv")
assert tracks == []
# ─── Route: GET /api/v1/audio/tracks ───────────────────────────────────────
def _media_root() -> str:
return os.environ.get("SUBLARR_MEDIA_PATH", os.path.realpath("/tmp"))
class TestAudioTracksRoute:
def test_requires_file_path_param(self, client):
resp = client.get("/api/v1/audio/tracks")
assert resp.status_code == 400
assert "file_path" in resp.get_json()["error"]
def test_rejects_path_traversal(self, client):
resp = client.get("/api/v1/audio/tracks?file_path=/etc/passwd")
assert resp.status_code == 403
def test_returns_404_for_missing_file(self, client, tmp_path):
# Path inside media_root but the file does not exist
fake = tmp_path / "missing.mkv"
resp = client.get(f"/api/v1/audio/tracks?file_path={fake}")
assert resp.status_code == 404
def test_returns_tracks_for_existing_file(self, client, tmp_path):
# Create an empty file inside the media root and mock the probe
video = tmp_path / "ep.mkv"
video.write_bytes(b"\x00") # ffprobe will be mocked, content ignored
with patch("services.audio_visualizer.get_media_streams", return_value=SAMPLE_PROBE):
resp = client.get(f"/api/v1/audio/tracks?file_path={video}")
assert resp.status_code == 200
body = resp.get_json()
assert "tracks" in body
assert len(body["tracks"]) == 2
assert body["video_path"] == str(video)