async def test_cancel_twice(ladder_service: LadderService, player_factory): p1 = player_factory('Dostya', player_id=1, ladder_rating=(1500, 500), ladder_games=0) p2 = player_factory('Brackman', player_id=2, ladder_rating=(2000, 500), ladder_games=0) search = Search([p1]) search2 = Search([p2]) await ladder_service.start_search(p1, search, 'ladder1v1') await ladder_service.start_search(p2, search2, 'ladder1v1') searches = ladder_service._cancel_existing_searches(p1) assert search.is_cancelled assert searches == [search] assert not search2.is_cancelled searches = ladder_service._cancel_existing_searches(p1) assert searches == [] searches = ladder_service._cancel_existing_searches(p2) assert search2.is_cancelled assert searches == [search2]
async def test_start_search_cancels_previous_search( ladder_service: LadderService, player_factory, event_loop ): p1 = player_factory( "Dostya", player_id=1, ladder_rating=(1500, 500), ladder_games=0, with_lobby_connection=True ) ladder_service.start_search([p1], "ladder1v1") await exhaust_callbacks(event_loop) search1 = ladder_service._searches[p1]["ladder1v1"] assert p1.state == PlayerState.SEARCHING_LADDER assert search1 in ladder_service.queues["ladder1v1"]._queue ladder_service.start_search([p1], "ladder1v1") await exhaust_callbacks(event_loop) search2 = ladder_service._searches[p1]["ladder1v1"] assert p1.state == PlayerState.SEARCHING_LADDER assert search1.is_cancelled assert search1 not in ladder_service.queues["ladder1v1"]._queue assert search2 in ladder_service.queues["ladder1v1"]._queue
def test_inform_player(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) ladder_service.inform_player(p1) assert p1.lobby_connection.sendJSON.called
async def test_start_game_called_on_match(ladder_service: LadderService, player_factory): p1 = player_factory( "Dostya", player_id=1, ladder_rating=(2300, 64), ladder_games=0, with_lobby_connection=True ) p2 = player_factory( "QAI", player_id=2, ladder_rating=(2350, 125), ladder_games=0, with_lobby_connection=True ) ladder_service.start_game = CoroutineMock() ladder_service.write_rating_progress = CoroutineMock() ladder_service.start_search([p1], "ladder1v1") ladder_service.start_search([p2], "ladder1v1") await asyncio.sleep(2) ladder_service.write_rating_progress.assert_called() ladder_service.start_game.assert_called_once()
def test_inform_player(ladder_service: LadderService): p1 = mock.create_autospec(Player("Dostya", id=1)) p1.ladder_rating = (1500, 500) ladder_service.inform_player(p1) assert p1.lobby_connection.sendJSON.called
async def test_write_rating_progress_message_2(ladder_service: LadderService, player_factory): player = player_factory(ladder_rating=(1500, 400.1235)) player.write_message = CoroutineMock() ladder_service.write_rating_progress(player, RatingType.LADDER_1V1) player.write_message.assert_called_once() assert player.write_message.call_args[0][0].get("command") == "notice" assert player.write_message.call_args[0][0].get("style") == "info" assert "40%" in player.write_message.call_args[0][0].get("text", "")
async def test_write_rating_progress_other_rating( ladder_service: LadderService, player_factory): player = player_factory(ladder_rating=(1500, 500), global_rating=(1500, 400.1235)) player.write_message = CoroutineMock() # There's no reason we would call it with global, but the logic is the same # and global is an available rating that's not ladder ladder_service.write_rating_progress(player, RatingType.GLOBAL) player.write_message.assert_called_once() assert player.write_message.call_args[0][0].get("command") == "notice" assert player.write_message.call_args[0][0].get("style") == "info" assert "40%" in player.write_message.call_args[0][0].get("text", "")
async def test_queue_initialization(database, game_service): ladder_service = LadderService(database, game_service) def make_mock_queue(*args, **kwargs): queue = create_autospec(MatchmakerQueue) queue.map_pools = {} return queue with mock.patch("server.ladder_service.MatchmakerQueue", make_mock_queue): for name in list(ladder_service.queues.keys()): ladder_service.queues[name] = make_mock_queue() await ladder_service.initialize() for queue in ladder_service.queues.values(): queue.initialize.assert_called_once()
async def test_cancel_all_searches(ladder_service: LadderService, player_factory, event_loop): p1 = player_factory(login="******", player_id=1, ladder_rating=(1500, 500), ladder_games=0) ladder_service.start_search([p1], "ladder1v1") await exhaust_callbacks(event_loop) search = ladder_service._searches[p1]["ladder1v1"] assert p1.state == PlayerState.SEARCHING_LADDER assert search in ladder_service.queues["ladder1v1"]._queue assert not search.is_cancelled ladder_service.cancel_search(p1) assert p1.state == PlayerState.IDLE assert search.is_cancelled assert "ladder1v1" not in ladder_service._searches[p1]
async def test_start_search_multiqueue_multiple_players( ladder_service: LadderService, player_factory, queue_factory, event_loop ): ladder_service.queues["tmm2v2"] = queue_factory("tmm2v2") p1 = player_factory( "Dostya", player_id=1, ladder_rating=(1000, 10), with_lobby_connection=True ) p2 = player_factory( "Brackman", player_id=2, ladder_rating=(1000, 10), with_lobby_connection=True ) ladder_service.start_search([p1, p2], "ladder1v1") await exhaust_callbacks(event_loop) assert "ladder1v1" in ladder_service._searches[p1] assert "ladder1v1" in ladder_service._searches[p2] ladder_service.start_search([p1, p2], "tmm2v2") await exhaust_callbacks(event_loop) assert "ladder1v1" in ladder_service._searches[p1] assert "tmm2v2" in ladder_service._searches[p1] assert "ladder1v1" in ladder_service._searches[p2] assert "tmm2v2" in ladder_service._searches[p2] ladder_service.cancel_search(p1, "tmm2v2") await exhaust_callbacks(event_loop) assert "ladder1v1" in ladder_service._searches[p1] assert "tmm2v2" not in ladder_service._searches[p1] assert "ladder1v1" in ladder_service._searches[p2] assert "tmm2v2" not in ladder_service._searches[p2] ladder_service.cancel_search(p2, "ladder1v1") await exhaust_callbacks(event_loop) assert "ladder1v1" not in ladder_service._searches[p1] assert "tmm2v2" not in ladder_service._searches[p1] assert "ladder1v1" not in ladder_service._searches[p2] assert "tmm2v2" not in ladder_service._searches[p2]
async def test_write_rating_progress_message_2( ladder_service: LadderService, player_factory ): player = player_factory(ladder_rating=(1500, 400.1235)) player.write_message = CoroutineMock() ladder_service.write_rating_progress(player, RatingType.LADDER_1V1) player.write_message.assert_called_once_with({ "command": "notice", "style": "info", "text": ( "The system is still learning you.<b><br><br>" "The learning phase is 40% complete<b>" ) })
async def test_cancel_twice(ladder_service: LadderService, player_factory): p1 = player_factory(login="******", player_id=1, ladder_rating=(1500, 500), ladder_games=0) p2 = player_factory(login="******", player_id=2, ladder_rating=(2000, 500), ladder_games=0) ladder_service.start_search([p1], "ladder1v1") search = ladder_service._searches[p1]["ladder1v1"] ladder_service.start_search([p2], "ladder1v1") search2 = ladder_service._searches[p2]["ladder1v1"] ladder_service.cancel_search(p1) assert search.is_cancelled assert not search2.is_cancelled ladder_service.cancel_search(p1) ladder_service.cancel_search(p2) assert search2.is_cancelled
async def test_cancel_twice(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) p1.numGames = 0 p2 = mock.create_autospec(Player('Brackman', id=1)) p2.ladder_rating = (2000, 50) p2.numGames = 0 search = Search([p1]) search2 = Search([p2]) ladder_service.start_search(p1, search, 'ladder1v1') ladder_service.start_search(p2, search2, 'ladder1v1') searches = ladder_service._cancel_existing_searches(p1) assert search.is_cancelled assert searches == [search] assert not search2.is_cancelled searches = ladder_service._cancel_existing_searches(p1) assert searches == [] searches = ladder_service._cancel_existing_searches(p2) assert search2.is_cancelled assert searches == [search2]
async def test_game_start_cancels_search(ladder_service: LadderService, player_factory, queue_factory, event_loop): ladder_service.queues["tmm2v2"] = queue_factory("tmm2v2") p1 = player_factory("Dostya", player_id=1, ladder_rating=(1000, 10), with_lobby_connection=True) p2 = player_factory("Brackman", player_id=2, ladder_rating=(1000, 10), with_lobby_connection=True) ladder_service.start_search([p1], "ladder1v1") ladder_service.start_search([p2], "ladder1v1") ladder_service.start_search([p1], "tmm2v2") ladder_service.start_search([p2], "tmm2v2") await exhaust_callbacks(event_loop) assert "ladder1v1" in ladder_service._searches[p1] assert "tmm2v2" in ladder_service._searches[p1] assert "ladder1v1" in ladder_service._searches[p2] assert "tmm2v2" in ladder_service._searches[p2] ladder_service.on_match_found(ladder_service._searches[p1]["ladder1v1"], ladder_service._searches[p2]["ladder1v1"], ladder_service.queues["ladder1v1"]) assert "ladder1v1" not in ladder_service._searches[p1] assert "tmm2v2" not in ladder_service._searches[p1] assert "ladder1v1" not in ladder_service._searches[p2] assert "tmm2v2" not in ladder_service._searches[p2]
async def test_start_and_cancel_search(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) p1.numGames = 0 search = Search([p1]) ladder_service.start_search(p1, search, 'ladder1v1') await asyncio.sleep(0) # Give the other coro a chance to run assert p1.state == PlayerState.SEARCHING_LADDER assert ladder_service.queues['ladder1v1'].queue[search] assert not search.is_cancelled ladder_service.cancel_search(p1) assert p1.state == PlayerState.IDLE assert search.is_cancelled
async def test_start_game_called_on_match(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (2300, 64) p1.numGames = 0 p2 = mock.create_autospec(Player('QAI', id=4)) p2.ladder_rating = (2350, 125) p2.numGames = 0 ladder_service.start_game = CoroMock() ladder_service.inform_player = mock.Mock() ladder_service.start_search(p1, Search([p1]), 'ladder1v1') ladder_service.start_search(p2, Search([p2]), 'ladder1v1') await asyncio.sleep(1) ladder_service.inform_player.assert_called() ladder_service.start_game.assert_called_once()
async def test_write_rating_progress_message(ladder_service: LadderService, player_factory): player = player_factory(ladder_rating=(1500, 500)) player.write_message = CoroutineMock() ladder_service.write_rating_progress(player, RatingType.LADDER_1V1) player.write_message.assert_called_once_with({ "command": "notice", "style": "info", "text": ("<i>Welcome to the matchmaker</i><br><br><b>Until " "you've played enough games for the system to learn " "your skill level, you'll be matched randomly.</b><br>" "Afterwards, you'll be more reliably matched up with " "people of your skill level: so don't worry if your " "first few games are uneven. This will improve as you " "play!</b>") })
async def test_search_info_message(ladder_service: LadderService, player_factory, queue_factory, event_loop): ladder_service.queues["tmm2v2"] = queue_factory("tmm2v2") p1 = player_factory("Dostya", player_id=1, ladder_rating=(1000, 10), with_lobby_connection=True) p1.write_message = CoroutineMock() p2 = player_factory("Rhiza", player_id=2, ladder_rating=(1000, 10), with_lobby_connection=True) p2.write_message = CoroutineMock() ladder_service.start_search([p1, p2], "ladder1v1") await exhaust_callbacks(event_loop) msg = { "command": "search_info", "queue_name": "ladder1v1", "state": "start" } p1.write_message.assert_called_once_with(msg) p2.write_message.assert_called_once_with(msg) p1.write_message.reset_mock() p2.write_message.reset_mock() ladder_service.start_search([p1, p2], "tmm2v2") await exhaust_callbacks(event_loop) msg = {"command": "search_info", "queue_name": "tmm2v2", "state": "start"} p1.write_message.assert_called_once_with(msg) p2.write_message.assert_called_once_with(msg) p1.write_message.reset_mock() p2.write_message.reset_mock() ladder_service.cancel_search(p1) await exhaust_callbacks(event_loop) call_args = [ mock.call({ "command": "search_info", "queue_name": "ladder1v1", "state": "stop" }), mock.call({ "command": "search_info", "queue_name": "tmm2v2", "state": "stop" }), ] assert p1.write_message.call_args_list == call_args assert p2.write_message.call_args_list == call_args
async def test_choose_map_all_maps_played(ladder_service: LadderService): ladder_service.get_ladder_history = CoroMock(return_value=[1, 2, 3]) ladder_service.game_service.ladder_maps = [ (1, "some_map", "maps/some_map.v001.zip"), (2, "some_map", "maps/some_map.v001.zip"), (3, "some_map", "maps/some_map.v001.zip"), ] chosen_map = await ladder_service.choose_map([None]) assert chosen_map is not None
async def test_write_rating_progress_other_rating( ladder_service: LadderService, player_factory ): player = player_factory( ladder_rating=(1500, 500), global_rating=(1500, 400.1235) ) player.write_message = CoroutineMock() # There's no reason we would call it with global, but the logic is the same # and global is an available rating that's not ladder ladder_service.write_rating_progress(player, RatingType.GLOBAL) player.write_message.assert_called_once_with({ "command": "notice", "style": "info", "text": ( "The system is still learning you.<b><br><br>" "The learning phase is 40% complete<b>" ) })
async def test_start_game_called_on_match(ladder_service: LadderService, player_factory): p1 = player_factory('Dostya', player_id=1, ladder_rating=(2300, 64), ladder_games=0, with_lobby_connection=True) p2 = player_factory('QAI', player_id=2, ladder_rating=(2350, 125), ladder_games=0, with_lobby_connection=True) ladder_service.start_game = CoroutineMock() ladder_service.inform_player = CoroutineMock() await ladder_service.start_search(p1, Search([p1]), 'ladder1v1') await ladder_service.start_search(p2, Search([p2]), 'ladder1v1') await asyncio.sleep(2) ladder_service.inform_player.assert_called() ladder_service.start_game.assert_called_once()
async def test_start_search_cancels_previous_search( ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) p1.numGames = 0 search1 = Search([p1]) ladder_service.start_search(p1, search1, 'ladder1v1') await asyncio.sleep(0) # Give the other coro a chance to run assert p1.state == PlayerState.SEARCHING_LADDER assert ladder_service.queues['ladder1v1'].queue[search1] search2 = Search([p1]) ladder_service.start_search(p1, search2, 'ladder1v1') await asyncio.sleep(0) # Give the other coro a chance to run assert p1.state == PlayerState.SEARCHING_LADDER assert search1.is_cancelled assert not ladder_service.queues['ladder1v1'].queue.get(search1) assert ladder_service.queues['ladder1v1'].queue[search2]
async def test_on_match_found_sets_player_state(ladder_service: LadderService, player_factory, event_loop): p1 = player_factory("Dostya", player_id=1, ladder_rating=(1000, 10), with_lobby_connection=True) p2 = player_factory("Brackman", player_id=2, ladder_rating=(1000, 10), with_lobby_connection=True) ladder_service.start_search([p1], "ladder1v1") ladder_service.start_search([p2], "ladder1v1") await exhaust_callbacks(event_loop) assert p1.state is PlayerState.SEARCHING_LADDER assert p2.state is PlayerState.SEARCHING_LADDER ladder_service.on_match_found(ladder_service._searches[p1]["ladder1v1"], ladder_service._searches[p2]["ladder1v1"], ladder_service.queues["ladder1v1"]) assert p1.state is PlayerState.STARTING_AUTOMATCH assert p2.state is PlayerState.STARTING_AUTOMATCH
async def test_choose_map(ladder_service: LadderService): ladder_service.get_ladder_history = CoroMock(return_value=[1, 2, 3]) ladder_service.game_service.ladder_maps = [ (1, "some_map", "maps/some_map.v001.zip"), (2, "some_map", "maps/some_map.v001.zip"), (3, "some_map", "maps/some_map.v001.zip"), (4, "CHOOSE_ME", "maps/choose_me.v001.zip"), ] chosen_map = await ladder_service.choose_map([None]) # Make the probability very low that the test passes because we got lucky for _ in range(20): assert chosen_map == (4, "CHOOSE_ME", "maps/choose_me.v001.zip")
def test_inform_player(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) ladder_service.inform_player(p1) # Message is sent after the first call p1.lobby_connection.sendJSON.assert_called_once() ladder_service.inform_player(p1) p1.lobby_connection.sendJSON.reset_mock() # But not after the second p1.lobby_connection.sendJSON.assert_not_called() ladder_service.on_connection_lost(p1) ladder_service.inform_player(p1) # But it is called if the player relogs p1.lobby_connection.sendJSON.assert_called_once()
async def test_write_rating_progress(ladder_service: LadderService, player_factory): p1 = player_factory("Dostya", player_id=1, ladder_rating=(1500, 500), with_lobby_connection=True) ladder_service.write_rating_progress(p1, RatingType.LADDER_1V1) # Message is sent after the first call p1.lobby_connection.write.assert_called_once() ladder_service.write_rating_progress(p1, RatingType.LADDER_1V1) p1.lobby_connection.write.reset_mock() # But not after the second p1.lobby_connection.write.assert_not_called() ladder_service.on_connection_lost(p1.lobby_connection) ladder_service.write_rating_progress(p1, RatingType.LADDER_1V1) # But it is called if the player relogs p1.lobby_connection.write.assert_called_once()
def ladder_service(game_service: GameService, game_stats_service: GameStatsService): return LadderService(game_service, game_stats_service)