unai 3c3f2354e3
All checks were successful
CI/CD Pipeline / test-and-lint (push) Successful in 35s
CI/CD Pipeline / publish-container (push) Has been skipped
Refactor server module to use dedicated public directory and enhance tests for run_server and get_public_dir functions
2026-02-01 19:14:23 +00:00

245 lines
10 KiB
Python

"""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