Example #1
0
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
Example #2
0
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_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
Example #4
0
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
Example #5
0
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)
Example #6
0
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_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_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)
Example #9
0
    async def call(self, event: DiscordEvent) -> DiscordResponse:
        guild_id = event.guild_id
        game_id = event.command.options.get('game-id')
        title = event.command.options.get('title')
        description = event.command.options.get('description')
        min_guess = event.command.options.get('min')
        max_guess = event.command.options.get('max')

        if game_id is None:
            game_id = self._generate_game_id(guild_id)
        else:
            existing_game = self.games_repository.get(guild_id, game_id)
            if existing_game is not None:
                message = self.message_provider.duplicate_game_id(game_id)

                return DiscordResponse.ephemeral_channel_message(message)

        game = Game(
            guild_id=guild_id,
            game_id=game_id,
            title=title,
            description=description,
            min_guess=min_guess,
            max_guess=max_guess,
            created_by=event.member.user_id,
            create_datetime=datetime.now(),
            close_datetime=None,
            closed=False,
        )
        self.games_repository.save(game)

        game_created_message = self.message_provider.game_created(game)
        return DiscordResponse.ephemeral_channel_message(game_created_message)
def test_get_minimal_game():
    # Given
    games_repository = GamesRepositoryImpl(table_name=TABLE_NAME, host=HOST)

    game = Game(
        guild_id=1,
        game_id="game-1",
        created_by=10
    )

    # When
    games_repository.save(game)
    retrieved_game = games_repository.get(game.guild_id, game.game_id)

    # Then
    assert game.guild_id == retrieved_game.guild_id
    assert game.game_id == retrieved_game.game_id
    assert not game.is_numeric()
def test_get_unknown_game_returns_none():
    # Given: an empty database
    games_repository = GamesRepositoryImpl(table_name=TABLE_NAME, host=HOST)
    other_game = Game(guild_id=1, game_id='game-id-1', created_by=10)

    # When
    games_repository.save(other_game)
    game = games_repository.get(1, 'game-id-2')

    # Then
    assert game is None
Example #12
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
Example #13
0
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
Example #14
0
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_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
Example #16
0
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
Example #17
0
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
def _game_from_model(model: EternalGuessesTable) -> Game:
    guild_id = int(re.match(PK_REGEX, model.pk).group(1))
    game_id = re.match(SK_REGEX, model.sk).group(1)

    create_datetime = None
    if model.create_datetime is not None:
        create_datetime = datetime.fromisoformat(model.create_datetime)

    close_datetime = None
    if model.close_datetime is not None:
        close_datetime = datetime.fromisoformat(model.close_datetime)

    closed = model.closed or False
    title = model.title or ""
    description = model.description or ""
    min_guess = model.min_guess
    max_guess = model.max_guess

    channel_messages = []
    if model.channel_messages is not None:
        channel_messages = list(
            _channel_message_from_model(message_model)
            for message_model in model.channel_messages)

    guesses = {}
    if model.guesses is not None:
        for (user_id, guess_model) in json.loads(model.guesses).items():
            guesses[int(user_id)] = _guess_from_model(guess_model)

    return Game(
        guild_id=guild_id,
        game_id=game_id,
        title=title,
        description=description,
        min_guess=min_guess,
        max_guess=max_guess,
        created_by=model.created_by,
        create_datetime=create_datetime,
        close_datetime=close_datetime,
        closed=closed,
        channel_messages=channel_messages,
        guesses=guesses,
    )
