@@ -196,61 +196,66 @@ async def test_get_ai_response_with_leading_trailing_spaces(
196196
197197 assert result == "Response with spaces"
198198
199- def test_create_ai_embed_normal_length (self , openai_cog , mock_ctx ):
200- """Test _create_ai_embed with normal length description."""
199+ def test_create_ai_embeds_normal_length (self , openai_cog , mock_ctx ):
200+ """Test _create_ai_embeds with normal length description."""
201201 description = "This is a normal length response."
202202 color = discord .Color .blue ()
203203
204- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
204+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
205205
206- assert isinstance (embed , discord .Embed )
207- assert embed .color == color
208- assert embed .description == description
209- assert embed .author .name == "TestUser"
210- assert embed .author .icon_url == "https://example.com/avatar.png"
206+ assert len (embeds ) == 1
207+ assert isinstance (embeds [0 ], discord .Embed )
208+ assert embeds [0 ].color == color
209+ assert embeds [0 ].description == description
210+ assert embeds [0 ].author .name == "TestUser"
211+ assert embeds [0 ].author .icon_url == "https://example.com/avatar.png"
211212
212- def test_create_ai_embed_long_description (self , openai_cog , mock_ctx ):
213- """Test _create_ai_embed with description exceeding 2000 characters."""
213+ def test_create_ai_embeds_long_description (self , openai_cog , mock_ctx ):
214+ """Test _create_ai_embeds with description exceeding 2000 characters paginates ."""
214215 long_description = "a" * 2010 # Exceeds 2000-character limit
215216 color = discord .Color .green ()
216217
217- embed = openai_cog ._create_ai_embed (mock_ctx , long_description , color )
218+ embeds = openai_cog ._create_ai_embeds (mock_ctx , long_description , color )
218219
219- assert len (embed .description ) <= 2000
220- assert embed .description .endswith ("..." )
221- assert embed .description .startswith ("a" * 1997 )
220+ assert len (embeds ) == 2
221+ assert len (embeds [0 ].description ) <= 2000
222+ assert len (embeds [1 ].description ) <= 2000
223+ assert embeds [0 ].description + embeds [1 ].description == long_description
224+ assert "Page 1/2" in embeds [0 ].footer .text
225+ assert "Page 2/2" in embeds [1 ].footer .text
222226
223- def test_create_ai_embed_exactly_2000_chars (self , openai_cog , mock_ctx ):
224- """Test _create_ai_embed with exactly 2000 characters."""
227+ def test_create_ai_embeds_exactly_2000_chars (self , openai_cog , mock_ctx ):
228+ """Test _create_ai_embeds with exactly 2000 characters returns single page ."""
225229 description = "a" * 2000
226230 color = discord .Color .red ()
227231
228- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
232+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
229233
230- assert embed .description == description
231- assert len (embed .description ) == 2000
234+ assert len (embeds ) == 1
235+ assert embeds [0 ].description == description
236+ assert len (embeds [0 ].description ) == 2000
232237
233- def test_create_ai_embed_no_author_avatar (self , openai_cog , mock_ctx ):
234- """Test _create_ai_embed when author has no avatar."""
238+ def test_create_ai_embeds_no_author_avatar (self , openai_cog , mock_ctx ):
239+ """Test _create_ai_embeds when author has no avatar."""
235240 mock_ctx .author .avatar = None
236241 description = "Test response"
237242 color = discord .Color .orange ()
238243
239- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
244+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
240245
241- assert embed .author .name == "TestUser"
242- assert embed .author .icon_url is None
246+ assert embeds [ 0 ] .author .name == "TestUser"
247+ assert embeds [ 0 ] .author .icon_url is None
243248
244- def test_create_ai_embed_no_bot_avatar (self , openai_cog , mock_ctx ):
245- """Test _create_ai_embed when bot has no avatar."""
249+ def test_create_ai_embeds_no_bot_avatar (self , openai_cog , mock_ctx ):
250+ """Test _create_ai_embeds when bot has no avatar."""
246251 mock_ctx .bot .user .avatar = None
247252 description = "Test response"
248253 color = discord .Color .purple ()
249254
250- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
255+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
251256
252- assert embed .footer .icon_url is None
253- assert "UTC" in embed .footer .text
257+ assert embeds [ 0 ] .footer .icon_url is None
258+ assert "UTC" in embeds [ 0 ] .footer .text
254259
255260 @pytest .mark .asyncio
256261 @patch ("src.bot.cogs.open_ai.get_bot_settings" )
@@ -350,13 +355,13 @@ async def test_get_ai_response_api_parameters(
350355 assert call_args ["model" ] == "gpt-3.5-turbo"
351356
352357 @patch ("src.bot.cogs.open_ai.bot_utils.get_current_date_time_str_long" )
353- def test_create_ai_embed_footer (self , mock_get_datetime , openai_cog , mock_ctx ):
358+ def test_create_ai_embeds_footer (self , mock_get_datetime , openai_cog , mock_ctx ):
354359 """Test that embed footer contains correct timestamp."""
355360 mock_get_datetime .return_value = "2023-01-01 12:00:00"
356361
357- embed = openai_cog ._create_ai_embed (mock_ctx , "Test" , discord .Color .blue ())
362+ embeds = openai_cog ._create_ai_embeds (mock_ctx , "Test" , discord .Color .blue ())
358363
359- assert embed .footer .text == "2023-01-01 12:00:00 UTC"
364+ assert embeds [ 0 ] .footer .text == "2023-01-01 12:00:00 UTC"
360365 mock_get_datetime .assert_called_once ()
361366
362367 @pytest .mark .asyncio
@@ -435,24 +440,53 @@ async def test_get_ai_response_empty_response(self, mock_get_settings, openai_co
435440
436441 assert result == "" # Should strip to empty string
437442
438- def test_create_ai_embed_edge_case_1997_chars (self , openai_cog , mock_ctx ):
439- """Test _create_ai_embed with exactly 1997 characters (edge case) ."""
443+ def test_create_ai_embeds_edge_case_1997_chars (self , openai_cog , mock_ctx ):
444+ """Test _create_ai_embeds with exactly 1997 characters returns single page ."""
440445 description = "a" * 1997
441446 color = discord .Color .teal ()
442447
443- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
448+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
444449
445- # Should not be truncated
446- assert embed .description == description
447- assert len (embed .description ) == 1997
450+ assert len ( embeds ) == 1
451+ assert embeds [ 0 ] .description == description
452+ assert len (embeds [ 0 ] .description ) == 1997
448453
449- def test_create_ai_embed_edge_case_1998_chars (self , openai_cog , mock_ctx ):
450- """Test _create_ai_embed with 1998 characters (should NOT be truncated) ."""
454+ def test_create_ai_embeds_edge_case_1998_chars (self , openai_cog , mock_ctx ):
455+ """Test _create_ai_embeds with 1998 characters returns single page ."""
451456 description = "a" * 1998
452457 color = discord .Color .magenta ()
453458
454- embed = openai_cog ._create_ai_embed (mock_ctx , description , color )
459+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
455460
456- # Should NOT be truncated since 1998 <= 2000
457- assert embed .description == description
458- assert len (embed .description ) == 1998
461+ assert len (embeds ) == 1
462+ assert embeds [0 ].description == description
463+ assert len (embeds [0 ].description ) == 1998
464+
465+ def test_create_ai_embeds_splits_on_newline (self , openai_cog , mock_ctx ):
466+ """Test _create_ai_embeds splits on newline boundary when possible."""
467+ # Create text with a newline near the 2000 char boundary
468+ first_part = "a" * 1990
469+ second_part = "b" * 100
470+ description = first_part + "\n " + second_part
471+ color = discord .Color .green ()
472+
473+ embeds = openai_cog ._create_ai_embeds (mock_ctx , description , color )
474+
475+ assert len (embeds ) == 2
476+ assert embeds [0 ].description == first_part
477+ assert embeds [1 ].description == second_part
478+
479+ @pytest .mark .asyncio
480+ @patch ("src.bot.cogs.open_ai.get_bot_settings" )
481+ async def test_ai_command_pagination (self , mock_get_settings , openai_cog , mock_ctx , mock_bot_settings ):
482+ """Test AI command uses pagination for long responses."""
483+ mock_get_settings .return_value = mock_bot_settings
484+ long_response = "a" * 3000
485+
486+ with patch .object (openai_cog , "_get_ai_response" , return_value = long_response ):
487+ await openai_cog .ai .callback (openai_cog , mock_ctx , msg_text = "Long question" )
488+
489+ mock_ctx .send .assert_called_once ()
490+ call_kwargs = mock_ctx .send .call_args [1 ]
491+ assert "embed" in call_kwargs
492+ assert "view" in call_kwargs
0 commit comments