diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 17:11:51 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 17:11:51 +0900 |
| commit | 66368d580cf569b50a33e438f2287a977e6fc704 (patch) | |
| tree | 8a3b9e538333abf4564846849affec1ef1279e05 /tests/edge_cases/test_notifier_failures.py | |
| parent | 2d1530f210f4b4f679a5d3b3597c4815904398a7 (diff) | |
test: add edge case tests for zero volume, empty data, extreme values
Diffstat (limited to 'tests/edge_cases/test_notifier_failures.py')
| -rw-r--r-- | tests/edge_cases/test_notifier_failures.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/tests/edge_cases/test_notifier_failures.py b/tests/edge_cases/test_notifier_failures.py new file mode 100644 index 0000000..4ba781f --- /dev/null +++ b/tests/edge_cases/test_notifier_failures.py @@ -0,0 +1,85 @@ +"""Tests for TelegramNotifier failure modes.""" + +import asyncio +import logging +import sys +from pathlib import Path +from unittest.mock import AsyncMock, MagicMock, patch + +import aiohttp +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine")) +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "backtester" / "src")) + +from shared.notifier import TelegramNotifier + + +@pytest.fixture +def notifier(): + return TelegramNotifier(bot_token="fake-token", chat_id="12345") + + +class TestSendConnectionError: + """TelegramNotifier.send() when session.post raises ConnectionError should not crash.""" + + @pytest.mark.asyncio + async def test_connection_error_does_not_crash(self, notifier, caplog): + mock_session = AsyncMock(spec=aiohttp.ClientSession) + mock_session.closed = False + mock_session.post = MagicMock(side_effect=aiohttp.ClientError("Connection refused")) + notifier._session = mock_session + + with caplog.at_level(logging.WARNING): + await notifier.send("test message") + # Should not raise, should log the error + + +class TestSendRateLimited: + """TelegramNotifier.send() when API returns 429 should retry.""" + + @pytest.mark.asyncio + async def test_rate_limit_retries(self, notifier): + mock_response_429 = AsyncMock() + mock_response_429.status = 429 + mock_response_429.json = AsyncMock(return_value={"description": "Too Many Requests"}) + mock_response_429.__aenter__ = AsyncMock(return_value=mock_response_429) + mock_response_429.__aexit__ = AsyncMock(return_value=False) + + mock_response_200 = AsyncMock() + mock_response_200.status = 200 + mock_response_200.__aenter__ = AsyncMock(return_value=mock_response_200) + mock_response_200.__aexit__ = AsyncMock(return_value=False) + + mock_session = AsyncMock(spec=aiohttp.ClientSession) + mock_session.closed = False + # First two calls return 429, third returns 200 + mock_session.post = MagicMock( + side_effect=[mock_response_429, mock_response_429, mock_response_200] + ) + notifier._session = mock_session + + with patch("shared.notifier.asyncio.sleep", new_callable=AsyncMock): + await notifier.send("test message") + + # Should have been called 3 times (2 retries + 1 success) + assert mock_session.post.call_count == 3 + + +class TestCloseAlreadyClosed: + """TelegramNotifier.close() when session already closed should not crash.""" + + @pytest.mark.asyncio + async def test_close_no_session(self): + notifier = TelegramNotifier(bot_token="fake", chat_id="123") + # No session created yet + await notifier.close() # Should not raise + + @pytest.mark.asyncio + async def test_close_already_closed_session(self, notifier): + mock_session = AsyncMock(spec=aiohttp.ClientSession) + mock_session.close = AsyncMock() + notifier._session = mock_session + + await notifier.close() + await notifier.close() # Second close should not crash |
