VisionApp/tests/test_ui.py

305 lines
10 KiB
Python
Raw Permalink Normal View History

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import discord
import pytest
from tickets import storage
from tickets.cog import ConfigToggleButton
from tickets.data import GuildTicketConfig, TicketCategory, TicketData, TicketStatus
from tickets.ui import (
AddMemberButton,
ClaimButton,
CloseButton,
LockButton,
PanelCreateButton,
TransferButton,
_is_staff,
_now_iso,
_send_log,
)
STAFF_ROLE_ID = 999999
@pytest.fixture(autouse=True)
def clean_storage():
storage._config_cache.clear()
storage._tickets_cache.clear()
config = GuildTicketConfig(guild_id=999)
config.staff_role_id = STAFF_ROLE_ID
storage._config_cache[999] = config
yield
storage._config_cache.clear()
storage._tickets_cache.clear()
def _make_member(is_admin=False, has_staff_role=True):
member = MagicMock(spec=discord.Member)
member.guild_permissions.administrator = is_admin
if has_staff_role:
member.roles = [MagicMock(id=STAFF_ROLE_ID)]
else:
member.roles = []
return member
def _mock_interaction(guild_id=999, user_id=1, channel_id=123, member=None):
if member is None:
member = _make_member()
interaction = MagicMock(spec=discord.Interaction)
interaction.guild_id = guild_id
interaction.channel_id = channel_id
interaction.channel = MagicMock()
interaction.channel.id = channel_id
interaction.user = MagicMock()
interaction.user.id = user_id
interaction.user.mention = f"<@{user_id}>"
interaction.response = AsyncMock()
interaction.followup = AsyncMock()
interaction.guild = MagicMock(spec=discord.Guild)
interaction.guild.id = guild_id
interaction.guild.get_member.return_value = member
interaction.channel.overwrites_for = MagicMock()
interaction.channel.set_permissions = AsyncMock()
interaction.channel.delete = AsyncMock()
interaction.channel.send = AsyncMock()
return interaction
def _save_ticket(channel_id=123, **kwargs):
defaults = dict(channel_id=channel_id, guild_id=999, user_id=1, category_id="cat")
defaults.update(kwargs)
ticket = TicketData(**defaults)
storage.save_ticket(ticket)
return ticket
class TestIsStaff:
def test_admin(self):
member = _make_member(is_admin=True)
config = storage.get_config(999)
assert _is_staff(member, config) is True
def test_staff_role_match(self):
member = _make_member(has_staff_role=True)
config = storage.get_config(999)
assert _is_staff(member, config) is True
def test_not_staff(self):
member = _make_member(has_staff_role=False)
config = storage.get_config(999)
assert _is_staff(member, config) is False
def test_category_staff_role(self):
member = _make_member(has_staff_role=False)
member.roles = [MagicMock(id=77777)]
config = storage.get_config(999)
config.categories = [TicketCategory(id="c", name="C", staff_role_id=77777)]
assert _is_staff(member, config) is True
class TestNowIso:
def test_returns_iso_string(self):
result = _now_iso()
assert "T" in result
class TestSendLog:
@pytest.mark.asyncio
async def test_no_log_channel(self):
config = storage.get_config(999)
guild = MagicMock()
guild.get_channel.return_value = None
await _send_log(guild, config, MagicMock())
guild.get_channel.assert_not_called()
@pytest.mark.asyncio
async def test_sends_to_log_channel(self):
config = storage.get_config(999)
config.log_channel_id = 555
channel = AsyncMock()
guild = MagicMock()
guild.get_channel.return_value = channel
embed = MagicMock()
await _send_log(guild, config, embed)
channel.send.assert_called_once_with(embed=embed)
class TestPanelCreateButton:
@pytest.mark.asyncio
async def test_disabled_system(self):
config = storage.get_config(999)
config.enabled = False
interaction = _mock_interaction()
await PanelCreateButton().callback(interaction)
interaction.response.send_message.assert_called_once()
@pytest.mark.asyncio
async def test_no_categories(self):
config = storage.get_config(999)
config.enabled = True
config.categories = []
interaction = _mock_interaction()
await PanelCreateButton().callback(interaction)
interaction.response.send_message.assert_called_once()
@pytest.mark.asyncio
async def test_single_category_opens_modal(self):
config = storage.get_config(999)
config.enabled = True
config.categories = [TicketCategory(id="c1", name="Cat1")]
interaction = _mock_interaction()
await PanelCreateButton().callback(interaction)
interaction.response.send_modal.assert_called_once()
@pytest.mark.asyncio
async def test_multiple_categories_sends_select(self):
config = storage.get_config(999)
config.enabled = True
config.categories = [
TicketCategory(id="c1", name="Cat1"),
TicketCategory(id="c2", name="Cat2"),
]
interaction = _mock_interaction()
await PanelCreateButton().callback(interaction)
call_kwargs = interaction.response.send_message.call_args.kwargs
assert "view" in call_kwargs
class TestClaimButton:
@pytest.mark.asyncio
async def test_not_a_ticket(self):
interaction = _mock_interaction()
await ClaimButton().callback(interaction)
assert "pas un ticket" in interaction.response.send_message.call_args[0][0]
@pytest.mark.asyncio
async def test_not_staff(self):
_save_ticket()
member = _make_member(is_admin=False, has_staff_role=False)
interaction = _mock_interaction(member=member)
await ClaimButton().callback(interaction)
assert "staff" in interaction.response.send_message.call_args[0][0].lower()
@pytest.mark.asyncio
async def test_claim_success(self):
_save_ticket()
interaction = _mock_interaction()
await ClaimButton().callback(interaction)
assert storage.get_ticket(123).claimed_by == 1
@pytest.mark.asyncio
async def test_unclaim_own(self):
_save_ticket(claimed_by=1)
interaction = _mock_interaction()
await ClaimButton().callback(interaction)
assert storage.get_ticket(123).claimed_by is None
@pytest.mark.asyncio
async def test_already_claimed_by_other(self):
_save_ticket(claimed_by=42)
interaction = _mock_interaction()
await ClaimButton().callback(interaction)
assert storage.get_ticket(123).claimed_by == 42
class TestLockButton:
@pytest.mark.asyncio
async def test_not_a_ticket(self):
interaction = _mock_interaction(channel_id=99999)
await LockButton().callback(interaction)
assert "pas un ticket" in interaction.response.send_message.call_args[0][0]
@pytest.mark.asyncio
async def test_lock_toggle(self):
_save_ticket(locked=False)
interaction = _mock_interaction()
interaction.channel.overwrites_for.return_value = MagicMock()
await LockButton().callback(interaction)
assert storage.get_ticket(123).locked is True
interaction.channel.set_permissions.assert_called_once()
@pytest.mark.asyncio
async def test_unlock_toggle(self):
_save_ticket(locked=True)
interaction = _mock_interaction()
interaction.channel.overwrites_for.return_value = MagicMock()
await LockButton().callback(interaction)
assert storage.get_ticket(123).locked is False
class TestCloseButton:
@pytest.mark.asyncio
async def test_not_a_ticket(self):
interaction = _mock_interaction(channel_id=99999)
await CloseButton().callback(interaction)
assert "pas un ticket" in interaction.response.send_message.call_args[0][0]
@pytest.mark.asyncio
async def test_not_staff(self):
_save_ticket()
member = _make_member(is_admin=False, has_staff_role=False)
interaction = _mock_interaction(member=member)
await CloseButton().callback(interaction)
assert "staff" in interaction.response.send_message.call_args[0][0].lower()
@pytest.mark.asyncio
async def test_close_success(self):
_save_ticket()
interaction = _mock_interaction()
await CloseButton().callback(interaction)
loaded = storage.get_ticket(123)
assert loaded.status == TicketStatus.CLOSED
assert loaded.closed_by == 1
@pytest.mark.asyncio
async def test_close_without_transcript(self):
config = storage.get_config(999)
config.transcript_enabled = False
_save_ticket()
interaction = _mock_interaction()
await CloseButton().callback(interaction)
assert storage.get_ticket(123).status == TicketStatus.CLOSED
class TestTransferButton:
@pytest.mark.asyncio
async def test_not_a_ticket(self):
interaction = _mock_interaction(channel_id=99999)
await TransferButton().callback(interaction)
assert "pas un ticket" in interaction.response.send_message.call_args[0][0]
@pytest.mark.asyncio
async def test_sends_user_select(self):
_save_ticket()
interaction = _mock_interaction()
await TransferButton().callback(interaction)
assert "view" in interaction.response.send_message.call_args.kwargs
class TestAddMemberButton:
@pytest.mark.asyncio
async def test_not_a_ticket(self):
interaction = _mock_interaction(channel_id=99999)
await AddMemberButton().callback(interaction)
assert "pas un ticket" in interaction.response.send_message.call_args[0][0]
@pytest.mark.asyncio
async def test_sends_user_select(self):
_save_ticket()
interaction = _mock_interaction()
await AddMemberButton().callback(interaction)
assert "view" in interaction.response.send_message.call_args.kwargs
class TestConfigToggleButton:
@pytest.mark.asyncio
async def test_toggle(self):
config = storage.get_config(999)
initial = config.enabled
interaction = _mock_interaction()
await ConfigToggleButton(enabled=initial).callback(interaction)
assert config.enabled != initial