Example #19
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 test_get_all_games():
    # Given
    games_repository = GamesRepositoryImpl(table_name=TABLE_NAME, host=HOST)

    guild_id = 50000

    game_1_id = 'game-1'
    game_1_created_by = 10
    game_1_channel_message_channel_id = 1000
    game_1_channel_message_message_id = 2000
    game_1_create_datetime = datetime(2020, 2, 7)

    game_2_id = 'game-2'
    game_2_created_by = 20
    game_2_guess_user_id = 3000
    game_2_guess_user_nick = 'user-nick'
    game_2_guess_answer = 'guess-answer'
    game_2_guess_timestamp = datetime(2021, 2, 3, 10, 10, 10)
    game_2_create_datetime = datetime(2021, 2, 2)

    game_1 = Game(
        guild_id=guild_id,
        game_id=game_1_id,
        created_by=game_1_created_by,
        create_datetime=game_1_create_datetime,
        channel_messages=[
            ChannelMessage(channel_id=game_1_channel_message_channel_id, message_id=game_1_channel_message_message_id),
        ]
    )

    game_2 = Game(
        guild_id=guild_id,
        game_id=game_2_id,
        created_by=game_2_created_by,
        create_datetime=game_2_create_datetime,
        guesses={
            game_2_guess_user_id: GameGuess(
                user_id=game_2_guess_user_id,
                nickname=game_2_guess_user_nick,
                guess=game_2_guess_answer,
                timestamp=game_2_guess_timestamp
            )
        }
    )

    # When
    games_repository.save(game_1)
    games_repository.save(game_2)
    games = games_repository.get_all(guild_id)

    # Then
    assert len(games) == 2

    game_1 = None
    game_2 = None

    for game in games:
        if game.game_id == game_1_id:
            game_1 = game
        elif game.game_id == game_2_id:
            game_2 = game

    assert game_1 is not None
    assert game_1.guild_id == guild_id
    assert game_1.game_id == game_1_id
    assert game_1.created_by == game_1_created_by
    assert game_1.create_datetime == game_1_create_datetime
    assert game_1.close_datetime is None
    assert game_1.closed is False
    assert game_1.guesses == {}

    assert len(game_1.channel_messages) == 1
    assert game_1.channel_messages[0].channel_id == game_1_channel_message_channel_id
    assert game_1.channel_messages[0].message_id == game_1_channel_message_message_id

    assert game_2 is not None
    assert game_2.guild_id == guild_id
    assert game_2.game_id == game_2_id
    assert game_2.created_by == game_2_created_by
    assert game_2.create_datetime == game_2_create_datetime
    assert game_2.close_datetime is None
    assert game_2.closed is False
    assert game_2.channel_messages == []

    assert game_2_guess_user_id in game_2.guesses
    game_guess = game_2.guesses[game_2_guess_user_id]
    assert game_guess.user_id == game_2_guess_user_id
    assert game_guess.nickname == game_2_guess_user_nick
    assert game_guess.guess == game_2_guess_answer
    assert game_guess.timestamp == game_2_guess_timestamp
def test_get_game():
    # Given
    games_repository = GamesRepositoryImpl(table_name=TABLE_NAME, host=HOST)

    guild_id = 50
    created_by = 120
    game_id = 'game-1'
    user_id = 12123
    user_nickname = 'nickname'
    guess_answer = 'user-guess'
    guess_timestamp = datetime.now()
    message_channel_id = 1000
    message_message_id = 2000
    create_datetime = datetime(2021, 1, 1, 10, 12, 45)
    close_datetime = datetime(2021, 1, 5, 8, 6, 3)
    closed = True
    title = 'A mockery of fools'
    description = 'A tale as old as time'
    min_guess = 1
    max_guess = 500

    game = Game(
        guild_id=guild_id,
        created_by=created_by,
        create_datetime=create_datetime,
        close_datetime=close_datetime,
        closed=closed,
        game_id=game_id,
        title=title,
        description=description,
        min_guess=min_guess,
        max_guess=max_guess,
        guesses={
            user_id: GameGuess(
                user_id=user_id,
                nickname=user_nickname,
                guess=guess_answer,
                timestamp=guess_timestamp
            )
        },
        channel_messages=[
            ChannelMessage(channel_id=message_channel_id, message_id=message_message_id)
        ]
    )

    # When
    games_repository.save(game)
    retrieved_game = games_repository.get(guild_id, game_id)

    # Then
    assert retrieved_game.guild_id == guild_id
    assert retrieved_game.game_id == game_id
    assert retrieved_game.created_by == created_by
    assert retrieved_game.create_datetime == create_datetime
    assert retrieved_game.close_datetime == close_datetime
    assert retrieved_game.closed is closed

    assert retrieved_game.title == title
    assert retrieved_game.description == description

    assert retrieved_game.min_guess == min_guess
    assert retrieved_game.max_guess == max_guess
    assert retrieved_game.is_numeric()

    assert user_id in retrieved_game.guesses

    game_guess = retrieved_game.guesses[user_id]
    assert game_guess.user_id == user_id
    assert game_guess.nickname == user_nickname
    assert game_guess.guess == guess_answer
    assert game_guess.timestamp == guess_timestamp

    assert len(retrieved_game.channel_messages) == 1
    assert retrieved_game.channel_messages[0].channel_id == message_channel_id
    assert retrieved_game.channel_messages[0].message_id == message_message_id