""" Tests for stream/main.py - Entry point coverage. Focus: Cover missing lines (66, 79-85, 110, 123-129). Strategy: Test exception handlers, upstream proxy logging, default port assignment. """ import asyncio import runpy import sys from unittest.mock import AsyncMock, MagicMock, patch import pytest def test_main_module_as_main(): """ Test scenario: Execute module in __main__ mode Expected: Cover line 129 (if __name__ == "__main__") """ # Mock ProxyServer to avoid actually starting the proxy with ( patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.parse_args") as mock_parse, patch("stream.main.asyncio.run") as mock_asyncio_run, ): mock_proxy = AsyncMock() mock_proxy.start = AsyncMock() mock_proxy_class.return_value = mock_proxy # Provide test arguments mock_args = MagicMock() mock_args.host = "127.0.0.1" mock_args.port = 3120 mock_args.domains = ["*.google.com"] mock_args.proxy = None mock_parse.return_value = mock_args # Temporarily replace sys.argv to avoid argument parsing errors original_argv = sys.argv try: sys.argv = ["stream.main"] # Execute module as __main__ (covers line 129) runpy.run_module("stream.main", run_name="__main__") # Verify asyncio.run was called with main() (line 129) mock_asyncio_run.assert_called_once() finally: sys.argv = original_argv @pytest.mark.asyncio async def test_main_with_upstream_proxy_logging(): """ Test scenario: Log output when main() function uses upstream proxy Expected: Cover line 66 (if args.proxy: logger.info(...)) """ with ( patch("stream.main.parse_args") as mock_parse, patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, ): # Mock logger to capture log calls mock_logger = MagicMock() mock_get_logger.return_value = mock_logger # Mock arguments with upstream proxy mock_args = MagicMock() mock_args.host = "127.0.0.1" mock_args.port = 3120 mock_args.domains = ["*.google.com"] mock_args.proxy = "http://upstream-proxy:8080" # CRITICAL: upstream proxy set mock_parse.return_value = mock_args # Mock ProxyServer to raise KeyboardInterrupt immediately mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=KeyboardInterrupt) mock_proxy_class.return_value = mock_proxy # Import main function from stream.main import main # Run main() and expect KeyboardInterrupt to be caught await main() # Verify upstream proxy was logged (line 66) mock_logger.info.assert_any_call( "Using upstream proxy: http://upstream-proxy:8080" ) @pytest.mark.asyncio async def test_main_keyboard_interrupt_handling(): """ Test scenario: main() function handles KeyboardInterrupt exception Expected: Cover lines 79-80 (except KeyboardInterrupt: logger.info(...)) """ with ( patch("stream.main.parse_args") as mock_parse, patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, ): mock_logger = MagicMock() mock_get_logger.return_value = mock_logger mock_args = MagicMock() mock_args.host = "127.0.0.1" mock_args.port = 3120 mock_args.domains = ["*.google.com"] mock_args.proxy = None mock_parse.return_value = mock_args # Mock ProxyServer.start() to raise KeyboardInterrupt mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=KeyboardInterrupt) mock_proxy_class.return_value = mock_proxy from stream.main import main # Run main() - KeyboardInterrupt should be caught await main() # Verify shutdown message was logged (line 80) mock_logger.info.assert_any_call("Shutting down proxy server") @pytest.mark.asyncio async def test_main_cancelled_error_re_raising(): """ Test scenario: main() function re-throws CancelledError Expected: Cover lines 81-82 (except asyncio.CancelledError: raise) """ with ( patch("stream.main.parse_args") as mock_parse, patch("stream.main.ProxyServer") as mock_proxy_class, ): mock_args = MagicMock() mock_args.host = "127.0.0.1" mock_args.port = 3120 mock_args.domains = ["*.google.com"] mock_args.proxy = None mock_parse.return_value = mock_args # Mock ProxyServer.start() to raise CancelledError mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=asyncio.CancelledError) mock_proxy_class.return_value = mock_proxy from stream.main import main # Run main() - CancelledError should be re-raised with pytest.raises(asyncio.CancelledError): await main() @pytest.mark.asyncio async def test_main_generic_exception_handling(): """ Test scenario: main() function handles generic exception and exits Expected: Cover lines 83-85 (except Exception: logger.error(...); sys.exit(1)) """ with ( patch("stream.main.parse_args") as mock_parse, patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, patch("stream.main.sys.exit") as mock_sys_exit, ): mock_logger = MagicMock() mock_get_logger.return_value = mock_logger mock_args = MagicMock() mock_args.host = "127.0.0.1" mock_args.port = 3120 mock_args.domains = ["*.google.com"] mock_args.proxy = None mock_parse.return_value = mock_args # Mock ProxyServer.start() to raise generic exception test_error = RuntimeError("Proxy server startup failed") mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=test_error) mock_proxy_class.return_value = mock_proxy from stream.main import main # Run main() - generic exception should be caught await main() # Verify error was logged (line 84) mock_logger.error.assert_called_once_with( f"Error starting proxy server: {test_error}", exc_info=True ) # Verify sys.exit(1) was called (line 85) mock_sys_exit.assert_called_once_with(1) @pytest.mark.asyncio async def test_builtin_with_default_port(): """ Test scenario: builtin() function uses default port when port=None Expected: Cover line 110 (if port is None: port = 3120) """ with ( patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, ): mock_logger = MagicMock() mock_get_logger.return_value = mock_logger # Mock ProxyServer to immediately raise KeyboardInterrupt mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=KeyboardInterrupt) mock_proxy_class.return_value = mock_proxy from stream.main import builtin # Call builtin with port=None (line 110 should execute) await builtin(queue=None, port=None, proxy=None) # Verify ProxyServer was created with default port 3120 mock_proxy_class.assert_called_once() call_kwargs = mock_proxy_class.call_args[1] assert call_kwargs["port"] == 3120 # Default port was used @pytest.mark.asyncio async def test_builtin_keyboard_interrupt_handling(): """ Test scenario: builtin() function handles KeyboardInterrupt exception Expected: Cover lines 123-124 (except KeyboardInterrupt: logger.info(...)) """ with ( patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, ): mock_logger = MagicMock() mock_get_logger.return_value = mock_logger # Mock ProxyServer.start() to raise KeyboardInterrupt mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=KeyboardInterrupt) mock_proxy_class.return_value = mock_proxy from stream.main import builtin # Run builtin() - KeyboardInterrupt should be caught await builtin(queue=None, port=3120, proxy=None) # Verify shutdown message was logged (line 124) mock_logger.info.assert_any_call("Shutting down proxy server") @pytest.mark.asyncio async def test_builtin_cancelled_error_re_raising(): """ Test scenario: builtin() function re-throws CancelledError Expected: Cover lines 125-126 (except asyncio.CancelledError: raise) """ with patch("stream.main.ProxyServer") as mock_proxy_class: # Mock ProxyServer.start() to raise CancelledError mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=asyncio.CancelledError) mock_proxy_class.return_value = mock_proxy from stream.main import builtin # Run builtin() - CancelledError should be re-raised with pytest.raises(asyncio.CancelledError): await builtin(queue=None, port=3120, proxy=None) @pytest.mark.asyncio async def test_builtin_generic_exception_handling(): """ Test scenario: builtin() function handles generic exception and exits Expected: Cover lines 127-129 (except Exception: logger.error(...); sys.exit(1)) """ with ( patch("stream.main.ProxyServer") as mock_proxy_class, patch("stream.main.logging.getLogger") as mock_get_logger, patch("stream.main.sys.exit") as mock_sys_exit, ): mock_logger = MagicMock() mock_get_logger.return_value = mock_logger # Mock ProxyServer.start() to raise generic exception test_error = RuntimeError("Critical proxy failure") mock_proxy = AsyncMock() mock_proxy.start = AsyncMock(side_effect=test_error) mock_proxy_class.return_value = mock_proxy from stream.main import builtin # Run builtin() - generic exception should be caught await builtin(queue=None, port=3120, proxy=None) # Verify error was logged (line 128) mock_logger.error.assert_called_once_with( f"Error starting proxy server: {test_error}", exc_info=True ) # Verify sys.exit(1) was called (line 129) mock_sys_exit.assert_called_once_with(1)