@@ -2299,3 +2299,75 @@ def test_get_timeout_from_request():
22992299 }
23002300 timeout = LiteLLMProxyRequestSetup ._get_timeout_from_request (headers )
23012301 assert timeout == 90.5
2302+
2303+
2304+ @pytest .mark .parametrize (
2305+ "ui_exists, ui_has_content" ,
2306+ [
2307+ (True , True ), # UI path exists and has content
2308+ (True , False ), # UI path exists but is empty
2309+ (False , False ), # UI path doesn't exist
2310+ ],
2311+ )
2312+ def test_non_root_ui_path_logic (monkeypatch , tmp_path , ui_exists , ui_has_content ):
2313+ """
2314+ Test the non-root Docker UI path detection logic.
2315+
2316+ Tests that when LITELLM_NON_ROOT is set to "true":
2317+ - If UI path exists and has content, it should be used
2318+ - If UI path doesn't exist or is empty, proper error logging occurs
2319+ """
2320+ import tempfile
2321+ import shutil
2322+ from unittest .mock import MagicMock
2323+
2324+ # Create a temporary directory to act as /tmp/litellm_ui
2325+ test_ui_path = tmp_path / "litellm_ui"
2326+
2327+ if ui_exists :
2328+ test_ui_path .mkdir (parents = True , exist_ok = True )
2329+ if ui_has_content :
2330+ # Create some dummy files to simulate built UI
2331+ (test_ui_path / "index.html" ).write_text ("<html></html>" )
2332+ (test_ui_path / "app.js" ).write_text ("console.log('test');" )
2333+
2334+ # Mock the environment variable and os.path operations
2335+ monkeypatch .setenv ("LITELLM_NON_ROOT" , "true" )
2336+
2337+ # Create a mock logger to capture log messages
2338+ mock_logger = MagicMock ()
2339+
2340+ # We need to reimport or reload the relevant code section
2341+ # Since this is module-level code, we'll test the logic directly
2342+ ui_path = None
2343+ non_root_ui_path = str (test_ui_path )
2344+
2345+ # Simulate the logic from proxy_server.py lines 909-920
2346+ if os .getenv ("LITELLM_NON_ROOT" , "" ).lower () == "true" :
2347+ if os .path .exists (non_root_ui_path ) and os .listdir (non_root_ui_path ):
2348+ mock_logger .info (f"Using pre-built UI for non-root Docker: { non_root_ui_path } " )
2349+ mock_logger .info (f"UI files found: { len (os .listdir (non_root_ui_path ))} items" )
2350+ ui_path = non_root_ui_path
2351+ else :
2352+ mock_logger .error (f"UI not found at { non_root_ui_path } . UI will not be available." )
2353+ mock_logger .error (f"Path exists: { os .path .exists (non_root_ui_path )} , Has content: { os .path .exists (non_root_ui_path ) and bool (os .listdir (non_root_ui_path ))} " )
2354+
2355+ # Verify behavior based on test parameters
2356+ if ui_exists and ui_has_content :
2357+ # UI should be found and used
2358+ assert ui_path == non_root_ui_path
2359+ assert mock_logger .info .call_count == 2
2360+ mock_logger .info .assert_any_call (f"Using pre-built UI for non-root Docker: { non_root_ui_path } " )
2361+ # Verify the second info call mentions the number of items
2362+ info_calls = [call [0 ][0 ] for call in mock_logger .info .call_args_list ]
2363+ assert any ("UI files found:" in call and "items" in call for call in info_calls )
2364+ assert mock_logger .error .call_count == 0
2365+ else :
2366+ # UI should not be found, error should be logged
2367+ assert ui_path is None
2368+ assert mock_logger .error .call_count == 2
2369+ mock_logger .error .assert_any_call (f"UI not found at { non_root_ui_path } . UI will not be available." )
2370+ # Verify the second error call has path existence info
2371+ error_calls = [call [0 ][0 ] for call in mock_logger .error .call_args_list ]
2372+ assert any ("Path exists:" in call for call in error_calls )
2373+ assert mock_logger .info .call_count == 0
0 commit comments