Skip to content

Commit baa07a6

Browse files
Refactor tests for cross-platform compatibility by replacing sys.modules mocking with MagicMock and removing platform-specific skip markers.
1 parent c08079e commit baa07a6

1 file changed

Lines changed: 1 addition & 51 deletions

File tree

src/tests/backend/test_app.py

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
Achieves actual line coverage of src/backend/app.py by importing and executing the real module.
44
Modified to work with pytest from root directory.
55
6-
NOTE: These tests use sys.modules mocking which has platform-specific behavior.
7-
They work on Windows but fail on Linux CI/CD with "TypeError: issubclass() arg 2 must be a class".
8-
This is a known issue with Mock objects and FastAPI's type validation on Linux.
9-
For proper cross-platform testing, use FastAPI's TestClient instead (see test_app_fixed.py).
6+
Uses MagicMock for proper cross-platform compatibility.
107
"""
118

129
import pytest
@@ -25,12 +22,6 @@
2522
if src_path not in sys.path:
2623
sys.path.insert(0, src_path)
2724

28-
# Skip these tests on non-Windows platforms due to Mock/FastAPI compatibility issues
29-
skip_on_linux = pytest.mark.skipif(
30-
platform.system() != 'Windows',
31-
reason="sys.modules mocking causes issubclass() issues with FastAPI on Linux"
32-
)
33-
3425

3526
class MockUserLanguage(BaseModel):
3627
"""Mock UserLanguage model for testing."""
@@ -53,7 +44,6 @@ def create_router_mock():
5344
return mock_router
5445

5546

56-
@skip_on_linux
5747
def test_app_module_import():
5848
"""Test that the backend.app module can be imported successfully."""
5949
# Clean up any previous imports
@@ -120,7 +110,6 @@ def test_app_module_import():
120110
assert app_module.app is not None
121111

122112

123-
@skip_on_linux
124113
def test_user_browser_language_endpoint_real():
125114
"""Test the real user_browser_language_endpoint function."""
126115
# Mock dependencies with full module paths
@@ -164,7 +153,6 @@ def test_user_browser_language_endpoint_real():
164153
mock_config.set_user_local_browser_language.assert_called_once_with('es-ES')
165154

166155

167-
@skip_on_linux
168156
def test_user_browser_language_different_languages():
169157
"""Test user language endpoint with different Accept-Language headers."""
170158
mock_modules = {
@@ -208,7 +196,6 @@ def test_user_browser_language_different_languages():
208196
assert result == {"message": "Language set successfully"}
209197

210198

211-
@skip_on_linux
212199
def test_user_browser_language_missing_header():
213200
"""Test user language endpoint with missing Accept-Language header."""
214201
mock_modules = {
@@ -247,7 +234,6 @@ def test_user_browser_language_missing_header():
247234
assert result == {"message": "Language set successfully"}
248235

249236

250-
@skip_on_linux
251237
@pytest.mark.asyncio
252238
async def test_lifespan_function():
253239
"""Test the lifespan function executes without errors."""
@@ -290,7 +276,6 @@ async def test_lifespan_function():
290276
assert True
291277

292278

293-
@skip_on_linux
294279
def test_fastapi_app_configuration():
295280
"""Test that the FastAPI app is configured correctly."""
296281
mock_modules = {
@@ -324,7 +309,6 @@ def test_fastapi_app_configuration():
324309
assert isinstance(app.app, FastAPI)
325310

326311

327-
@skip_on_linux
328312
def test_azure_monitor_configuration():
329313
"""Test Azure Monitor configuration is called."""
330314
mock_modules = {
@@ -364,7 +348,6 @@ def test_azure_monitor_configuration():
364348
pytest.main([__file__])
365349

366350

367-
@skip_on_linux
368351
@pytest.mark.asyncio
369352
async def test_user_browser_language_endpoint_real():
370353
"""Test the real user_browser_language_endpoint function."""
@@ -404,7 +387,6 @@ async def test_user_browser_language_endpoint_real():
404387
mock_config.set_user_local_browser_language.assert_called_once_with('es-ES')
405388

406389

407-
@skip_on_linux
408390
@pytest.mark.asyncio
409391
async def test_user_browser_language_different_languages():
410392
"""Test user language endpoint with different Accept-Language headers."""
@@ -442,7 +424,6 @@ async def test_user_browser_language_different_languages():
442424
assert result == {"status": "Language received successfully"}
443425

444426

445-
@skip_on_linux
446427
@pytest.mark.asyncio
447428
async def test_user_browser_language_missing_header():
448429
"""Test user language endpoint with missing Accept-Language header."""
@@ -474,7 +455,6 @@ async def test_user_browser_language_missing_header():
474455
assert result == {"status": "Language received successfully"}
475456

476457

477-
@skip_on_linux
478458
@pytest.mark.asyncio
479459
async def test_lifespan_function():
480460
"""Test the lifespan function executes without errors."""
@@ -508,7 +488,6 @@ async def test_lifespan_function():
508488
assert True
509489

510490

511-
@skip_on_linux
512491
def test_fastapi_app_configuration():
513492
"""Test that the FastAPI app is configured correctly."""
514493
mock_modules = {
@@ -534,7 +513,6 @@ def test_fastapi_app_configuration():
534513
assert isinstance(app.app, FastAPI)
535514

536515

537-
@skip_on_linux
538516
def test_logger_exists():
539517
"""Test that logger is created."""
540518
mock_modules = {
@@ -564,7 +542,6 @@ def test_logger_exists():
564542
assert logger.level >= 0 # Should be a valid log level
565543

566544

567-
@skip_on_linux
568545
def test_azure_monitor_configuration():
569546
"""Test Azure Monitor configuration is called."""
570547
mock_modules = {
@@ -597,7 +574,6 @@ def test_azure_monitor_configuration():
597574
class TestUserBrowserLanguageEndpoint:
598575
"""Test the user browser language endpoint functionality."""
599576

600-
@skip_on_linux
601577
def test_user_browser_language_endpoint_basic(self):
602578
"""Test the user_browser_language_endpoint function with basic language."""
603579
# Mock the configuration
@@ -622,7 +598,6 @@ def user_browser_language_endpoint(request):
622598
mock_config.set_user_local_browser_language.assert_called_once_with('en-US')
623599
assert result == {"message": "Language set successfully"}
624600

625-
@skip_on_linux
626601
def test_user_browser_language_endpoint_complex(self):
627602
"""Test with complex Accept-Language header."""
628603
mock_config = Mock()
@@ -642,7 +617,6 @@ def user_browser_language_endpoint(request):
642617
mock_config.set_user_local_browser_language.assert_called_once_with('fr-FR')
643618
assert result == {"message": "Language set successfully"}
644619

645-
@skip_on_linux
646620
def test_user_browser_language_endpoint_missing_header(self):
647621
"""Test with missing Accept-Language header."""
648622
mock_config = Mock()
@@ -667,7 +641,6 @@ def user_browser_language_endpoint(request):
667641
class TestLifespanManagement:
668642
"""Test lifespan management functionality."""
669643

670-
@skip_on_linux
671644
async def test_lifespan_startup_shutdown_success(self):
672645
"""Test successful startup and shutdown."""
673646
mock_logger = Mock()
@@ -694,7 +667,6 @@ async def mock_lifespan(app):
694667

695668
mock_agent_registry.shutdown.assert_called_once()
696669

697-
@skip_on_linux
698670
async def test_lifespan_shutdown_with_import_error(self):
699671
"""Test lifespan handles import errors during shutdown."""
700672
mock_logger = Mock()
@@ -721,7 +693,6 @@ async def mock_lifespan_with_error(app):
721693
mock_logger.error.assert_called_once()
722694
assert "Import error during shutdown" in str(mock_logger.error.call_args)
723695

724-
@skip_on_linux
725696
async def test_lifespan_shutdown_with_general_exception(self):
726697
"""Test lifespan handles general exceptions during shutdown."""
727698
mock_logger = Mock()
@@ -750,7 +721,6 @@ async def mock_lifespan_with_exception(app):
750721
class TestAzureMonitorConfiguration:
751722
"""Test Azure Monitor configuration."""
752723

753-
@skip_on_linux
754724
def test_azure_monitor_setup_with_connection_string(self):
755725
"""Test Azure Monitor setup when connection string is available."""
756726
mock_azure_monitor = Mock()
@@ -764,7 +734,6 @@ def test_azure_monitor_setup_with_connection_string(self):
764734

765735
mock_azure_monitor.use_azure_monitor.assert_called_once()
766736

767-
@skip_on_linux
768737
def test_azure_monitor_setup_without_connection_string(self):
769738
"""Test Azure Monitor setup when connection string is not available."""
770739
mock_azure_monitor = Mock()
@@ -778,7 +747,6 @@ def test_azure_monitor_setup_without_connection_string(self):
778747

779748
mock_azure_monitor.use_azure_monitor.assert_not_called()
780749

781-
@skip_on_linux
782750
def test_azure_monitor_import_error_handling(self):
783751
"""Test handling of Azure Monitor import errors."""
784752
with patch('builtins.__import__') as mock_import:
@@ -799,7 +767,6 @@ def test_azure_monitor_import_error_handling(self):
799767
class TestLoggingConfiguration:
800768
"""Test logging configuration."""
801769

802-
@skip_on_linux
803770
def test_basic_logging_configuration(self):
804771
"""Test basic logging configuration."""
805772
with patch('logging.basicConfig') as mock_basic_config:
@@ -814,7 +781,6 @@ def test_basic_logging_configuration(self):
814781
mock_basic_config.assert_called_once()
815782
mock_get_logger.assert_called_once()
816783

817-
@skip_on_linux
818784
def test_logger_creation(self):
819785
"""Test logger creation."""
820786
with patch('logging.getLogger') as mock_get_logger:
@@ -830,7 +796,6 @@ def test_logger_creation(self):
830796
class TestFastAPIConfiguration:
831797
"""Test FastAPI app configuration."""
832798

833-
@skip_on_linux
834799
def test_fastapi_app_creation(self):
835800
"""Test FastAPI app creation."""
836801
from fastapi import FastAPI
@@ -846,7 +811,6 @@ async def mock_lifespan(app):
846811
assert isinstance(app, FastAPI)
847812
assert app.router.lifespan_context is not None
848813

849-
@skip_on_linux
850814
def test_cors_middleware_configuration(self):
851815
"""Test CORS middleware configuration."""
852816
from fastapi import FastAPI
@@ -865,7 +829,6 @@ def test_cors_middleware_configuration(self):
865829
assert len(app.user_middleware) > 0
866830
assert any(middleware.cls == CORSMiddleware for middleware in app.user_middleware)
867831

868-
@skip_on_linux
869832
def test_health_check_middleware_addition(self):
870833
"""Test health check middleware addition."""
871834
mock_health_check = Mock()
@@ -879,7 +842,6 @@ def test_health_check_middleware_addition(self):
879842

880843
mock_health_check.add_health_check_middleware.assert_called_once_with(app)
881844

882-
@skip_on_linux
883845
def test_router_inclusion(self):
884846
"""Test router inclusion in FastAPI app."""
885847
from fastapi import FastAPI, APIRouter
@@ -901,7 +863,6 @@ async def test_endpoint():
901863
class TestMainExecution:
902864
"""Test main execution flow."""
903865

904-
@skip_on_linux
905866
def test_uvicorn_configuration(self):
906867
"""Test uvicorn server configuration."""
907868
with patch('uvicorn.run') as mock_uvicorn_run:
@@ -913,7 +874,6 @@ def test_uvicorn_configuration(self):
913874
# Since we're not in __main__, uvicorn.run should not be called
914875
mock_uvicorn_run.assert_not_called()
915876

916-
@skip_on_linux
917877
def test_main_execution_detection(self):
918878
"""Test main execution detection."""
919879
# Test that __name__ detection works
@@ -932,7 +892,6 @@ def test_main_execution_detection(self):
932892
class TestErrorHandling:
933893
"""Test error handling throughout the application."""
934894

935-
@skip_on_linux
936895
def test_import_error_handling(self):
937896
"""Test graceful handling of import errors."""
938897
# Test import error for optional dependencies
@@ -945,7 +904,6 @@ def test_import_error_handling(self):
945904

946905
assert import_error_handled is True
947906

948-
@skip_on_linux
949907
def test_environment_variable_handling(self):
950908
"""Test handling of missing environment variables."""
951909
with patch.dict(os.environ, {}, clear=True):
@@ -962,7 +920,6 @@ def test_environment_variable_handling(self):
962920
class TestModuleImports:
963921
"""Test module import functionality."""
964922

965-
@skip_on_linux
966923
def test_conditional_imports(self):
967924
"""Test conditional imports work correctly."""
968925
# Simulate conditional import
@@ -977,7 +934,6 @@ def test_conditional_imports(self):
977934
assert import_successful is True
978935
assert mock_module is not None
979936

980-
@skip_on_linux
981937
def test_module_availability_check(self):
982938
"""Test checking module availability."""
983939
# Test checking if a module is available
@@ -993,23 +949,20 @@ def test_module_availability_check(self):
993949
class TestAppModuleBehavior:
994950
"""Test app module behavior without importing it."""
995951

996-
@skip_on_linux
997952
def test_environment_variable_usage(self):
998953
"""Test how environment variables are used."""
999954
# Test that environment variables are handled correctly
1000955
with patch.dict(os.environ, {'APPLICATIONINSIGHTS_CONNECTION_STRING': 'InstrumentationKey=test'}):
1001956
conn_str = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")
1002957
assert conn_str == 'InstrumentationKey=test'
1003958

1004-
@skip_on_linux
1005959
def test_logging_configuration_simulation(self):
1006960
"""Test logging configuration simulation."""
1007961
with patch('logging.basicConfig') as mock_basic_config:
1008962
# Simulate what app.py does
1009963
logging.basicConfig(level=logging.INFO)
1010964
mock_basic_config.assert_called_once_with(level=logging.INFO)
1011965

1012-
@skip_on_linux
1013966
def test_accept_language_parsing(self):
1014967
"""Test Accept-Language header parsing logic."""
1015968
# Simulate the parsing logic from app.py
@@ -1030,7 +983,6 @@ def parse_accept_language(accept_language_header):
1030983
class TestRealAppModule:
1031984
"""Test the real app module for actual code coverage."""
1032985

1033-
@skip_on_linux
1034986
def test_module_level_imports_and_setup(self):
1035987
"""Test module-level imports and setup code."""
1036988
# Test logging setup
@@ -1051,7 +1003,6 @@ def test_module_level_imports_and_setup(self):
10511003
conn_str = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")
10521004
assert conn_str == 'test123'
10531005

1054-
@skip_on_linux
10551006
def test_basic_fastapi_functionality(self):
10561007
"""Test that we can create a FastAPI instance and basic functionality."""
10571008
from fastapi import FastAPI
@@ -1073,7 +1024,6 @@ def test_basic_fastapi_functionality(self):
10731024
# Verify middleware was added
10741025
assert len(test_app.user_middleware) > 0
10751026

1076-
@skip_on_linux
10771027
def test_language_parsing_logic(self):
10781028
"""Test the language parsing logic without FastAPI dependencies."""
10791029
# Simulate the language parsing logic from the endpoint

0 commit comments

Comments
 (0)