async def test_close_already_closed_game(): # Given guild_id = 1007 game_id = 'game-id-1' # We have a closed game game = Game(game_id=game_id, guild_id=guild_id, closed=True) games_repository = FakeGamesRepository([game]) event = _make_event(guild_id=guild_id, options={ 'game-id': game.game_id, }) game_already_closed_message = "Game was already closed closed." message_provider = MagicMock(MessageProvider) message_provider.game_closed.return_value = game_already_closed_message route = CloseGameRoute( games_repository=games_repository, discord_messaging=FakeDiscordMessaging(), message_provider=message_provider, ) # When we close it again response = await route.call(event) # Then it should just still be closed saved_game = games_repository.get(guild_id, game.game_id) assert saved_game.closed assert response.is_ephemeral assert response.content == game_already_closed_message
async def test_post_saves_message_id_to_game(): # Given guild_id = 1001 game_id = 'game-1' channel_id = 50001 new_message_id = 1000 discord_messaging = FakeDiscordMessaging() discord_messaging.created_channel_message_id = 1000 game = Game(guild_id=guild_id, game_id=game_id) games_repository = FakeGamesRepository([game]) post_route = PostRoute( games_repository=games_repository, discord_messaging=discord_messaging, message_provider=FakeMessageProvider(), ) # When event = _make_event(guild_id=guild_id, options={ 'game-id': game_id, 'channel': channel_id, }) await post_route.call(event) # Then updated_game = games_repository.get(guild_id, game_id) assert updated_game.channel_messages is not None assert any(channel_message.channel_id == channel_id and channel_message.message_id == new_message_id for channel_message in updated_game.channel_messages)
async def test_guess_game_does_not_exist(): # Given guild_id = 1001 game_id = 'fun-game' user_id = 2001 event_channel_id = 7002 member = DiscordMember() fake_games_repository = FakeGamesRepository([]) error_message = "error dm" message_provider = MagicMock(MessageProvider) message_provider.error_game_not_found.return_value = error_message guess_route = _route( games_repository=fake_games_repository, message_provider=message_provider, ) # When event = _create_guess_event( guild_id=guild_id, game_id=game_id, user_id=user_id, event_channel_id=event_channel_id, user_nickname='nickname', member=member, ) response = await guess_route.call(event) # Then assert len(fake_games_repository.get_all(guild_id=guild_id)) == 0 assert response.is_ephemeral assert response.content == error_message
async def test_guess_non_numerical_on_numeric_game(): # Given guild_id = 1005 user_id = 13000 game_id = 'game-id' response_message = "Guess must be a number" message_provider = MagicMock(MessageProvider, autospec=True) message_provider.invalid_guess.return_value = response_message # And we have a game with a min and a max game = Game(guild_id=guild_id, game_id=game_id, min_guess=1, max_guess=100) games_repository = FakeGamesRepository([game]) guess_route = _route( games_repository=games_repository, message_provider=message_provider, ) # When event = _create_guess_event(guild_id, game.game_id, user_id, 'nickname') response = await guess_route.call(event) # Then assert response.is_ephemeral assert response.content == response_message # And the guess was not added updated_game = games_repository.get(guild_id=guild_id, game_id=game_id) assert len(updated_game.guesses) == 0
async def test_edit_guess(): # Given guild_id = 10 game_id = "game-1" guessing_player_id = 100 old_guess = "<My Guess>" guess_edited_message = "Guess has been edited." message_provider = MagicMock(MessageProvider) message_provider.guess_edited.return_value = guess_edited_message game = Game( guild_id=guild_id, game_id=game_id, guesses={ guessing_player_id: GameGuess( user_id=guessing_player_id, guess=old_guess, ) } ) games_repository = FakeGamesRepository(games=[game]) game_post_manager = AsyncMock(GamePostManager) route = _route( games_repository=games_repository, message_provider=message_provider, game_post_manager=game_post_manager, ) # When new_guess = "500" event = _make_event( guild_id=guild_id, options={ 'game-id': game_id, 'member': str(guessing_player_id), 'guess': new_guess, } ) response = await route.call(event) # Then updated_game = games_repository.get(guild_id, game_id) assert updated_game.guesses[guessing_player_id].guess == new_guess game_post_manager.update.assert_called_with(game) assert response.is_ephemeral assert response.content == guess_edited_message
async def test_guess_replies_with_ephemeral_message(): # Given guild_id = 1005 user_id = 13000 game_id = 'game-id' response_message = "message with new guess" message_provider = MagicMock(MessageProvider, autospec=True) message_provider.guess_added.return_value = response_message game = Game(guild_id=guild_id, game_id=game_id) fake_games_repository = FakeGamesRepository([game]) guess_route = _route( games_repository=fake_games_repository, message_provider=message_provider, ) # When event = _create_guess_event(guild_id, game.game_id, user_id, 'nickname') response = await guess_route.call(event) # Then assert response.is_ephemeral assert response.content == response_message
async def test_list_all_without_closed_option(): # Given list_games_message = "message listing all games, open and closed" message_provider = MagicMock(MessageProvider) message_provider.channel_manage_list_all_games.return_value = list_games_message open_game = Game(closed=False) closed_game = Game(closed=True) games_repository = FakeGamesRepository([open_game, closed_game]) route = ListGamesRoute( games_repository=games_repository, message_provider=message_provider, ) # When event = _make_event(options={}) response = await route.call(event) # Then message_provider.channel_manage_list_all_games.assert_called() used_games = message_provider.channel_manage_list_all_games.call_args.args[ 0] assert closed_game in used_games assert open_game in used_games assert response.is_ephemeral assert response.content == list_games_message
async def test_list_all_open_games(): # Given guild_id = 100 # We have one open and one closed game open_game = Game(closed=False) closed_game = Game(closed=True) games_repository = FakeGamesRepository([open_game, closed_game]) # And we have a mock message provider list_games_message = "message listing all games, open and closed" message_provider = MagicMock(MessageProvider) message_provider.channel_manage_list_open_games.return_value = list_games_message route = ListGamesRoute( games_repository=games_repository, message_provider=message_provider, ) # When we list only the open games event = _make_event(guild_id=guild_id, options={'closed': False}) response = await route.call(event) # Then: a message is sent based on only the open games message_provider.channel_manage_list_open_games.assert_called() used_games = message_provider.channel_manage_list_open_games.call_args.args[ 0] assert closed_game not in used_games assert open_game in used_games assert response.is_ephemeral assert response.content == list_games_message
async def test_edit_guess_game_does_not_exist(): # Given guild_id = 10 game_id = "game-1" guessing_player_id = 100 game_not_found_message = "No game found." message_provider = MagicMock(MessageProvider) message_provider.error_game_not_found.return_value = game_not_found_message games_repository = FakeGamesRepository(games=[]) route = _route( games_repository=games_repository, message_provider=message_provider, ) # When event = _make_event( guild_id=guild_id, options={ 'game-id': game_id, 'member': str(guessing_player_id), 'guess': "some new guess", } ) response = await route.call(event) # Then assert response.is_ephemeral assert response.content == game_not_found_message
async def test_post_without_channel_uses_event_channel(): # Given guild_id = 1001 game_id = 'game-1' event_channel_id = 50001 # We have a game game = Game(guild_id=guild_id, game_id=game_id) games_repository = FakeGamesRepository([game]) post_embed = discord.Embed() message_provider = MagicMock(MessageProvider) message_provider.game_post_embed.return_value = post_embed discord_messaging = FakeDiscordMessaging() post_route = PostRoute( games_repository=games_repository, discord_messaging=discord_messaging, message_provider=message_provider, ) # When we post a channel message without an explicit target channel event = _make_event(guild_id=guild_id, channel_id=event_channel_id, options={'game-id': game_id}) await post_route.call(event) # Then a message for that game is posted in the channel we sent this command from assert { 'channel_id': event_channel_id, 'embed': post_embed } in discord_messaging.sent_channel_messages
async def test_guess_updates_channel_messages(): # Given guild_id = 1001 user_id = 12000 post_embed = discord.Embed() message_provider = MagicMock(MessageProvider) message_provider.game_post_embed.return_value = post_embed game = Game( guild_id=guild_id, game_id="game-1", ) games_repository = FakeGamesRepository([game]) game_post_manager = MagicMock(GamePostManager) guess_route = _route( games_repository=games_repository, message_provider=message_provider, game_post_manager=game_post_manager, ) # When event = _create_guess_event(guild_id, game.game_id, user_id, 'nickname') await guess_route.call(event) # Then game_post_manager.update.assert_called_with(game)
async def test_guess_duplicate_guess(): # Given guild_id = 1000 game_id = 'game-id' event_channel_id = 7002 guessing_user_id = 2000 member = DiscordMember(user_id=guessing_user_id, ) old_guess = '100' new_guess = '100' existing_guesses = { guessing_user_id: GameGuess(user_id=guessing_user_id, guess=old_guess), } # We have a game existing_game = Game(guild_id=guild_id, game_id=game_id, guesses=existing_guesses) games_repository = FakeGamesRepository(games=[existing_game]) duplicate_guess_message = "You already placed a guess for this game." message_provider = MagicMock(MessageProvider) message_provider.error_duplicate_guess.return_value = duplicate_guess_message guess_route = _route(games_repository=games_repository, message_provider=message_provider) # When we guess '42' as the same user event = _create_guess_event( guild_id=guild_id, user_id=guessing_user_id, game_id=game_id, guess=new_guess, event_channel_id=event_channel_id, member=member, ) response = await guess_route.call(event) # Then no new guess is added saved_game = games_repository.get(guild_id, existing_game.game_id) assert saved_game.guesses[guessing_user_id].guess == old_guess # And an ephemeral error is the response assert response.is_ephemeral assert response.content == duplicate_guess_message
async def test_guess_closed_game(): # Given guild_id = 1515 game_id = 'closed-game' guessing_user_id = 3000 other_user_id = 3050 event_channel_id = 7775 member = DiscordMember() # We have a closed game game = Game(game_id=game_id, guild_id=guild_id, closed=True, guesses={ other_user_id: GameGuess(), }) games_repository = FakeGamesRepository([game]) duplicate_guess_message = "This game has been closed for new guesses." message_provider = MagicMock(MessageProvider) message_provider.error_guess_on_closed_game.return_value = duplicate_guess_message route = _route( games_repository=games_repository, message_provider=message_provider, ) # When event = _create_guess_event( guild_id=guild_id, game_id=game_id, user_id=guessing_user_id, event_channel_id=event_channel_id, member=member, ) response = await route.call(event) # Then: no guess is added saved_game = games_repository.get(guild_id, game_id) assert list(saved_game.guesses.keys()) == [other_user_id] # And an ephemeral error is the response assert response.is_ephemeral assert response.content == duplicate_guess_message
async def test_lower_than_min(): # Given guild_id = 1005 user_id = 13000 game_id = 'game-id' min_guess = -5 response_message = "Guess must be a number higher than -5" message_provider = MagicMock(MessageProvider, autospec=True) message_provider.invalid_guess.return_value = response_message # And we have a game with a max game = Game( guild_id=guild_id, game_id=game_id, min_guess=min_guess, ) games_repository = FakeGamesRepository([game]) guess_route = _route( games_repository=games_repository, message_provider=message_provider, ) # When we guess lower than that guess = "-6" event = _create_guess_event( guild_id=guild_id, game_id=game.game_id, user_id=user_id, user_nickname='nickname', guess=guess, ) response = await guess_route.call(event) # Then assert response.is_ephemeral assert response.content == response_message # And the guess was not added updated_game = games_repository.get(guild_id=guild_id, game_id=game_id) assert len(updated_game.guesses) == 0
async def test_guess_updates_game_guesses(mock_datetime): # Given game_id = 'game-id' guild_id = 1000 other_user_id = 5000 user_id = 100 user_nickname = 'user-1' guess_answer = '42' guess_timestamp = datetime.now() mock_datetime.now.return_value = guess_timestamp existing_game = Game(guild_id=guild_id, game_id=game_id, guesses={ other_user_id: GameGuess(), }) fake_games_repository = FakeGamesRepository([existing_game]) guess_route = _route(games_repository=fake_games_repository, ) # When we make a guess event = _create_guess_event(guild_id=guild_id, game_id=game_id, user_id=user_id, user_nickname=user_nickname, guess=guess_answer) await guess_route.call(event) # Then saved_game = fake_games_repository.get(guild_id, game_id) assert saved_game.guild_id == guild_id assert saved_game.game_id == game_id assert other_user_id in saved_game.guesses.keys() saved_guess = saved_game.guesses[user_id] assert saved_guess.user_id == user_id assert saved_guess.user_nickname == user_nickname assert saved_guess.timestamp == guess_timestamp assert saved_guess.guess == guess_answer
async def test_post_creates_channel_message(): # Given guild_id = 1001 game_id = 'game-1' channel_id = 50001 # We have a game game = Game(guild_id=guild_id, game_id=game_id) games_repository = FakeGamesRepository([game]) # And we have a mock message provider post_embed = discord.Embed() message_provider = MagicMock(MessageProvider) message_provider.game_post_embed.return_value = post_embed discord_messaging = FakeDiscordMessaging() post_route = PostRoute( games_repository=games_repository, discord_messaging=discord_messaging, message_provider=message_provider, ) # When we post a channel message for our game with an explicit channel event = _make_event(guild_id=guild_id, options={ 'game-id': game_id, 'channel': channel_id, }) await post_route.call(event) # Then a message about that game is posted in the given channel assert { 'channel_id': channel_id, 'embed': post_embed } in discord_messaging.sent_channel_messages # And the channel id is saved in the game saved_game = games_repository.get(guild_id, game_id) assert len(saved_game.channel_messages) == 1
def _route(games_repository=None, message_provider=None, game_post_manager=None): if games_repository is None: games_repository = FakeGamesRepository() if message_provider is None: message_provider = MagicMock(MessageProvider) if game_post_manager is None: game_post_manager = AsyncMock(GamePostManager) return EditGuessRoute( games_repository=games_repository, message_provider=message_provider, game_post_manager=game_post_manager, )
async def test_edit_guess_does_not_exist(): # Given guild_id = 10 game_id = "game-1" delete_member_id = 200 guess_not_found_message = "No guess found." message_provider = MagicMock(MessageProvider) message_provider.error_guess_not_found.return_value = guess_not_found_message game = Game( guild_id=guild_id, game_id=game_id, guesses={ -1: GameGuess( user_id=-1, guess="<your guess>", ) } ) games_repository = FakeGamesRepository(games=[game]) game_post_manager = AsyncMock(GamePostManager) route = _route( games_repository=games_repository, message_provider=message_provider, game_post_manager=game_post_manager, ) # When event = _make_event( guild_id=guild_id, options={ 'game-id': game_id, 'member': str(delete_member_id), 'guess': "some new guess", } ) response = await route.call(event) # Then game_post_manager.update.assert_not_called() assert response.is_ephemeral assert response.content == guess_not_found_message
async def test_close_updates_channel_messages(): guild_id = 1007 game_id = 'game-id-1' # We have an open game with channel messages channel_message_1 = ChannelMessage(channel_id=1, message_id=10) channel_message_2 = ChannelMessage(channel_id=2, message_id=11) game = Game(game_id=game_id, guild_id=guild_id, closed=False, channel_messages=[channel_message_1, channel_message_2]) games_repository = FakeGamesRepository([game]) post_embed = discord.Embed() message_provider = MagicMock(MessageProvider) message_provider.game_post_embed.return_value = post_embed discord_messaging = FakeDiscordMessaging() route = CloseGameRoute( games_repository=games_repository, discord_messaging=discord_messaging, message_provider=message_provider, ) # When we close it event = _make_event(guild_id=guild_id, options={ 'game-id': game.game_id, }) await route.call(event) # Then the channel messages are updated assert len(discord_messaging.updated_channel_messages) == 2 assert { 'channel_id': channel_message_1.channel_id, 'message_id': channel_message_1.message_id, 'embed': post_embed, } in discord_messaging.updated_channel_messages assert { 'channel_id': channel_message_2.channel_id, 'message_id': channel_message_2.message_id, 'embed': post_embed, } in discord_messaging.updated_channel_messages
def _route(games_repository: GamesRepository = None, message_provider: MessageProvider = None, game_post_manager: GamePostManager = None): if games_repository is None: games_repository = FakeGamesRepository([]) if message_provider is None: message_provider = FakeMessageProvider() if game_post_manager is None: game_post_manager = AsyncMock(GamePostManager, autospec=True) guess_route = GuessRoute( games_repository=games_repository, message_provider=message_provider, game_post_manager=game_post_manager, ) return guess_route
async def test_post_invalid_game_id_sends_dm_error(): # Given guild_id = 1001 game_id = 'game-1' channel_id = 50001 event_channel_id = 7733 # We have no games games_repository = FakeGamesRepository([]) discord_messaging = FakeDiscordMessaging() formatted_error = "mock formatted error" message_provider = MagicMock(MessageProvider) message_provider.error_game_not_found.return_value = formatted_error post_route = PostRoute( games_repository=games_repository, discord_messaging=discord_messaging, message_provider=message_provider, ) discord_member = DiscordMember() # When event = _make_event(guild_id=guild_id, discord_member=discord_member, channel_id=event_channel_id, options={ 'game-id': game_id, 'channel': channel_id, }) response = await post_route.call(event) # Then message_provider.error_game_not_found.assert_called_with(game_id) assert response.is_ephemeral assert response.content == formatted_error