From cbf22422e3d5b0a80c8db63943f98d4de1fff156 Mon Sep 17 00:00:00 2001 From: unai Date: Sun, 1 Feb 2026 19:13:42 +0000 Subject: [PATCH] Refactor PlaylistManager to use PUBLIC_DIR for file operations and update tests accordingly --- src/m3u_list_builder/playlist.py | 9 +- tests/test_playlist.py | 153 ++++++++++++++++--------------- 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/src/m3u_list_builder/playlist.py b/src/m3u_list_builder/playlist.py index 35da940..84ef5f7 100644 --- a/src/m3u_list_builder/playlist.py +++ b/src/m3u_list_builder/playlist.py @@ -10,6 +10,9 @@ from m3u_list_builder.config import settings logger = logging.getLogger(__name__) +# Directorio dedicado para servir archivos +PUBLIC_DIR = Path("public") + class PlaylistManager: """Clase para gestionar la generación y actualización de listas M3U.""" @@ -17,6 +20,8 @@ class PlaylistManager: def __init__(self): """Inicialize the PlaylistManager.""" self.running = False + # Asegurar que el directorio público existe + PUBLIC_DIR.mkdir(exist_ok=True) def fetch_and_generate(self): """Descarga datos y regenera el archivo M3U.""" @@ -46,8 +51,8 @@ class PlaylistManager: def _write_m3u(self, channels: list): """Escribe el archivo M3U en disco de forma atómica.""" - temp_file = Path(f"{settings.output_file}.tmp") - final_file = Path(settings.output_file) + temp_file = PUBLIC_DIR / f"{settings.output_file}.tmp" + final_file = PUBLIC_DIR / settings.output_file with open(temp_file, "w", encoding="utf-8") as f: f.write("#EXTM3U\n") diff --git a/tests/test_playlist.py b/tests/test_playlist.py index 0858482..fb9d39a 100644 --- a/tests/test_playlist.py +++ b/tests/test_playlist.py @@ -219,51 +219,49 @@ class TestPlaylistManager: def test_write_m3u_empty_channels(self, temp_dir): """Test: _write_m3u genera archivo con solo header para lista vacía.""" - output_file = temp_dir / "playlist.m3u" + with patch("m3u_list_builder.playlist.PUBLIC_DIR", temp_dir): + with patch("m3u_list_builder.playlist.settings") as mock_settings: + mock_settings.host = "http://test-iptv.com" + mock_settings.username = "testuser" + mock_settings.password = "testpass" + mock_settings.output_file = "playlist.m3u" - with patch("m3u_list_builder.playlist.settings") as mock_settings: - mock_settings.host = "http://test-iptv.com" - mock_settings.username = "testuser" - mock_settings.password = "testpass" - mock_settings.output_file = str(output_file) + from m3u_list_builder.playlist import PlaylistManager - from m3u_list_builder.playlist import PlaylistManager + manager = PlaylistManager() + manager._write_m3u([]) - manager = PlaylistManager() - manager._write_m3u([]) - - content = output_file.read_text() - assert content == "#EXTM3U\n" + output_file = temp_dir / "playlist.m3u" + content = output_file.read_text() + assert content == "#EXTM3U\n" def test_write_m3u_multiple_channels(self, temp_dir, sample_channels): """Test: _write_m3u genera archivo correcto con múltiples canales.""" - output_file = temp_dir / "playlist.m3u" + with patch("m3u_list_builder.playlist.PUBLIC_DIR", temp_dir): + with patch("m3u_list_builder.playlist.settings") as mock_settings: + mock_settings.host = "http://test-iptv.com" + mock_settings.username = "testuser" + mock_settings.password = "testpass" + mock_settings.output_file = "playlist.m3u" - with patch("m3u_list_builder.playlist.settings") as mock_settings: - mock_settings.host = "http://test-iptv.com" - mock_settings.username = "testuser" - mock_settings.password = "testpass" - mock_settings.output_file = str(output_file) + from m3u_list_builder.playlist import PlaylistManager - from m3u_list_builder.playlist import PlaylistManager + manager = PlaylistManager() + manager._write_m3u(sample_channels) - manager = PlaylistManager() - manager._write_m3u(sample_channels) + output_file = temp_dir / "playlist.m3u" + content = output_file.read_text() - content = output_file.read_text() + # Verificar header + assert content.startswith("#EXTM3U\n") - # Verificar header - assert content.startswith("#EXTM3U\n") - - # Verificar cada canal - for channel in sample_channels: - assert channel["name"] in content - assert str(channel["stream_id"]) in content + # Verificar cada canal + for channel in sample_channels: + assert channel["name"] in content + assert str(channel["stream_id"]) in content def test_write_m3u_channel_format(self, temp_dir): """Test: _write_m3u genera formato EXTINF correcto.""" - output_file = temp_dir / "playlist.m3u" - channel = [ { "name": "Test Channel", @@ -273,52 +271,54 @@ class TestPlaylistManager: } ] - with patch("m3u_list_builder.playlist.settings") as mock_settings: - mock_settings.host = "http://iptv.com" - mock_settings.username = "user" - mock_settings.password = "pass" - mock_settings.output_file = str(output_file) + with patch("m3u_list_builder.playlist.PUBLIC_DIR", temp_dir): + with patch("m3u_list_builder.playlist.settings") as mock_settings: + mock_settings.host = "http://iptv.com" + mock_settings.username = "user" + mock_settings.password = "pass" + mock_settings.output_file = "playlist.m3u" - from m3u_list_builder.playlist import PlaylistManager + from m3u_list_builder.playlist import PlaylistManager - manager = PlaylistManager() - manager._write_m3u(channel) + manager = PlaylistManager() + manager._write_m3u(channel) - content = output_file.read_text() - lines = content.strip().split("\n") + output_file = temp_dir / "playlist.m3u" + content = output_file.read_text() + lines = content.strip().split("\n") - assert len(lines) == 3 # Header + EXTINF + URL + assert len(lines) == 3 # Header + EXTINF + URL - # Verificar EXTINF - assert '#EXTINF:-1 tvg-id="Test Channel"' in lines[1] - assert 'tvg-logo="http://icon.com/test.png"' in lines[1] - assert 'group-title="Cat_5"' in lines[1] - assert lines[1].endswith(",Test Channel") + # Verificar EXTINF + assert '#EXTINF:-1 tvg-id="Test Channel"' in lines[1] + assert 'tvg-logo="http://icon.com/test.png"' in lines[1] + assert 'group-title="Cat_5"' in lines[1] + assert lines[1].endswith(",Test Channel") - # Verificar URL - assert lines[2] == "http://iptv.com/live/user/pass/123.ts" + # Verificar URL + assert lines[2] == "http://iptv.com/live/user/pass/123.ts" def test_write_m3u_missing_fields_uses_defaults(self, temp_dir, minimal_channel): """Test: _write_m3u usa valores por defecto para campos faltantes.""" - output_file = temp_dir / "playlist.m3u" + with patch("m3u_list_builder.playlist.PUBLIC_DIR", temp_dir): + with patch("m3u_list_builder.playlist.settings") as mock_settings: + mock_settings.host = "http://test-iptv.com" + mock_settings.username = "testuser" + mock_settings.password = "testpass" + mock_settings.output_file = "playlist.m3u" - with patch("m3u_list_builder.playlist.settings") as mock_settings: - mock_settings.host = "http://test-iptv.com" - mock_settings.username = "testuser" - mock_settings.password = "testpass" - mock_settings.output_file = str(output_file) + from m3u_list_builder.playlist import PlaylistManager - from m3u_list_builder.playlist import PlaylistManager + manager = PlaylistManager() + manager._write_m3u(minimal_channel) - manager = PlaylistManager() - manager._write_m3u(minimal_channel) + output_file = temp_dir / "playlist.m3u" + content = output_file.read_text() - content = output_file.read_text() - - # Debe usar "Unknown" como nombre por defecto - assert "Unknown" in content - # Debe tener icono vacío - assert 'tvg-logo=""' in content + # Debe usar "Unknown" como nombre por defecto + assert "Unknown" in content + # Debe tener icono vacío + assert 'tvg-logo=""' in content def test_write_m3u_atomic_replacement(self, temp_dir): """Test: _write_m3u reemplaza atómicamente (usa archivo temporal).""" @@ -328,22 +328,23 @@ class TestPlaylistManager: # Crear archivo existente output_file.write_text("OLD CONTENT") - with patch("m3u_list_builder.playlist.settings") as mock_settings: - mock_settings.host = "http://test-iptv.com" - mock_settings.username = "testuser" - mock_settings.password = "testpass" - mock_settings.output_file = str(output_file) + with patch("m3u_list_builder.playlist.PUBLIC_DIR", temp_dir): + with patch("m3u_list_builder.playlist.settings") as mock_settings: + mock_settings.host = "http://test-iptv.com" + mock_settings.username = "testuser" + mock_settings.password = "testpass" + mock_settings.output_file = "playlist.m3u" - from m3u_list_builder.playlist import PlaylistManager + from m3u_list_builder.playlist import PlaylistManager - manager = PlaylistManager() - manager._write_m3u([]) + manager = PlaylistManager() + manager._write_m3u([]) - # El archivo temporal no debe existir después - assert not temp_file.exists() + # El archivo temporal no debe existir después + assert not temp_file.exists() - # El archivo final debe tener el nuevo contenido - assert output_file.read_text() == "#EXTM3U\n" + # El archivo final debe tener el nuevo contenido + assert output_file.read_text() == "#EXTM3U\n" # ======================================== # Tests para loop - Complejidad Media