11"""Tests for the server module."""
22
3+ import os
34from unittest .mock import MagicMock , patch , AsyncMock
45
56import pytest
1617from src .codegate .codegate_logging import LogLevel , LogFormat
1718from uvicorn .config import Config as UvicornConfig
1819from click .testing import CliRunner
20+ from pathlib import Path
1921
2022
2123@pytest .fixture
@@ -277,7 +279,8 @@ def test_serve_custom_options(cli_runner):
277279
278280 # Check if Config object attributes match the expected values
279281 for key , expected_value in expected_values .items ():
280- assert getattr (config_arg , key ) == expected_value , f"{ key } does not match expected value"
282+ assert getattr (config_arg , key ) == expected_value , \
283+ f"{ key } does not match expected value"
281284
282285
283286def test_serve_invalid_port (cli_runner ):
@@ -326,7 +329,7 @@ def test_serve_with_config_file(cli_runner, temp_config_file):
326329
327330 # Validate that run_servers was called with the expected configuration
328331 mock_run .assert_called_once ()
329- config_arg = mock_run .call_args [0 ][0 ] # Assuming Config object is the first positional argument
332+ config_arg = mock_run .call_args [0 ][0 ]
330333
331334 # Define expected values based on the temp_config_file content
332335 expected_values = {
@@ -339,5 +342,161 @@ def test_serve_with_config_file(cli_runner, temp_config_file):
339342
340343 # Check if passed arguments match the expected values
341344 for key , expected_value in expected_values .items ():
342- assert getattr (config_arg , key ) == expected_value , f"{ key } does not match expected value"
345+ assert getattr (config_arg , key ) == expected_value , \
346+ f"{ key } does not match expected value"
343347
348+
349+ def test_serve_with_nonexistent_config_file (cli_runner : CliRunner ) -> None :
350+ """Test serve command with nonexistent config file."""
351+ result = cli_runner .invoke (cli , ["serve" , "--config" , "nonexistent.yaml" ])
352+ assert result .exit_code == 2
353+ assert "does not exist" in result .output
354+
355+
356+ def test_serve_priority_resolution (cli_runner : CliRunner , temp_config_file : Path ) -> None :
357+ """Test serve command respects configuration priority."""
358+ # Set up environment variables and ensure they get cleaned up after the test
359+ with patch .dict (os .environ , {'LOG_LEVEL' : 'INFO' , 'PORT' : '9999' }, clear = True ), \
360+ patch ('src.codegate.cli.run_servers' ) as mock_run , \
361+ patch ('src.codegate.cli.structlog.get_logger' ) as mock_logging , \
362+ patch ('src.codegate.cli.setup_logging' ) as mock_setup_logging :
363+ # Set up mock logger
364+ logger_instance = MagicMock ()
365+ mock_logging .return_value = logger_instance
366+
367+ # Execute CLI command with specific options overriding environment and config file settings
368+ result = cli_runner .invoke (
369+ cli ,
370+ [
371+ "serve" ,
372+ "--config" ,
373+ str (temp_config_file ),
374+ "--port" ,
375+ "8080" ,
376+ "--host" ,
377+ "example.com" ,
378+ "--log-level" ,
379+ "ERROR" ,
380+ "--log-format" ,
381+ "TEXT" ,
382+ "--certs-dir" ,
383+ "./cli-certs" ,
384+ "--ca-cert" ,
385+ "cli-ca.crt" ,
386+ "--ca-key" ,
387+ "cli-ca.key" ,
388+ "--server-cert" ,
389+ "cli-server.crt" ,
390+ "--server-key" ,
391+ "cli-server.key" ,
392+ ],
393+ )
394+
395+ # Check the result of the command
396+ assert result .exit_code == 0
397+
398+ # Ensure logging setup was called with the highest priority settings (CLI arguments)
399+ mock_setup_logging .assert_called_once_with ('ERROR' , 'TEXT' )
400+ mock_logging .assert_called_with ("codegate" )
401+
402+ # Verify that the run_servers was called with the overridden settings
403+ config_arg = mock_run .call_args [0 ][0 ] # Assuming Config is the first positional arg
404+
405+ expected_values = {
406+ "port" : 8080 ,
407+ "host" : "example.com" ,
408+ "log_level" : 'ERROR' ,
409+ "log_format" : 'TEXT' ,
410+ "certs_dir" : "./cli-certs" ,
411+ "ca_cert" : "cli-ca.crt" ,
412+ "ca_key" : "cli-ca.key" ,
413+ "server_cert" : "cli-server.crt" ,
414+ "server_key" : "cli-server.key" ,
415+ }
416+
417+ # Verify if Config object attributes match the expected values from CLI arguments
418+ for key , expected_value in expected_values .items ():
419+ assert getattr (config_arg , key ) == expected_value , \
420+ f"{ key } does not match expected value"
421+
422+
423+ def test_serve_certificate_options (cli_runner : CliRunner ) -> None :
424+ """Test serve command with certificate options."""
425+ with patch ('src.codegate.cli.run_servers' ) as mock_run , \
426+ patch ('src.codegate.cli.structlog.get_logger' ) as mock_logging , \
427+ patch ('src.codegate.cli.setup_logging' ) as mock_setup_logging :
428+ # Set up mock logger
429+ logger_instance = MagicMock ()
430+ mock_logging .return_value = logger_instance
431+
432+ # Execute CLI command with certificate options
433+ result = cli_runner .invoke (
434+ cli ,
435+ [
436+ "serve" ,
437+ "--certs-dir" ,
438+ "./custom-certs" ,
439+ "--ca-cert" ,
440+ "custom-ca.crt" ,
441+ "--ca-key" ,
442+ "custom-ca.key" ,
443+ "--server-cert" ,
444+ "custom-server.crt" ,
445+ "--server-key" ,
446+ "custom-server.key" ,
447+ ],
448+ )
449+
450+ # Check the result of the command
451+ assert result .exit_code == 0
452+
453+ # Ensure logging setup was called with expected arguments
454+ mock_setup_logging .assert_called_once_with ('INFO' , 'JSON' )
455+ mock_logging .assert_called_with ("codegate" )
456+
457+ # Verify that run_servers was called with the provided certificate options
458+ config_arg = mock_run .call_args [0 ][0 ] # Assuming Config is the first positional arg
459+
460+ expected_values = {
461+ "certs_dir" : "./custom-certs" ,
462+ "ca_cert" : "custom-ca.crt" ,
463+ "ca_key" : "custom-ca.key" ,
464+ "server_cert" : "custom-server.crt" ,
465+ "server_key" : "custom-server.key" ,
466+ }
467+
468+ # Check if Config object attributes match the expected values
469+ for key , expected_value in expected_values .items ():
470+ assert getattr (config_arg , key ) == expected_value , \
471+ f"{ key } does not match expected value"
472+
473+
474+ def test_main_function () -> None :
475+ """Test main function."""
476+ with patch ("sys.argv" , ["cli" ]), patch ("codegate.cli.cli" ) as mock_cli :
477+ from codegate .cli import main
478+ main ()
479+ mock_cli .assert_called_once ()
480+
481+
482+ @pytest .fixture
483+ def mock_uvicorn_server ():
484+ mock_config = MagicMock () # Setup the configuration mock
485+ mock_server = MagicMock (spec = UvicornServer )
486+ mock_server .shutdown = AsyncMock () # Ensure shutdown is an async mock
487+
488+ uvicorn_server = UvicornServer (config = mock_config , server = mock_server )
489+ return uvicorn_server
490+
491+
492+ @pytest .mark .asyncio
493+ async def test_uvicorn_server_cleanup (mock_uvicorn_server ):
494+ with patch ("asyncio.get_running_loop" ), \
495+ patch .object (mock_uvicorn_server .server , 'shutdown' , AsyncMock ()):
496+ # Mock the loop or other components as needed
497+
498+ # Start the server or trigger the condition you want to test
499+ await mock_uvicorn_server .cleanup () # This should now complete without error
500+
501+ # Verify that the shutdown was called
502+ mock_uvicorn_server .server .shutdown .assert_awaited_once ()
0 commit comments