Files
duty-teller/tests/test_handlers_group_duty_pin.py
Nikolay Tatarinov e25eb7be2f
Some checks failed
CI / lint-and-test (push) Failing after 11s
chore: update development dependencies and improve test coverage
- Upgraded `pytest-asyncio` to version 1.0 to ensure compatibility with the latest features and improvements.
- Increased the coverage threshold in pytest configuration to 80%, enhancing the quality assurance process.
- Added a new `conftest.py` file to manage shared fixtures and improve test organization.
- Introduced multiple new test files to cover various components, ensuring comprehensive test coverage across the application.
- Updated the `.coverage` file to reflect the latest coverage metrics.
2026-02-20 17:33:04 +03:00

146 lines
6.0 KiB
Python

"""Tests for duty_teller.handlers.group_duty_pin (sync wrappers, update_group_pin, pin_duty_cmd)."""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from duty_teller.handlers import group_duty_pin as mod
class TestSyncWrappers:
"""Tests for _get_duty_message_text_sync, _sync_save_pin, _sync_delete_pin, _sync_get_message_id, _get_all_pin_chat_ids_sync."""
def test_get_duty_message_text_sync(self):
with patch.object(mod, "session_scope") as mock_scope:
mock_session = MagicMock()
mock_scope.return_value.__enter__.return_value = mock_session
mock_scope.return_value.__exit__.return_value = None
with patch.object(mod, "get_duty_message_text", return_value="Duty text"):
result = mod._get_duty_message_text_sync("en")
assert result == "Duty text"
mock_scope.assert_called_once()
def test_sync_save_pin(self):
with patch.object(mod, "session_scope") as mock_scope:
mock_session = MagicMock()
mock_scope.return_value.__enter__.return_value = mock_session
mock_scope.return_value.__exit__.return_value = None
with patch.object(mod, "save_pin") as mock_save:
mod._sync_save_pin(100, 42)
mock_save.assert_called_once_with(mock_session, 100, 42)
def test_sync_delete_pin(self):
with patch.object(mod, "session_scope") as mock_scope:
mock_session = MagicMock()
mock_scope.return_value.__enter__.return_value = mock_session
mock_scope.return_value.__exit__.return_value = None
with patch.object(mod, "delete_pin") as mock_delete:
mod._sync_delete_pin(200)
mock_delete.assert_called_once_with(mock_session, 200)
def test_sync_get_message_id(self):
with patch.object(mod, "session_scope") as mock_scope:
mock_session = MagicMock()
mock_scope.return_value.__enter__.return_value = mock_session
mock_scope.return_value.__exit__.return_value = None
with patch.object(mod, "get_message_id", return_value=99):
result = mod._sync_get_message_id(300)
assert result == 99
def test_get_all_pin_chat_ids_sync(self):
with patch.object(mod, "session_scope") as mock_scope:
mock_session = MagicMock()
mock_scope.return_value.__enter__.return_value = mock_session
mock_scope.return_value.__exit__.return_value = None
with patch.object(mod, "get_all_pin_chat_ids", return_value=[10, 20]):
result = mod._get_all_pin_chat_ids_sync()
assert result == [10, 20]
@pytest.mark.asyncio
async def test_update_group_pin_edits_message_and_schedules_next():
"""update_group_pin: with message_id and text, edits message and schedules next update."""
context = MagicMock()
context.job = MagicMock()
context.job.data = {"chat_id": 123}
context.bot = MagicMock()
context.bot.edit_message_text = AsyncMock()
context.application = MagicMock()
context.application.job_queue = MagicMock()
context.application.job_queue.get_jobs_by_name = MagicMock(return_value=[])
context.application.job_queue.run_once = MagicMock()
with patch.object(
mod, "_sync_get_message_id", return_value=1
):
with patch.object(
mod, "_get_duty_message_text_sync", return_value="Current duty"
):
with patch.object(
mod, "_get_next_shift_end_sync", return_value=None
):
with patch.object(mod, "_schedule_next_update", AsyncMock()):
await mod.update_group_pin(context)
context.bot.edit_message_text.assert_called_once_with(
chat_id=123, message_id=1, text="Current duty"
)
@pytest.mark.asyncio
async def test_update_group_pin_no_message_id_skips():
"""update_group_pin: when no pin record (message_id None), does not edit."""
context = MagicMock()
context.job = MagicMock()
context.job.data = {"chat_id": 456}
context.bot = MagicMock()
context.bot.edit_message_text = AsyncMock()
with patch.object(mod, "_sync_get_message_id", return_value=None):
await mod.update_group_pin(context)
context.bot.edit_message_text.assert_not_called()
@pytest.mark.asyncio
async def test_pin_duty_cmd_group_only_reply():
"""pin_duty_cmd in private chat -> reply group_only."""
update = MagicMock()
update.message = MagicMock()
update.message.reply_text = AsyncMock()
update.effective_chat = MagicMock()
update.effective_chat.type = "private"
update.effective_chat.id = 1
update.effective_user = MagicMock()
context = MagicMock()
with patch("duty_teller.handlers.group_duty_pin.get_lang", return_value="en"):
with patch("duty_teller.handlers.group_duty_pin.t") as mock_t:
mock_t.return_value = "Group only"
await mod.pin_duty_cmd(update, context)
update.message.reply_text.assert_called_once_with("Group only")
mock_t.assert_called_with("en", "pin_duty.group_only")
@pytest.mark.asyncio
async def test_pin_duty_cmd_group_pins_and_replies_pinned():
"""pin_duty_cmd in group with existing pin record -> pin and reply pinned."""
update = MagicMock()
update.message = MagicMock()
update.message.reply_text = AsyncMock()
update.effective_chat = MagicMock()
update.effective_chat.type = "group"
update.effective_chat.id = 100
update.effective_user = MagicMock()
context = MagicMock()
context.bot = MagicMock()
context.bot.pin_chat_message = AsyncMock()
with patch("duty_teller.handlers.group_duty_pin.get_lang", return_value="en"):
with patch.object(mod, "_sync_get_message_id", return_value=5):
with patch("duty_teller.handlers.group_duty_pin.t") as mock_t:
mock_t.return_value = "Pinned"
await mod.pin_duty_cmd(update, context)
context.bot.pin_chat_message.assert_called_once_with(
chat_id=100, message_id=5, disable_notification=True
)
update.message.reply_text.assert_called_once_with("Pinned")