11# pylint: disable=redefined-outer-name
22"""Tests for the FastAPI application."""
33
4+ import logging
5+ import os
6+
47from backend .app import create_app
58
69from fastapi import FastAPI
710
811from httpx import ASGITransport
912from httpx import AsyncClient
1013
14+ from opentelemetry .sdk ._logs import LoggingHandler
15+
1116import pytest
1217
1318
@@ -34,3 +39,46 @@ async def test_backend_routes_exist(app: FastAPI):
3439 routes = [route .path for route in app .router .routes ]
3540 backend_routes = [r for r in routes if r .startswith ("/api" )]
3641 assert backend_routes , "No backend routes found under /api prefix"
42+
43+
44+ def test_logging_handler_deduplication ():
45+ """Test that creating multiple apps doesn't accumulate LoggingHandler instances."""
46+ # Set up Application Insights connection string to trigger telemetry setup
47+ connection_string = "InstrumentationKey=test-key;IngestionEndpoint=https://test.com"
48+ original_env = os .environ .get ("APPLICATIONINSIGHTS_CONNECTION_STRING" )
49+
50+ try :
51+ os .environ ["APPLICATIONINSIGHTS_CONNECTION_STRING" ] = connection_string
52+
53+ # Get root logger and count existing LoggingHandlers
54+ root_logger = logging .getLogger ()
55+ initial_handler_count = sum (1 for h in root_logger .handlers if isinstance (h , LoggingHandler ))
56+
57+ # Create first app
58+ app1 = create_app ()
59+ handler_count_after_first = sum (1 for h in root_logger .handlers if isinstance (h , LoggingHandler ))
60+
61+ # Create second app
62+ app2 = create_app ()
63+ handler_count_after_second = sum (1 for h in root_logger .handlers if isinstance (h , LoggingHandler ))
64+
65+ # Assert only one LoggingHandler exists after multiple create_app() calls
66+ assert handler_count_after_first == initial_handler_count + 1 , \
67+ "First create_app() should add one LoggingHandler"
68+ assert handler_count_after_second == handler_count_after_first , \
69+ "Second create_app() should not add another LoggingHandler (de-duplication should work)"
70+
71+ # Clean up - remove the handler we added
72+ for handler in list (root_logger .handlers ):
73+ if isinstance (handler , LoggingHandler ):
74+ root_logger .removeHandler (handler )
75+ try :
76+ handler .close ()
77+ except Exception :
78+ pass
79+ finally :
80+ # Restore original environment
81+ if original_env is not None :
82+ os .environ ["APPLICATIONINSIGHTS_CONNECTION_STRING" ] = original_env
83+ else :
84+ os .environ .pop ("APPLICATIONINSIGHTS_CONNECTION_STRING" , None )
0 commit comments