Spaces:
Paused
Paused
| import os | |
| import sys | |
| from unittest.mock import MagicMock, patch | |
| import pytest | |
| from launcher.checks import check_dependencies, ensure_auth_dirs_exist | |
| from launcher.config import determine_proxy_configuration, parse_args | |
| from launcher.utils import ( | |
| find_pids_on_port, | |
| get_proxy_from_gsettings, | |
| is_port_in_use, | |
| kill_process_interactive, | |
| ) | |
| # --- Test launcher.config --- | |
| def test_parse_args_defaults(): | |
| """Test argument parsing with default values.""" | |
| with ( | |
| patch.dict(os.environ, {"STREAM_PORT": "3120"}, clear=True), | |
| patch.object(sys, "argv", ["launcher"]), | |
| ): | |
| os.environ["LAUNCH_MODE"] = "test" | |
| # Patch constants in launcher.config where they are used by parse_args | |
| with ( | |
| patch("launcher.config.DEFAULT_STREAM_PORT", 3120), | |
| patch("launcher.config.DEFAULT_CAMOUFOX_PORT", 9222), | |
| patch("launcher.config.DEFAULT_SERVER_PORT", 2048), | |
| ): | |
| args = parse_args() | |
| assert args.server_port == 2048 | |
| assert args.stream_port == 3120 | |
| assert args.camoufox_debug_port == 9222 | |
| assert args.debug is False | |
| assert args.headless is False | |
| def test_parse_args_custom(): | |
| """Test argument parsing with custom values.""" | |
| test_args = [ | |
| "launcher", | |
| "--server-port", | |
| "8080", | |
| "--stream-port", | |
| "9090", | |
| "--debug", | |
| "--active-auth-json", | |
| "test.json", | |
| ] | |
| with patch.object(sys, "argv", test_args): | |
| args = parse_args() | |
| assert args.server_port == 8080 | |
| assert args.stream_port == 9090 | |
| assert args.debug is True | |
| assert args.active_auth_json == "test.json" | |
| def test_determine_proxy_configuration_arg(): | |
| """Test proxy configuration from command-line argument.""" | |
| result = determine_proxy_configuration( | |
| internal_camoufox_proxy_arg="http://proxy:8080" | |
| ) | |
| assert result["camoufox_proxy"] == "http://proxy:8080" | |
| assert result["stream_proxy"] == "http://proxy:8080" | |
| assert result["source"] is not None | |
| assert "CLI argument" in result["source"] | |
| def test_determine_proxy_configuration_env(): | |
| """Test proxy configuration from environment variable.""" | |
| with patch.dict(os.environ, {"UNIFIED_PROXY_CONFIG": "http://env:8080"}): | |
| result = determine_proxy_configuration() | |
| assert result["camoufox_proxy"] == "http://env:8080" | |
| assert result["source"] is not None | |
| assert "Environment variable UNIFIED_PROXY_CONFIG" in result["source"] | |
| # --- Test launcher.utils --- | |
| def test_is_port_in_use(mock_socket, bind_side_effect, expected_result, test_id): | |
| """Test port availability check when port is in use or free.""" | |
| mock_socket_instance = MagicMock() | |
| mock_socket.return_value.__enter__.return_value = mock_socket_instance | |
| if bind_side_effect: | |
| mock_socket_instance.bind.side_effect = bind_side_effect | |
| assert is_port_in_use(8080) is expected_result | |
| def test_find_pids_on_port_linux(mock_system, mock_popen): | |
| """Test finding processes on port using lsof (Linux).""" | |
| mock_system.return_value = "Linux" | |
| process_mock = MagicMock() | |
| process_mock.communicate.return_value = ("123\n456\n", "") | |
| process_mock.returncode = 0 | |
| mock_popen.return_value = process_mock | |
| pids = find_pids_on_port(8080) | |
| assert 123 in pids | |
| assert 456 in pids | |
| def test_find_pids_on_port_windows(mock_system, mock_popen): | |
| """Test finding processes on port using netstat (Windows).""" | |
| mock_system.return_value = "Windows" | |
| process_mock = MagicMock() | |
| # Simulate netstat output | |
| output = ( | |
| " TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 1234\n" | |
| ) | |
| process_mock.communicate.return_value = (output, "") | |
| process_mock.returncode = 0 | |
| mock_popen.return_value = process_mock | |
| pids = find_pids_on_port(8080) | |
| assert 1234 in pids | |
| def test_kill_process_interactive_linux(mock_system, mock_run): | |
| """Test killing process on Linux using kill command.""" | |
| mock_system.return_value = "Linux" | |
| mock_run.return_value.returncode = 0 | |
| assert kill_process_interactive(1234) is True | |
| mock_run.assert_called_with( | |
| "kill 1234", shell=True, capture_output=True, text=True, timeout=3, check=False | |
| ) | |
| def test_kill_process_interactive_windows(mock_system, mock_run): | |
| """Test killing process on Windows using taskkill.""" | |
| mock_system.return_value = "Windows" | |
| mock_run.return_value.returncode = 0 | |
| mock_run.return_value.stdout = ( | |
| "SUCCESS: The process with PID 1234 has been terminated." | |
| ) | |
| assert kill_process_interactive(1234) is True | |
| mock_run.assert_called() | |
| def test_get_proxy_from_gsettings(mock_run): | |
| """Test retrieving proxy configuration from gsettings (Linux).""" | |
| # Mock sequence of calls: mode, http host, http port | |
| mock_run.side_effect = [ | |
| MagicMock(returncode=0, stdout="'manual'"), | |
| MagicMock(returncode=0, stdout="'proxy.example.com'"), | |
| MagicMock(returncode=0, stdout="'8080'"), | |
| ] | |
| proxy = get_proxy_from_gsettings() | |
| assert proxy == "http://proxy.example.com:8080" | |
| # --- Test launcher.checks --- | |
| def test_ensure_auth_dirs_exist(mock_makedirs): | |
| """Test creation of authentication directories.""" | |
| ensure_auth_dirs_exist() | |
| assert mock_makedirs.call_count == 2 | |
| def test_check_dependencies_missing_camoufox(mock_exit): | |
| """Test dependency check exits when camoufox is missing.""" | |
| # Simulate missing camoufox | |
| with patch.dict(sys.modules, {"camoufox": None}): | |
| # We need to mock __import__ to raise ImportError for camoufox | |
| original_import = __import__ | |
| def side_effect(name, *args, **kwargs): | |
| if name == "camoufox": | |
| raise ImportError("No module named 'camoufox'") | |
| return original_import(name, *args, **kwargs) | |
| with patch("builtins.__import__", side_effect=side_effect): | |
| check_dependencies(launch_server=True, DefaultAddons=True) | |
| mock_exit.assert_called_with(1) | |
| def test_check_dependencies_success(): | |
| """Test dependency check succeeds when all dependencies are available.""" | |
| with ( | |
| patch("builtins.__import__"), | |
| patch("sys.modules", {"server": MagicMock(), "server.app": MagicMock()}), | |
| ): | |
| # Mock server.app import | |
| with patch.dict(sys.modules): | |
| sys.modules["server"] = MagicMock() | |
| sys.modules["server"].app = MagicMock() | |
| # Should not raise SystemExit | |
| check_dependencies(launch_server=None, DefaultAddons=None) | |