test_api_server_normalize.py
1 """Tests for _normalize_chat_content in the API server adapter.""" 2 3 from gateway.platforms.api_server import _normalize_chat_content 4 5 6 class TestNormalizeChatContent: 7 """Content normalization converts array-based content parts to plain text.""" 8 9 def test_none_returns_empty_string(self): 10 assert _normalize_chat_content(None) == "" 11 12 def test_plain_string_returned_as_is(self): 13 assert _normalize_chat_content("hello world") == "hello world" 14 15 def test_empty_string_returned_as_is(self): 16 assert _normalize_chat_content("") == "" 17 18 def test_text_content_part(self): 19 content = [{"type": "text", "text": "hello"}] 20 assert _normalize_chat_content(content) == "hello" 21 22 def test_input_text_content_part(self): 23 content = [{"type": "input_text", "text": "user input"}] 24 assert _normalize_chat_content(content) == "user input" 25 26 def test_output_text_content_part(self): 27 content = [{"type": "output_text", "text": "assistant output"}] 28 assert _normalize_chat_content(content) == "assistant output" 29 30 def test_multiple_text_parts_joined_with_newline(self): 31 content = [ 32 {"type": "text", "text": "first"}, 33 {"type": "text", "text": "second"}, 34 ] 35 assert _normalize_chat_content(content) == "first\nsecond" 36 37 def test_mixed_string_and_dict_parts(self): 38 content = ["plain string", {"type": "text", "text": "dict part"}] 39 assert _normalize_chat_content(content) == "plain string\ndict part" 40 41 def test_image_url_parts_silently_skipped(self): 42 content = [ 43 {"type": "text", "text": "check this:"}, 44 {"type": "image_url", "image_url": {"url": "https://example.com/img.png"}}, 45 ] 46 assert _normalize_chat_content(content) == "check this:" 47 48 def test_integer_content_converted(self): 49 assert _normalize_chat_content(42) == "42" 50 51 def test_boolean_content_converted(self): 52 assert _normalize_chat_content(True) == "True" 53 54 def test_deeply_nested_list_respects_depth_limit(self): 55 """Nesting beyond max_depth returns empty string.""" 56 content = [[[[[[[[[[[["deep"]]]]]]]]]]]] 57 result = _normalize_chat_content(content) 58 # The deep nesting should be truncated, not crash 59 assert isinstance(result, str) 60 61 def test_large_list_capped(self): 62 """Lists beyond MAX_CONTENT_LIST_SIZE are truncated.""" 63 content = [{"type": "text", "text": f"item{i}"} for i in range(2000)] 64 result = _normalize_chat_content(content) 65 # Should not contain all 2000 items 66 assert result.count("item") <= 1000 67 68 def test_oversized_string_truncated(self): 69 """Strings beyond 64KB are truncated.""" 70 huge = "x" * 100_000 71 result = _normalize_chat_content(huge) 72 assert len(result) == 65_536 73 74 def test_empty_text_parts_filtered(self): 75 content = [ 76 {"type": "text", "text": ""}, 77 {"type": "text", "text": "actual"}, 78 {"type": "text", "text": ""}, 79 ] 80 assert _normalize_chat_content(content) == "actual" 81 82 def test_dict_without_type_skipped(self): 83 content = [{"foo": "bar"}, {"type": "text", "text": "real"}] 84 assert _normalize_chat_content(content) == "real" 85 86 def test_empty_list_returns_empty(self): 87 assert _normalize_chat_content([]) == ""