"""Tests unitarios para el módulo server.""" import tempfile from pathlib import Path from unittest.mock import MagicMock, patch class TestRunServer: """Tests para la función run_server.""" # ======================================== # Tests para run_server - Complejidad Media # Path 1: Servidor inicia y sirve normalmente # Path 2: KeyboardInterrupt detiene el servidor # Path 3: Servidor se cierra correctamente en finally # ======================================== def test_run_server_creates_server_with_correct_address(self): """Test: run_server crea servidor en la dirección correcta.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 9999 mock_settings.output_file = "test.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() # Verificar que se creó con la dirección correcta mock_server_class.assert_called_once() call_args = mock_server_class.call_args[0] assert call_args[0] == ("", 9999) def test_run_server_uses_correct_port(self): """Test: run_server usa el puerto de settings.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() call_args = mock_server_class.call_args[0] assert call_args[0][1] == 8080 def test_run_server_calls_serve_forever(self): """Test: run_server llama a serve_forever.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() mock_server.serve_forever.assert_called_once() def test_run_server_handles_keyboard_interrupt(self): """Test: run_server maneja KeyboardInterrupt sin crash.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server # No debe lanzar excepción run_server() def test_run_server_closes_server_on_interrupt(self): """Test: run_server cierra el servidor tras KeyboardInterrupt.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() mock_server.server_close.assert_called_once() def test_run_server_closes_server_on_normal_exit(self): """Test: run_server cierra el servidor en salida normal.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() # Simular que serve_forever termina normalmente mock_server.serve_forever.return_value = None mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() mock_server.server_close.assert_called_once() def test_run_server_logs_startup_message(self, caplog): """Test: run_server registra mensaje de inicio.""" with tempfile.TemporaryDirectory() as tmpdir: with patch( "m3u_list_builder.server.get_public_dir", return_value=Path(tmpdir) ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server import logging with caplog.at_level(logging.INFO): from m3u_list_builder.server import run_server run_server() def test_run_server_serves_from_public_dir(self): """Test: run_server sirve desde el directorio public.""" with tempfile.TemporaryDirectory() as tmpdir: public_path = Path(tmpdir) with patch( "m3u_list_builder.server.get_public_dir", return_value=public_path ): with patch("m3u_list_builder.server.settings") as mock_settings: mock_settings.port = 8080 mock_settings.output_file = "playlist.m3u" with patch( "m3u_list_builder.server.ThreadingHTTPServer" ) as mock_server_class: with patch( "m3u_list_builder.server.partial" ) as mock_partial: mock_server = MagicMock() mock_server.serve_forever.side_effect = KeyboardInterrupt() mock_server_class.return_value = mock_server from m3u_list_builder.server import run_server run_server() # Verificar que partial se llamó con el directorio correcto mock_partial.assert_called_once() call_kwargs = mock_partial.call_args[1] assert call_kwargs["directory"] == str(public_path) class TestGetPublicDir: """Tests para la función get_public_dir.""" def test_get_public_dir_creates_directory(self, tmp_path, monkeypatch): """Test: get_public_dir crea el directorio si no existe.""" import m3u_list_builder.server as server_module test_public = tmp_path / "test_public" monkeypatch.setattr(server_module, "PUBLIC_DIR", test_public) from m3u_list_builder.server import get_public_dir result = get_public_dir() assert result == test_public assert test_public.exists() def test_get_public_dir_returns_existing_directory(self, tmp_path, monkeypatch): """Test: get_public_dir retorna directorio existente.""" import m3u_list_builder.server as server_module test_public = tmp_path / "existing_public" test_public.mkdir() monkeypatch.setattr(server_module, "PUBLIC_DIR", test_public) from m3u_list_builder.server import get_public_dir result = get_public_dir() assert result == test_public