Files
duty-teller/tests/test_handlers_group_duty_pin.py
Nikolay Tatarinov aa89494bd5
All checks were successful
CI / lint-and-test (push) Successful in 22s
feat: enhance calendar ICS generation with event type filtering
- Added support for filtering calendar events by type in the ICS generation API endpoint, allowing users to specify whether to include only duty shifts or all event types (duty, unavailable, vacation).
- Updated the `get_duties_for_user` function to accept an optional `event_types` parameter, enabling more flexible data retrieval based on user preferences.
- Enhanced unit tests to cover the new event type filtering functionality, ensuring correct behavior and reliability of the ICS generation process.
2026-02-20 17:47:52 +03:00

142 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")