Skip to content

Commit

Permalink
Adds test coverage for voice create
Browse files Browse the repository at this point in the history
  • Loading branch information
lexicalunit committed Dec 16, 2024
1 parent d31fdc6 commit 182301a
Show file tree
Hide file tree
Showing 14 changed files with 702 additions and 489 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Changed

- Changed twitter links to bluesky in readme.
- Updated dependencies.
- Comment out unreachable code for now, pending refactor of points/ELO

### Added

- Adds pytest-socket
- Adds coverage for `create_game_link`
- Adds coverage for voice channel creation helper function
- Adds coverage for table stream game objects

### Fixed

- Fixes typo in name of operation `safe_create_channel_invite`
- Fixes typo in the name of file `test_lfg_action.py`

## [v13.0.0](https://github.com/lexicalunit/spellbot/releases/tag/v13.0.0) - 2024-11-30

### Added
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ If you have more questions, please don't hesitate to join us on the [SpellBot Di

Thoughts and suggestions? Come join us on the [SpellBot Discord server][discord-invite]! Please
also feel free to [directly report any bugs][issues] that you encounter. Or reach out to me on
Twitter at [@SpellBotIO][follow].
BlueSky at [@spellbot.io][follow].

## 🙌 Supported By

Expand Down Expand Up @@ -161,8 +161,8 @@ Any usage of SpellBot implies that you accept the following policies.
[discord-py]: https://github.com/Rapptz/discord.py
[docker-badge]: https://img.shields.io/docker/pulls/lexicalunit/spellbot.svg
[docker-hub]: https://hub.docker.com/r/lexicalunit/spellbot
[follow-badge]: https://img.shields.io/twitter/follow/SpellBotIO?style=social
[follow]: https://twitter.com/intent/follow?screen_name=SpellBotIO
[follow-badge]: https://img.shields.io/badge/Bluesky-1185FE?style=flat&logo=bluesky&logoColor=white
[follow]: https://bsky.app/profile/spellbot.io
[heroku-badge]: https://img.shields.io/badge/cloud-heroku-green
[heroku]: https://dashboard.heroku.com/apps/lexicalunit-spellbot
[issues]: https://github.com/lexicalunit/spellbot/issues
Expand Down
882 changes: 440 additions & 442 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ safe_licenses = [

[tool.pytest.ini_options]
addopts = """
-W ignore::DeprecationWarning --no-header --cov --cov-append --cov-report xml:coverage.xml --cov-report=html -vv
-W ignore::DeprecationWarning --no-header --disable-socket --allow-unix-socket --allow-hosts=127.0.0.1,::1 --cov --cov-append --cov-report xml:coverage.xml --cov-report=html -vv
"""
asyncio_default_fixture_loop_scope = "function"

Expand Down Expand Up @@ -194,6 +194,7 @@ pytest-asyncio = "^0"
pytest-cov = ">=4,<7"
pytest-freezegun = "^0"
pytest-mock = "^3"
pytest-socket = "^0"
pytest-xdist = "^3"
ruff = "^0"
shellingham = "^1"
Expand Down
21 changes: 11 additions & 10 deletions src/spellbot/actions/admin_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,16 +442,17 @@ async def set_show_points(self, value: bool) -> None:
ephemeral=True,
)

async def set_require_confirmation(self, value: bool) -> None:
assert self.interaction.channel_id is not None
setting = await self.services.channels.set_require_confirmation(
self.interaction.channel_id, value
)
await safe_send_channel(
self.interaction,
f"Require confirmation setting for this channel has been set to: {setting}",
ephemeral=True,
)
# TODO: Refactor how confirmation/points/ELO works.
# async def set_require_confirmation(self, value: bool) -> None:
# assert self.interaction.channel_id is not None
# setting = await self.services.channels.set_require_confirmation(
# self.interaction.channel_id, value
# )
# await safe_send_channel(
# self.interaction,
# f"Require confirmation setting for this channel has been set to: {setting}",
# ephemeral=True,
# )

