def test_create_two_games(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid2, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(None, GameAction(m.REQGAMELIST))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertIsNone(game) self.assertEqual(action, m.GAMELIST) self.assertEqual(len(args), 1) # one JSON string records = map(lambda t: GameRecord(**t), json.loads(args[0])) self.assertEqual(len(records), 2) # two games record = records[0] self.assertEqual(record.game_id, 0) self.assertEqual(record.players, ['p1']) self.assertEqual(record.started, False) record = records[1] self.assertEqual(record.game_id, 1) self.assertEqual(record.players, ['p2']) self.assertEqual(record.started, False)
def test_join_game(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid2, Command(0, GameAction(m.REQJOINGAME))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid2) self.assertEqual(game, 0) self.assertEqual(action, m.JOINGAME) self.assertEqual(args, []) self.s.handle_command(self.uid2, Command(0, GameAction(m.REQGAMELIST))) user, game, action, args = self.get_response(-1) records = map(lambda t: GameRecord(**t), json.loads(args[0])) self.assertEqual(len(records), 1) record = records[0] self.assertEqual(record.game_id, 0) self.assertEqual(record.players, ['p1', 'p2']) self.assertEqual(record.started, False)
def test_handle_action(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQSTARTGAME))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertEqual(game, 0) self.assertEqual(action, m.GAMESTATE) self.assertEqual(len(args), 1) gs_dict = json.loads(args[0]) self.assertEqual(gs_dict['expected_action'], m.THINKERORLEAD) a = GameAction(m.THINKERORLEAD, True) self.s.handle_command(self.uid1, Command(0, a)) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertEqual(game, 0) self.assertEqual(action, m.GAMESTATE) self.assertEqual(len(args), 1) gs_dict = json.loads(args[0]) self.assertEqual(gs_dict['expected_action'], m.THINKERTYPE)
def test_start_game(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQSTARTGAME))) # REQSTARTGAME has two respones: STARTGAME then GAMESTATE user, game, action, args = self.get_response(-2) self.assertEqual(user, self.uid1) self.assertEqual(game, 0) self.assertEqual(action, m.STARTGAME) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertEqual(game, 0) self.assertEqual(action, m.GAMESTATE) self.assertEqual(len(args), 1) # GameState JSON gs_dict = json.loads(args[0]) # Ensure valid JSON self.s.handle_command(self.uid1, Command(0, GameAction(m.REQGAMELIST))) user, game, action, args = self.get_response(-1) records = map(lambda t: GameRecord(**t), json.loads(args[0])) self.assertEqual(len(records), 1) record = records[0] self.assertEqual(record.game_id, 0) self.assertEqual(record.players, ['p1']) self.assertEqual(record.started, True)
def test_start_game_not_joined(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid2, Command(0, GameAction(m.REQSTARTGAME))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid2) self.assertIsNone(game) self.assertEqual(action, m.SERVERERROR)
def test_join_game_twice(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQJOINGAME))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertIsNone(game) self.assertEqual(action, m.SERVERERROR)
def test_from_json_bad_action(self): """Raises ParsingError if the valid JSON doesn't represent a valid GameAction. """ with self.assertRaises(GameActionError): a = GameAction.from_json('{"action": -1, "args": [true]}') with self.assertRaises(GameActionError): a = GameAction.from_json('{"action": 0, "args": []}') with self.assertRaises(GameActionError): a = GameAction.from_json('{"action": 0, "args": [true, false]}')
def test_multiple_args_to_json(self): """Convert GameAction to JSON. """ a = GameAction(message.LEADROLE, 'Craftsman', 1, 106, 107, 108) a_json = a.to_json() d = json.loads(a_json) action, args = d['action'], d['args'] self.assertEqual(action, message.LEADROLE) self.assertEqual(args, ['Craftsman', 1, 106, 107, 108])
def test_from_bad_json(self): """Failure to convert bad JSON raises ValueError. """ a_json = '{"act]]]}' # not valid JSON with self.assertRaises(ParsingError): a = GameAction.from_json(a_json)
def test_handle_bad_action(self): """A bad action will send a SERVERERROR and then the GAMESTATE. """ self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQSTARTGAME))) a = GameAction(m.PATRONFROMDECK, True) self.s.handle_command(self.uid1, Command(0, a)) user, game, action, args = self.get_response(-2) self.assertEqual(user, self.uid1) self.assertEqual(action, m.SERVERERROR) self.assertIsNone(game)
def test_to_json(self): """Convert GameAction to JSON. """ a = GameAction(message.THINKERORLEAD, True) a_json = a.to_json() d = json.loads(a_json) self.assertIn('action', d.keys()) self.assertIn('args', d.keys()) action, args = d['action'], d['args'] self.assertEqual(action, message.THINKERORLEAD) self.assertEqual(args, [True])
def test_join_full_game(self): uids = [uuid4().int for i in range(5)] for i, uid in enumerate(uids): self.s.register_user(uid, {'name': 'p' + str(i + 1)}) self.s.handle_command(uids[0], Command(None, GameAction(m.REQCREATEGAME))) for uid in uids[1:]: self.s.handle_command(uid, Command(0, GameAction(m.REQJOINGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQJOINGAME))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertIsNone(game) self.assertEqual(action, m.SERVERERROR)
def test_gamestate(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQCREATEGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQSTARTGAME))) self.s.handle_command(self.uid1, Command(0, GameAction(m.REQGAMESTATE))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertEqual(game, 0) self.assertEqual(action, m.GAMESTATE) self.assertEqual(len(args), 1) gs_dict = json.loads(args[0]) self.assertEqual(gs_dict['turn_number'], 1)
def _send_gamestate(self, user, game): """Sends the game state from the specified game to the user as a GAMESTATE command. """ gs = self._get_game_state(user, game) gs_json = json.dumps(gs, sort_keys=True, default=lambda o: o.__dict__) resp = Command(game, GameAction(message.GAMESTATE, gs_json)) self.send_command(user, resp)
def test_from_json(self): """Convert JSON dictionary to GameAction. """ a_json = '{"action": 0, "args": [true]}' a = GameAction.from_json(a_json) self.assertEqual(a.action, message.THINKERORLEAD) self.assertEqual(a.args, [True])
def test_join_nonexistent_game(self): self.s.handle_command(self.uid2, Command(0, GameAction(m.REQJOINGAME))) user, game, action, args = self.get_response(0) self.assertEqual(user, self.uid2) self.assertIsNone(game) self.assertEqual(action, m.SERVERERROR)
def test_bad_input(self): d = self.deck with self.assertRaises(GameActionError): a = GameAction(message.THINKERORLEAD, None) self.game.handle(a) with self.assertRaises(GameActionError): a = GameAction(message.THINKERORLEAD, d.jack0) self.game.handle(a) with self.assertRaises(GameActionError): a = GameAction(message.THINKERORLEAD, 0) self.game.handle(a) with self.assertRaises(GameActionError): a = GameAction(message.THINKERORLEAD, 1) self.game.handle(a) with self.assertRaises(GameActionError): a = GameAction(message.THINKERORLEAD, Card(-1)) self.game.handle(a)
def test_gamelist(self): self.s.handle_command(self.uid1, Command(None, GameAction(m.REQGAMELIST))) user, game, action, args = self.get_response(0) self.assertEqual(user, self.uid1) self.assertIsNone(game) self.assertEqual(action, m.GAMELIST) self.assertEqual(len(args), 1) records = json.loads(args[0]) self.assertEqual(records, [])
def test_to_json(self): """Convert Command to JSON. """ a = GameAction(message.THINKERORLEAD, True) c = Command(1, a) c_json = c.to_json() d = json.loads(c_json) self.assertIn('action', d.keys()) self.assertIn('game', d.keys()) action, args, game = d['action']['action'], d['action']['args'], d[ 'game'] self.assertEqual(action, message.THINKERORLEAD) self.assertEqual(args, [True]) self.assertEqual(game, 1)
def test_handle_invalid_actions(self): """The server doens't handle GAMESTATE, CREATEGAME, etc. Those are exclusively server responses sent to the client. """ for game, action, args in [(None, m.CREATEGAME, []), (0, m.JOINGAME, []), (None, m.GAMESTATE, ['notagamestate']), (None, m.GAMELIST, ['notagamelist']), (None, m.LOGIN, ['0']), (0, m.STARTGAME, [])]: self.s.handle_command(self.uid1, Command(game, GameAction(action, *args))) user, game, action, args = self.get_response(-1) self.assertEqual(user, self.uid1) self.assertEqual(action, m.SERVERERROR) self.assertIsNone(game)
def handle_command(self, user, command): """Muliplexes the action to helper functions. """ game_id = command.game action = command.action.action args = command.action.args lg.debug('Handling command: {0!s}, {1!s}, {2!s}, {3!s}'.format( user, game_id, action, args)) if action == message.REQGAMESTATE: self._send_gamestate(user, game_id) elif action == message.REQGAMELIST: gl = self._get_game_list() json_list = json.dumps(gl, sort_keys=True, default=lambda o: o.__dict__) resp = Command(game_id, GameAction(message.GAMELIST, json_list)) self.send_command(user, resp) elif action == message.REQJOINGAME: # Game id is the argument here, not the game part of the request try: id_ = self._join_game(user, game_id) except GTRError as e: # I want to filter out the case where we're re-joining. Presumably # the user wants the game state and a join acknowledgement. # They can get this with a GAMESTATE request, though. self._send_error(user, e.message) else: resp = Command(id_, GameAction(message.JOINGAME)) self.send_command(user, resp) # If the game is started, we need the game state gs = self._get_game_state(user, id_) if gs is not None and self.games[id_].started: self._send_gamestate(user, id_) elif action == message.REQSTARTGAME: try: self._start_game(user, game_id) except GTRError as e: self._send_error(user, e.message) else: gs = self._get_game_state(user, game_id) for u in [p.uid for p in gs.players]: resp = Command(game_id, GameAction(message.STARTGAME)) self.send_command(u, resp) self._send_gamestate(u, game_id) elif action == message.REQCREATEGAME: game_id = self._create_game(user) resp = Command(game_id, GameAction(message.JOINGAME)) self.send_command(user, resp) elif action in (message.CREATEGAME, message.JOINGAME, message.GAMESTATE, message.GAMELIST, message.STARTGAME, message.LOGIN): # Todo: send error to client. # It would be better to check if the action is a GameAction # command and return an error otherwise self._send_error(user, 'Invalid server command: ' + str(command.action)) else: # Game commands try: game = self.games[game_id] except IndexError as e: msg = ("Couldn't find game {0:d} in {1!s}").format( game_id, self.games[:10]) lg.warning(msg) self._send_error(user, msg) name = self._userinfo(user)['name'] player_index = game.find_player_index(name) if player_index is None: msg = ('User {0} is not part of game {1:d}, players: {2!s}' ).format(name, game_id, [p.name for p in game.players]) lg.warning(msg) self._send_error(user, msg) lg.debug('Expected action: {0}'.format(str(game.expected_action))) lg.debug('Got action: {0}'.format(repr(action))) lg.debug('Handling action: {0}'.format(repr(action))) i_active_p = game.active_player_index if game.finished: self._send_error(user, 'Game {0} has finished.'.format(game_id)) if i_active_p == player_index: try: game.handle(command.action) except GTRError as e: lg.warning(e.message) self._send_error(user, e.message) except GameOver: lg.info('Game {0} has ended.'.format(game_id)) self._save_backup() else: msg = ('Received action for player {0!s}, ' 'but waiting on player {1!s}').format( player_index, i_active_p) lg.warning(msg) self._send_error(user, msg) for u in [p.uid for p in game.players]: gs = self._get_game_state(u, game_id) self._send_gamestate(u, game_id)
def _send_error(self, user, msg): resp = Command(None, GameAction(message.SERVERERROR, msg)) self.send_command(user, resp)