/ tests / gateway / test_api_server_normalize.py
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([]) == ""