@@ -31,6 +31,7 @@ def mock_logger():
3131
3232@pytest .fixture
3333def mock_file_utils ():
34+ """Fixture providing mocked file utility functions for cross-platform testing."""
3435 with (
3536 patch ("api_utils.utils.extract_data_url_to_local" ) as mock_extract ,
3637 patch ("api_utils.utils.save_blob_to_local" ) as mock_save ,
@@ -438,28 +439,39 @@ def test_prepare_combined_prompt_input_video_processing(mock_file_utils, mock_lo
438439 mock_save .assert_called ()
439440
440441
441- def test_prepare_combined_prompt_complex_nested_dict (mock_file_utils , mock_logger ):
442- """Test nested dictionary content with specific attachment keys."""
442+ def test_prepare_combined_prompt_complex_nested_dict (
443+ mock_file_utils , mock_logger , tmp_path
444+ ):
445+ """Test nested dictionary content with specific attachment keys.
446+
447+ Uses platform-appropriate paths via tmp_path fixture for cross-platform compatibility.
448+ """
443449 mock_extract , _ , mock_exists = mock_file_utils
444450 mock_exists .return_value = True
445451
452+ # Create actual temp files for cross-platform path handling
453+ img_file = tmp_path / "img1.png"
454+ doc_file = tmp_path / "doc1.pdf"
455+ img_file .touch ()
456+ doc_file .touch ()
457+
446458 content = {
447459 "text" : "Look at these files" ,
448- "images" : [{"url" : "file:///c:/img1.png " }],
449- "files" : [{"path" : "c:/doc1.pdf" }], # absolute path
460+ "images" : [{"url" : f "file://{ img_file } " }], # Platform-appropriate file URL
461+ "files" : [{"path" : str ( doc_file ) }], # Platform-appropriate absolute path
450462 "media" : [{"url" : "data:video..." }], # data url
451463 }
452464
453- mock_extract .return_value = "/tmp/ media.mp4"
465+ mock_extract .return_value = str ( tmp_path / " media.mp4")
454466
455467 messages = [Message .model_construct (role = "user" , content = content )]
456468
457469 prompt , files = prepare_combined_prompt (messages , "req1" )
458470
459471 assert "Look at these files" in prompt
460- assert "/tmp/ media.mp4" in files
472+ assert str ( tmp_path / " media.mp4") in files
461473 assert any (f .endswith ("img1.png" ) for f in files )
462- assert "c:/doc1.pdf" in files or "/c:/ doc1.pdf" in files or "c: \\ doc1.pdf" in files
474+ assert any ( f . endswith ( " doc1.pdf") for f in files )
463475
464476
465477@pytest .mark .asyncio
@@ -860,13 +872,38 @@ def test_prepare_combined_prompt_non_existent_local_file(mock_file_utils, mock_l
860872 assert len (files ) == 0
861873
862874
863- def test_collect_and_validate_attachments_detailed (mock_file_utils , mock_logger ):
864- """Test detailed attachment collection logic including top-level and various message keys."""
875+ def test_collect_and_validate_attachments_detailed (
876+ mock_file_utils , mock_logger , tmp_path
877+ ):
878+ """Test detailed attachment collection logic including top-level and various message keys.
879+
880+ Uses tmp_path for platform-compatible paths.
881+ """
865882 mock_extract , _ , mock_exists = mock_file_utils
866883
867- # Mock file existence
884+ # Create actual temp files for cross-platform path handling
885+ valid_initial = tmp_path / "valid_initial.png"
886+ valid_top_level = tmp_path / "valid_top_level.png"
887+ valid_file_url = tmp_path / "valid_file_url.txt"
888+ valid_image = tmp_path / "valid_image.png"
889+ valid_file = tmp_path / "valid_file.pdf"
890+ valid_media = tmp_path / "valid_media.mp4"
891+ missing_initial = tmp_path / "missing_initial.png"
892+ missing = tmp_path / "missing.png"
893+
894+ # Create "valid" files, leave "missing" files uncreated
895+ valid_initial .touch ()
896+ valid_top_level .touch ()
897+ valid_file_url .touch ()
898+ valid_image .touch ()
899+ valid_file .touch ()
900+ valid_media .touch ()
901+
902+ # Mock file existence based on actual file existence
868903 def side_effect (path ):
869- return "existing" in path or "valid" in path
904+ from pathlib import Path
905+
906+ return Path (path ).exists ()
870907
871908 mock_exists .side_effect = side_effect
872909
@@ -875,10 +912,10 @@ def side_effect(path):
875912 # Mock request object with various attachment fields
876913 class MockRequest :
877914 attachments = [
878- "c:/tmp/ valid_top_level.png" ,
879- "c:/tmp/ missing.png" ,
915+ str ( valid_top_level ) ,
916+ str ( missing ) ,
880917 {"url" : "data:image/png;base64,data1" },
881- {"url" : "file:///c:/ valid_file_url.txt " },
918+ {"url" : f "file://{ valid_file_url } " },
882919 "" , # Empty string
883920 None , # None
884921 {"url" : "" }, # Empty URL in dict
@@ -887,31 +924,31 @@ class MockRequest:
887924 Message .model_construct (
888925 role = "user" ,
889926 content = "msg1" ,
890- images = ["c:/tmp/ valid_image.png" ],
891- files = [{"path" : "c:/tmp/ valid_file.pdf" }],
892- media = ["file:///c:/ valid_media.mp4 " ],
927+ images = [str ( valid_image ) ],
928+ files = [{"path" : str ( valid_file ) }],
929+ media = [f "file://{ valid_media } " ],
893930 )
894931 ]
895932
896933 req = MockRequest ()
897- initial_list = ["c:/tmp/ valid_initial.png" , "c:/tmp/ missing_initial.png" ]
934+ initial_list = [str ( valid_initial ), str ( missing_initial ) ]
898935
899936 result = collect_and_validate_attachments (req , "req1" , initial_list )
900937
901938 # Check initial list filtering
902- assert "c:/tmp/ valid_initial.png" in result
903- assert "c:/tmp/ missing_initial.png" not in result
939+ assert str ( valid_initial ) in result
940+ assert str ( missing_initial ) not in result
904941
905942 # Check top-level attachments
906- assert "c:/tmp/ valid_top_level.png" in result
907- assert "c:/tmp/ missing.png" not in result
943+ assert str ( valid_top_level ) in result
944+ assert str ( missing ) not in result
908945 # data: URL -> extracted (mock extract returns /tmp/...)
909946 assert mock_extract .called
910947
911948 # Check message-level attachments
912- assert "c:/tmp/ valid_image.png" in result
913- assert "c:/tmp/ valid_file.pdf" in result
914- # file:///c:/valid_media.mp4 -> unquoted path
949+ assert str ( valid_image ) in result
950+ assert str ( valid_file ) in result
951+ # file:// -> unquoted path
915952 assert any ("valid_media.mp4" in f for f in result )
916953
917954
@@ -1034,15 +1071,24 @@ def test_prepare_combined_prompt_dict_image_url_with_detail(
10341071 assert "[图像细节: detail=auto]" in prompt
10351072
10361073
1037- def test_prepare_combined_prompt_audio_absolute_path (mock_file_utils , mock_logger ):
1038- """Test audio with absolute path (lines 427-431)."""
1074+ def test_prepare_combined_prompt_audio_absolute_path (
1075+ mock_file_utils , mock_logger , tmp_path
1076+ ):
1077+ """Test audio with absolute path (lines 427-431).
1078+
1079+ Uses tmp_path for platform-compatible paths.
1080+ """
10391081 _ , _ , mock_exists = mock_file_utils
10401082 mock_exists .return_value = True
10411083
1084+ # Create actual temp file for cross-platform path handling
1085+ audio_file = tmp_path / "test.mp3"
1086+ audio_file .touch ()
1087+
10421088 # Absolute path for audio
10431089 content_item = {
10441090 "type" : "input_audio" ,
1045- "input_audio" : {"url" : "c:/audio/test.mp3" },
1091+ "input_audio" : {"url" : str ( audio_file ) },
10461092 }
10471093
10481094 messages = [
@@ -1051,7 +1097,7 @@ def test_prepare_combined_prompt_audio_absolute_path(mock_file_utils, mock_logge
10511097
10521098 prompt , files = prepare_combined_prompt (messages , "req1" )
10531099
1054- assert "c:/audio/test.mp3" in files
1100+ assert str ( audio_file ) in files
10551101
10561102
10571103def test_prepare_combined_prompt_video_absolute_path (mock_file_utils , mock_logger ):
@@ -1185,36 +1231,47 @@ def __iter__(self):
11851231
11861232
11871233def test_collect_and_validate_attachments_empty_url_handling (
1188- mock_file_utils , mock_logger
1234+ mock_file_utils , mock_logger , tmp_path
11891235):
1190- """Test attachment collection with empty URL strings (lines 802, 831, 834)."""
1236+ """Test attachment collection with empty URL strings (lines 802, 831, 834).
1237+
1238+ Uses tmp_path for platform-compatible paths.
1239+ """
11911240 _ , _ , mock_exists = mock_file_utils
11921241 mock_exists .return_value = True
11931242
1243+ # Create actual temp files for cross-platform path handling
1244+ valid_png = tmp_path / "valid.png"
1245+ valid_image = tmp_path / "valid_image.png"
1246+ valid_file = tmp_path / "valid_file.pdf"
1247+ valid_png .touch ()
1248+ valid_image .touch ()
1249+ valid_file .touch ()
1250+
11941251 class MockRequest :
11951252 attachments = [
11961253 "" , # Empty string
11971254 " " , # Whitespace only
11981255 {"url" : "" }, # Empty URL in dict
11991256 {"url" : " " }, # Whitespace URL in dict
1200- "c:/valid.png" , # Valid path
1257+ str ( valid_png ) , # Valid path
12011258 ]
12021259 messages = [
12031260 Message .model_construct (
12041261 role = "user" ,
12051262 content = "test" ,
1206- images = ["" , "c:/ valid_image.png" ],
1207- files = [{"path" : "" }, {"path" : "c:/ valid_file.pdf" }],
1263+ images = ["" , str ( valid_image ) ],
1264+ files = [{"path" : "" }, {"path" : str ( valid_file ) }],
12081265 )
12091266 ]
12101267
12111268 req = MockRequest ()
12121269 result = collect_and_validate_attachments (req , "req1" , [])
12131270
12141271 # Only valid paths should be included
1215- assert "c:/valid.png" in result
1216- assert "c:/ valid_image.png" in result
1217- assert "c:/ valid_file.pdf" in result
1272+ assert str ( valid_png ) in result
1273+ assert str ( valid_image ) in result
1274+ assert str ( valid_file ) in result
12181275
12191276 # Empty strings should be filtered out
12201277 assert "" not in result
@@ -1246,23 +1303,32 @@ def test_prepare_combined_prompt_dict_input_image_with_detail(
12461303 assert "[图像细节: detail=low]" in prompt
12471304
12481305
1249- def test_prepare_combined_prompt_dict_content_file_field (mock_file_utils , mock_logger ):
1250- """Test dict content with generic 'file' field (lines 313-322)."""
1306+ def test_prepare_combined_prompt_dict_content_file_field (
1307+ mock_file_utils , mock_logger , tmp_path
1308+ ):
1309+ """Test dict content with generic 'file' field (lines 313-322).
1310+
1311+ Uses tmp_path for platform-compatible paths.
1312+ """
12511313 _ , _ , mock_exists = mock_file_utils
12521314 mock_exists .return_value = True
12531315
1316+ # Create actual temp file for cross-platform path handling
1317+ file_path = tmp_path / "from_file_field.png"
1318+ file_path .touch ()
1319+
12541320 content = [
12551321 {
12561322 "type" : "image_url" ,
1257- "file" : {"url" : "c:/from_file_field.png" },
1323+ "file" : {"url" : str ( file_path ) },
12581324 }
12591325 ]
12601326
12611327 messages = [Message .model_construct (role = "user" , content = content )]
12621328
12631329 prompt , files = prepare_combined_prompt (messages , "req1" )
12641330
1265- assert "c:/from_file_field.png" in files
1331+ assert str ( file_path ) in files
12661332
12671333
12681334def test_prepare_combined_prompt_dict_content_file_path (mock_file_utils , mock_logger ):
@@ -1284,20 +1350,29 @@ def test_prepare_combined_prompt_dict_content_file_path(mock_file_utils, mock_lo
12841350 assert "/absolute/path/file.pdf" in files
12851351
12861352
1287- def test_prepare_combined_prompt_object_url_attribute (mock_file_utils , mock_logger ):
1288- """Test content item with direct url attribute (lines 249-250)."""
1353+ def test_prepare_combined_prompt_object_url_attribute (
1354+ mock_file_utils , mock_logger , tmp_path
1355+ ):
1356+ """Test content item with direct url attribute (lines 249-250).
1357+
1358+ Uses tmp_path for platform-compatible paths.
1359+ """
12891360 _ , _ , mock_exists = mock_file_utils
12901361 mock_exists .return_value = True
12911362
1363+ # Create actual temp file for cross-platform path handling
1364+ url_path = tmp_path / "direct_url.png"
1365+ url_path .touch ()
1366+
12921367 class UrlItem :
12931368 type = "image_url"
1294- url = "c:/direct_url.png"
1369+ url = str ( url_path )
12951370
12961371 messages = [Message .model_construct (role = "user" , content = [UrlItem ()])]
12971372
12981373 prompt , files = prepare_combined_prompt (messages , "req1" )
12991374
1300- assert "c:/direct_url.png" in files
1375+ assert str ( url_path ) in files
13011376
13021377
13031378def test_prepare_combined_prompt_dict_attachments_nested_input_image (
@@ -1323,25 +1398,32 @@ def test_prepare_combined_prompt_dict_attachments_nested_input_image(
13231398
13241399
13251400def test_prepare_combined_prompt_content_item_input_image_string (
1326- mock_file_utils , mock_logger
1401+ mock_file_utils , mock_logger , tmp_path
13271402):
1328- """Test content item with input_image as string (lines 283-284)."""
1403+ """Test content item with input_image as string (lines 283-284).
1404+
1405+ Uses tmp_path for platform-compatible paths.
1406+ """
13291407 _ , _ , mock_exists = mock_file_utils
13301408 mock_exists .return_value = True
13311409
1410+ # Create actual temp file for cross-platform path handling
1411+ img_path = tmp_path / "string_input_image.png"
1412+ img_path .touch ()
1413+
13321414 # This tests the case where item has input_image as a string directly
13331415 content = [
13341416 {
13351417 "type" : "image_url" ,
1336- "input_image" : "c:/string_input_image.png" , # String, not dict
1418+ "input_image" : str ( img_path ) , # String, not dict
13371419 }
13381420 ]
13391421
13401422 messages = [Message .model_construct (role = "user" , content = content )]
13411423
13421424 prompt , files = prepare_combined_prompt (messages , "req1" )
13431425
1344- assert "c:/string_input_image.png" in files
1426+ assert str ( img_path ) in files
13451427
13461428
13471429def test_prepare_combined_prompt_audio_video_data_base64 (mock_file_utils , mock_logger ):
0 commit comments