Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/bot/tools/bot_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,12 @@ def _dict_to_embed(data: dict) -> discord.Embed:
return discord.Embed.from_dict(data)

async def send_and_save(self, ctx) -> None:
"""Send the first page and save all pages to the database."""
msg = await ctx.send(embed=self.pages[0], view=self)
"""Send the first page and save all pages to the database.

Uses _send_with_retry so transient Discord errors (5xx, code 40062) are
retried before propagating to the command error handler.
"""
msg = await _send_with_retry(ctx, ctx.send, embed=self.pages[0], view=self)
self.message = msg
from src.database.dal.bot.embed_pages_dal import EmbedPagesDal

Expand Down
30 changes: 30 additions & 0 deletions tests/unit/bot/tools/test_bot_utils_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,36 @@ async def test_send_and_save(self, mock_dal_class):
assert call_args[0][2] == 42 # author_id
assert len(call_args[0][3]) == 2 # pages data

@pytest.mark.asyncio
@patch("src.database.dal.bot.embed_pages_dal.EmbedPagesDal")
async def test_send_and_save_retries_on_transient_error(self, mock_dal_class):
"""send_and_save retries on 429 code 40062 instead of letting it propagate."""
mock_dal = MagicMock()
mock_dal.insert_embed_pages = AsyncMock()
mock_dal_class.return_value = mock_dal

pages = self._make_pages(2)
view = EmbedPaginatorView(pages, author_id=42)

ctx = MagicMock()
mock_msg = MagicMock()
mock_msg.id = 111
mock_msg.channel.id = 222
# First attempt: 429 code 40062 (transient). Notice send: ok. Retry: ok.
rate_limited = _make_http_exception(429, code=40062)
notice_msg = MagicMock()
ctx.send = AsyncMock(side_effect=[rate_limited, notice_msg, mock_msg])
ctx.bot.db_session = MagicMock()
ctx.bot.log = MagicMock()

with patch("src.bot.tools.bot_utils.asyncio.sleep", new_callable=AsyncMock):
await view.send_and_save(ctx)

# 3 ctx.send calls: failed attempt, retry notice, successful retry
assert ctx.send.await_count == 3
assert view.message is mock_msg
mock_dal.insert_embed_pages.assert_awaited_once()

@pytest.mark.asyncio
@patch("src.database.dal.bot.embed_pages_dal.EmbedPagesDal")
async def test_load_from_db_pages_already_set(self, mock_dal_class):
Expand Down
Loading
Loading