async def set_voice_invite(self, value: bool) -> None:
assert self.interaction.channel_id is not None
Expand Down
5 changes: 2 additions & 3 deletions src/spellbot/actions/lfg_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from spellbot.operations import (
safe_add_role,
safe_channel_reply,
safe_create_channel_invite,
safe_create_voice_channel,
safe_ensure_voice_category,
safe_fetch_text_channel,
Expand All @@ -21,7 +22,6 @@
safe_send_user,
safe_update_embed,
safe_update_embed_origin,
save_create_channel_invite,
)
from spellbot.settings import settings
from spellbot.views import BaseView, PendingGameView, StartedGameView, StartedGameViewWithConfirm
Expand Down Expand Up @@ -439,7 +439,7 @@ async def _handle_voice_creation(self, guild_xid: int) -> None:
should_create_invite = self.channel_data.get("voice_invite", False)
invite: discord.Invite | None = None
if should_create_invite:
invite = await save_create_channel_invite(
invite = await safe_create_channel_invite(
voice_channel,
max_age=2 * 60 * 60, # 2 hours
max_uses=0, # unlimited uses
Expand All @@ -461,7 +461,6 @@ async def _create_initial_post(
) -> None:
assert self.guild
assert self.channel

if message := await safe_followup_channel(
self.interaction,
content=content,
Expand Down
2 changes: 1 addition & 1 deletion src/spellbot/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ async def safe_channel_reply(


@tracer.wrap()
async def save_create_channel_invite(
async def safe_create_channel_invite(
channel: discord.abc.GuildChannel,
*args: Any,
**kwargs: Any,
Expand Down
23 changes: 12 additions & 11 deletions src/spellbot/services/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,18 @@ def set_show_points(self, xid: int, value: bool) -> bool:
DatabaseSession.commit()
return value

@sync_to_async()
def set_require_confirmation(self, xid: int, value: bool) -> bool:
query = (
update(Channel)
.where(Channel.xid == xid)
.values(require_confirmation=value)
.execution_options(synchronize_session=False)
)
DatabaseSession.execute(query)
DatabaseSession.commit()
return value
# TODO: Refactor how confirmation/points/ELO works.
# @sync_to_async()
# def set_require_confirmation(self, xid: int, value: bool) -> bool:
# query = (
# update(Channel)
# .where(Channel.xid == xid)
# .values(require_confirmation=value)
# .execution_options(synchronize_session=False)
# )
# DatabaseSession.execute(query)
# DatabaseSession.commit()
# return value

@sync_to_async()
def set_voice_invite(self, xid: int, value: bool) -> bool:
Expand Down
File renamed without changes.
11 changes: 6 additions & 5 deletions tests/actions/test_tasks_aciton.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import pytest
import pytest_asyncio
import pytz
from sqlalchemy import update

from spellbot.actions import TasksAction
from spellbot.client import build_bot
from spellbot.database import DatabaseSession
from spellbot.models import Channel, Game, Guild
from spellbot.services import ChannelsService, GamesService, GuildsService, ServicesRegistry
from tests.mocks import mock_discord_object

Expand All @@ -22,7 +24,7 @@
from pytest_mock import MockerFixture

from spellbot import SpellBot
from spellbot.models import Channel, Game, Guild
from spellbot.models import Channel, Guild
from tests.fixtures import Factories


Expand Down Expand Up @@ -465,16 +467,15 @@ async def test_when_voice_channel_is_renamed(
make_category_channel: Callable[..., discord.CategoryChannel],
action: TasksAction,
) -> None:
manage_perms = discord.Permissions(
discord.Permissions.manage_channels.flag,
)
manage_perms = discord.Permissions(discord.Permissions.manage_channels.flag)
voice_channel = make_voice_channel(
id=4001,
name=f"XXX-Game-SB{game.id}",
perms=manage_perms,
created_at=datetime.now(tz=pytz.utc) - timedelta(hours=1),
)
game.voice_xid = voice_channel.id # type: ignore
stmt = update(Game).where(Game.id == game.id).values(voice_xid=voice_channel.id)
DatabaseSession.execute(stmt)
DatabaseSession.commit()
voice_channel.voice_states.keys = lambda: False # type: ignore
make_category_channel(
Expand Down
74 changes: 70 additions & 4 deletions tests/cogs/test_events_cog.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, cast
from unittest.mock import MagicMock

import discord
import pytest
import pytest_asyncio
import pytz
from sqlalchemy.sql.expression import and_

from spellbot.actions import lfg_action
from spellbot.cogs import EventsCog
from spellbot.database import DatabaseSession
from spellbot.enums import GameFormat
from spellbot.models import Game, GameStatus, Play, User
from spellbot.enums import GameFormat, GameService
from spellbot.models import Game, GameStatus, Guild, Play, User
from tests.mixins import InteractionMixin
from tests.mocks import mock_discord_object, mock_operations

if TYPE_CHECKING:
from collections.abc import Callable

import discord

from spellbot.client import SpellBot


Expand All @@ -26,6 +29,27 @@ def cog(bot: SpellBot) -> EventsCog:
return EventsCog(bot)


@pytest_asyncio.fixture
async def make_voice_channel() -> Callable[..., discord.VoiceChannel]:
def factory(
discord_guild: discord.Guild,
id: int,
name: str,
perms: discord.Permissions,
created_at: datetime,
) -> discord.VoiceChannel:
voice = MagicMock(spec=discord.VoiceChannel)
voice.id = id
voice.name = name
voice.guild = discord_guild
voice.type = discord.ChannelType.voice
voice.permissions_for = MagicMock(return_value=perms)
voice.created_at = created_at
return voice

return factory


@pytest.mark.asyncio
class TestCogEvents(InteractionMixin):
async def test_game(
Expand Down Expand Up @@ -105,3 +129,45 @@ async def test_game_with_banned_player(
self.interaction,
f"Some of the players you mentioned can not be added to a game: <@{banned.xid}>",
)

async def test_game_with_voice_channel(
self,
cog: EventsCog,
guild: Guild,
message: discord.Message,
make_voice_channel: Callable[..., discord.VoiceChannel],
) -> None:
discord_guild = mock_discord_object(guild)
user_1 = self.factories.user.create()
user_2 = self.factories.user.create()
discord_user_1 = mock_discord_object(user_1)
discord_user_2 = mock_discord_object(user_2)
guild.voice_create = True # type: ignore
DatabaseSession.commit()
channel = self.factories.channel.create(guild=guild, voice_invite=True)
message.channel.id = channel.xid
manage_perms = discord.Permissions(discord.Permissions.manage_channels.flag)
voice_channel = make_voice_channel(
discord_guild,
id=4001,
name="does-not-matter",
perms=manage_perms,
created_at=datetime.now(tz=pytz.utc),
)
voice_invite = MagicMock(spec=discord.Invite, url="http://example")

with mock_operations(lfg_action, users=[discord_user_1, discord_user_2]):
lfg_action.safe_followup_channel.return_value = message
lfg_action.safe_create_voice_channel.return_value = voice_channel
lfg_action.safe_create_channel_invite.return_value = voice_invite

await self.run(
cog.game,
players=f"<@{user_1.xid}><@{user_2.xid}>",
format=GameFormat.MODERN.value,
service=GameService.NOT_ANY.value,
)

game = DatabaseSession.query(Game).one()
assert game.voice_xid == voice_channel.id
assert game.voice_invite_link == voice_invite.url
Loading

0 comments on commit 182301a

Please sign in to comment.