def test_getPlayers(self): """ Test players.getPlayers """ # setup the test data self.setUp() players = Players() vbar() print("Test players.getPlayers") vbar() vprint( "We collect the players from the DB structure, and compare with the" ) vprint("reference test data:") test_players = players.getPlayers() ref_players = refPlayers(True) # check that there are the same number of players on DB and reference lref = len(ref_players) ltest = len(test_players) result = (lref == ltest) vprint(" - there are " + str(ltest) + " players in the DB: " + str(result)) self.assertTrue(result) # check that the DB is the same as the reference for pp_ref in ref_players: result = (pp_ref in test_players) vprint(" " + pp_ref['nickname'] + ": " + str(result)) self.assertTrue(result) # end of the test self.teardown(players)
def test_updateTotalScore(self): """ Test players.updateTotalScore """ # setup the test data self.setUp() players = Players() # runs the test vbar() print("Test players.updateTotalScore") vbar() vprint( "We check that we can properly update the 'totalScore' with more points" ) vprint("(typically updating the 'totalScore' after a game ended).") # self.list_test_players(players.playersColl) pid = players.getPlayerID("Daisy")['playerID'] self.assertTrue(players.updateTotalScore(pid, 5)) self.assertEqual( players.playersColl.find_one({'nickname': "Daisy"})['totalScore'], 50) for pp in players.playersColl.find({}): vprint(" " + pp['nickname'] + " - totalScore: " + str(pp['totalScore'])) # end of the test self.teardown(players)
def test_deserialize(self): """ Test players.deserialize """ # setup the test data self.setUp() players = Players() # We copy in 'target' the reference players (as in 'refPlayersDict') # without the password target = {'__class__': 'SetPlayers', 'players': []} for pp in refPlayers_Dict(): target['players'].append({ 'playerID': pp['playerID'], 'nickname': pp['nickname'], 'passwordHash': pp['passwordHash'], 'totalScore': pp['totalScore'], 'gameID': pp['gameID'] }) players.deserialize(target) # runs the test vbar() print("Test players.deserialize") vbar() vprint( "We erase and rebuilt the DB thanks to the 'deserialize' method, and we" ) vprint( "then compare the 'serialized' image of this new DB with the target," ) vprint("which is:") vprint(target) result = players.serialize() self.assertTrue(playersDict_equality(target, result)) # end of the test self.teardown(players)
def test_getHash(self): """ Test players.getHash """ # setup the test data self.setUp() players = Players() vbar() print("Test players.getHash") vbar() vprint("We collect the hash of the players in the DB and compare with") vprint("the reference test data:") # test valid players for pp in refPlayers(): playerID = pp['playerID'] hash_ref = pp['passwordHash'] result = players.getHash(playerID) valid = (result['status'] == "ok") and (result['passwordHash'] == hash_ref) vprint(" " + pp['nickname'] + ": result = " + str(result)) self.assertTrue(valid) # test unknown player result = players.getHash(ObjectId()) valid = (result['status'] == "ko") and (result['reason'] == "unknown playerID") vprint(" Unknown player: result = " + str(result)) self.assertTrue(valid) # test invalid playerID result = players.getHash("invalid") valid = (result['status'] == "ko") and (result['reason'] == "invalid playerID") vprint(" Invalid playerID: result = " + str(result)) self.assertTrue(valid) # end of the test self.teardown(players)
def gameSetup(test_data_index): """ Initialize a game from test data: the 'test_data_index' (0 or 1) points at the data set to be used from 'test_utilities' for: - players - cardset We then use the reference 'set proposal history' (in 'test_utilities' in order to go through the game and compare the progress against reference data available from 'refSteps'. """ # Connection to the MongoDB server # read the players, register them and initiate a game. players = refPlayers(True) temp_players = Players() for pp in players: temp_players.register(pp['nickname'], pp['passwordHash']) partie = Game(players) # overwrite the gameID with the reference test data partie.gameID = ObjectId(refGameHeader_start()[test_data_index]['gameID']) # Overwrite the cardset with reference test data cards_dict = refGames_Dict()[test_data_index]['cardset'] partie.cards.deserialize(cards_dict) # Force 'Steps' to take into account this new cardset. partie.steps[0].start(partie.cards) # The game is ready to start. return partie
def test_getPlayerID(self): """ Test players.getPlayerID """ # setup the test data self.setUp() players = Players() vbar() print("Test players.getPlayerID") vbar() vprint( "We collect the playerID of the players in the DB and compare with" ) vprint("the reference test data:") # test valid players for pp in refPlayers(): playerID_ref = pp['playerID'] result = players.getPlayerID(pp['nickname']) valid = (result['status'] == "ok") and (result['playerID'] == playerID_ref) vprint(" " + pp['nickname'] + ": result = " + str(result)) self.assertTrue(valid) # test invalid players nickname = "inexistantplayer" result = players.getPlayerID(nickname) valid = (result['status'] == "ko") and (result['reason'] == "unknown nickname") vprint(" " + nickname + ": result = " + str(result)) self.assertTrue(valid) # end of the test self.teardown(players)
def __init__(self): """ Initiates the backend by creating game-persistent data """ # initiate the list of future games self.games = [] # initiates the players and waiting list self.players = Players() self.playersWaitingList = [] self.nextGameID = None
def test__init__(self): """ Test players.__init__ """ # setup the test data self.setUp() players = Players() vbar() vprint("Test data creation") vprint(" We create 6 players in the DB:") for pp in players.playersColl.find(): vprint(" " + pp['nickname']) vprint(" These will be used as test data for the whole test suite.") # self.list_test_players(players.playersColl) # check the number of players read from the database vbar() print("Test players.__init__") vbar() # replaced 'count' (deprecated) with 'count_documents' self.assertEqual(players.playersColl.count_documents({}), 6) # test if the db was properly read vprint( "We test that the __init__ will properly read 6 players from the DB, and" ) vprint("check few details.") pp = players.playersColl.find_one({'nickname': "Fifi"}) self.assertEqual(pp['totalScore'], 0) pp = players.playersColl.find_one({'nickname': "Zorro"}) self.assertEqual(pp, None) pp = players.playersColl.find_one({'totalScore': 45}) self.assertEqual(pp['nickname'], "Daisy") # end of the test self.teardown(players)
def reset(self): """ ths method is used for test purposes: it enables 'test_setserver' to reset the server's variables and ensure that all tests will run in a clean context. It is an alternative to endlessly stopping and restarting the server. """ # connect to the players collection, and empty it getPlayersColl().drop() # connect to the games collection and empty it getGamesColl().drop() # voluntarily, we do NOT empty the log collection # initialize again all generic variables self.games = [] self.players = Players() self.playersWaitingList = [] self.nextGameID = None # returns status update answer return {'status': "reset"}
def test_isNicknameAvailable(self): """ Test players.isNicknameAvailable """ # test that the new player is actually added both in memory and in DB vbar() print("Test players.isNicknameAvailable") vbar() vprint( "We register players and check that their nickname are not available anymore." ) # empty the database playersColl = getPlayersColl() playersColl.drop() # setup the test data players = Players() for pp in refPlayers(): # test that the nickname is available vprint(" > " + pp['nickname'] + ":") answer = players.isNicknameAvailable(pp['nickname']) self.assertEqual(answer['status'], "ok") vprint(" * nickname is available") # register the player and test that the nickname is not available anymore. players.register(pp['nickname'], pp['passwordHash']) vprint(" * register " + pp['nickname']) answer = players.isNicknameAvailable(pp['nickname']) self.assertEqual(answer['status'], "ko") vprint(" * nickname is not available anymore")
def test_getPlayer(self): """ Test players.getPlayer """ # setup the test data self.setUp() players = Players() vbar() print("Test players.getPlayer") vbar() vprint( "We collect several players from the DB structure, and compare with the" ) vprint("reference test data:") # collect Donald and Daisy and check results ref_players = refPlayers(True) idDonald = players.getPlayerID("Donald")['playerID'] print("BOGUS: ", idDonald) test_player = players.getPlayer(idDonald) vprint(" Donald: " + str(test_player)) self.assertEqual(test_player['status'], "ok") self.assertEqual(test_player['playerID'], ref_players[0]['playerID']) self.assertEqual(test_player['nickname'], ref_players[0]['nickname']) self.assertEqual(test_player['passwordHash'], ref_players[0]['passwordHash']) self.assertEqual(test_player['totalScore'], ref_players[0]['totalScore']) self.assertEqual(test_player['gameID'], ref_players[0]['gameID']) idDaisy = players.getPlayerID("Daisy")['playerID'] test_player = players.getPlayer(idDaisy) vprint(" Daisy: " + str(test_player)) self.assertEqual(test_player['status'], "ok") self.assertEqual(test_player['playerID'], ref_players[5]['playerID']) self.assertEqual(test_player['nickname'], ref_players[5]['nickname']) self.assertEqual(test_player['passwordHash'], ref_players[5]['passwordHash']) self.assertEqual(test_player['totalScore'], ref_players[5]['totalScore']) self.assertEqual(test_player['gameID'], ref_players[5]['gameID']) # try to get invalid and unknown playerIDs idUnknown = ObjectId() test_player = players.getPlayer(idUnknown) vprint(" Unkown playerID: " + str(test_player)) self.assertEqual(test_player['status'], "ko") self.assertEqual(test_player['reason'], "unknown playerID") idInvalid = "invalid" test_player = players.getPlayer(idInvalid) vprint(" Invalid playerID: " + str(test_player)) self.assertEqual(test_player['status'], "ko") self.assertEqual(test_player['reason'], "invalid playerID")
def test_register(self): """ Test players.register """ # test that the new player is actually added both in memory and in DB vbar() print("Test players.register") vbar() vprint( "We add a player and check that it is properly registered to the DB." ) vprint( "We also check that duplicate registering a player will not work.") # setup the test data self.setUp() players = Players() new_hash = "lEyycZ2UYZV0bX6ChdtSA5MGCmN3BrF1xoZG4TMRzEmwmpp" playerID = players.register("Dingo", new_hash)['playerID'] read_id = players.playersColl.find_one({'nickname': "Dingo"})['_id'] self.assertEqual(playerID, read_id) # replaced 'count' (deprecated) with 'count_documents' self.assertEqual(players.playersColl.count_documents({}), 7) # check the various fields of registered players pp = players.playersColl.find_one({'nickname': "Dingo"}) self.assertEqual(pp['_id'], playerID) self.assertEqual(pp['passwordHash'], new_hash) # check that it is impossible to register a duplicate nickname result = players.register("Daisy", new_hash) self.assertEqual(result['status'], "ko") self.assertEqual(result['reason'], "invalid nickname") # replaced 'count' (deprecated) with 'count_documents' self.assertEqual(players.playersColl.count_documents({}), 7) # summarize vprint(" We now have 7 players in the DB:") for pp in players.playersColl.find(): vprint(" " + pp['nickname']) # end of the test self.teardown(players)
def test_deRegister(self): """ Test players.deRegister """ # setup the test data self.setUp() players = Players() # removes a player vbar() print("Test players.deRegister") vbar() vprint("We first register all 6 reference test players.") pp = players.playersColl.find_one({'nickname': "Donald"}) self.assertTrue(players.deRegister(pp['_id'])) # replaced 'count' (deprecated) with 'count_documents' self.assertEqual(players.playersColl.count_documents({}), 5) pp = players.playersColl.find_one({'nickname': "Donald"}) self.assertEqual(pp, None) vprint("We removed Donald and check that we have 5 players left.") for pp in players.playersColl.find(): vprint(" " + pp['nickname']) # end of the test self.teardown(players)
def test_changeHash(self): """ Test players.changeHash """ # setup the test data vbar() print("Test players.changeHash") vbar() vprint( "We change the hash of the players in the DB and compare the result with" ) vprint("the expected test data:") # test valid players self.setUp() players = Players() new_hash = "lEyycZ2UYZV0bX6ChdtSA5MGCmN3BrF1xoZG4TMRzEmwmpp" for pp in refPlayers(): playerID = pp['playerID'] result = players.changeHash(playerID, new_hash) valid = (result['status'] == "ok") and (result['passwordHash'] == new_hash) vprint(" " + pp['nickname'] + ": result = " + str(result)) self.assertTrue(valid) # test unknown player result = players.getHash(ObjectId()) valid = (result['status'] == "ko") and (result['reason'] == "unknown playerID") vprint(" Unknown player: result = " + str(result)) self.assertTrue(valid) # test invalid playerID result = players.getHash("invalid") valid = (result['status'] == "ko") and (result['reason'] == "invalid playerID") vprint(" Invalid playerID: result = " + str(result)) self.assertTrue(valid) # end of the test self.teardown(players)
class Backend(): """ This class 'runs' the backend, i.e. the business logics to power the web server described in 'setserver.py'. Its methods enabled to: - welcome the front requests for games and players (one called by the web server) - initiate as per need new games (calling on the Game class) Typically: - it starts a new Game, with a unique gameID, when the players are identified (per team or via a waiting list for isolated players) - it starts a new game when conditions are met - it can stop a game and store it when instructed """ def __init__(self): """ Initiates the backend by creating game-persistent data """ # initiate the list of future games self.games = [] # initiates the players and waiting list self.players = Players() self.playersWaitingList = [] self.nextGameID = None def reset(self): """ ths method is used for test purposes: it enables 'test_setserver' to reset the server's variables and ensure that all tests will run in a clean context. It is an alternative to endlessly stopping and restarting the server. """ # connect to the players collection, and empty it getPlayersColl().drop() # connect to the games collection and empty it getGamesColl().drop() # voluntarily, we do NOT empty the log collection # initialize again all generic variables self.games = [] self.players = Players() self.playersWaitingList = [] self.nextGameID = None # returns status update answer return {'status': "reset"} def isNicknameAvailable(self, nickname): """ This method checks if a nickname is still available so that a new player could register with this nickname. NB: there is no possible 'reservation' for a nickname, and they are assigned on a "first ask first served" basis. The client need to request if a nickname is available: it will push a "nickname" to the server who will - if available - return a positive answer: - post: { "nickname": "str" } answer: { "playerID": str(ObjectId) } The client need to parse the returned message to read the (stringified) playerID. """ answer = self.players.isNicknameAvailable(nickname) if answer['status'] == "ok": result = {'status': "ok", 'nickname': answer['nickname']} else: result = {'status': "ko", 'reason': answer['reason']} return result def getPlayerLoginDetails(self, nickname): """ This method returns the details enabling a local client to log-in a player. To date (v100), the required details are a nickname, a playerID and a password hash. The result can be: - if successful: { 'result': "ok", 'nickname': str, 'playerID': str(ObjectId), 'passwordHash': str } - if not successful: { 'result': "ko", 'reason': str } """ answer = self.players.getPlayerID(nickname) if answer['status'] == "ok": playerID = answer['playerID'] passwordHash = self.players.getHash(playerID)['passwordHash'] result = { 'status': "ok", 'nickname': nickname, 'playerID': str(playerID), 'passwordHash': passwordHash } else: result = {'status': "ko", 'reason': answer['reason']} return result def registerPlayer(self, nickname, passwordHash): """ This method registers new players so that they can play and connect to a game with their nickname (must be unique) or playerID and then enlist onto a game. The players are persistent: they are stored on the db, although the game will manipulate 'in memory' objects as long as it runs. The client need to request the creation of a player: it will push a "nickname" to the server who will - if successful - return a player ID: { 'status': "ok", 'playerID': "str(ObjectId) } or { 'status': "ko", 'reason': error_msg } The client need to parse the returned message to read the (stringified) playerID. """ # It will try to register a new player via 'players.addPlayer'. # - If the answer is True, it returns the playerID # - if the answer is False, it returns 'Failed'. answer = self.players.register(nickname, passwordHash) if answer['status'] == "ok": answer['playerID'] = str(answer['playerID']) return answer def deRegisterPlayer(self, playerID): """ This method de-registers a player so that we remove it from the database. It will also remove the player from any game it would be part of. If returns: { 'status': "ok" } or { 'status': "ko", 'reason': error_msg } """ # remove the player from all the games for gg in self.games: gg.delistPlayer(playerID) # remove the player from the database. return self.players.deRegister(playerID) def enlistPlayer(self, playerID): """ This method enlists one player on a "new-game-yet-to-start". The client places a GET with its 'playerID' and receives 3 possible answers: - the player is set on the waiting list for the next game which will be started. The returned message is 'wait' - the game is starting, and the server indicates the corresponding 'gameID'. - the player is not available (because it is already part of another game): the answer is the 'gameID' of the game the player is being part of. This is the nominal way for a client to check the gameID for an active player. - the player is not recognized (because its 'playerID' is not in the DB or is invalid): the answer is 'invalid' The information needed to start a game is a list of 'players' dictionaries as: { 'playerID': ObjectId, 'nickname': string } The list is assumed to be filled with 'valid' player's ID and we check that there are enough players to start a game (and is so, actually start it). """ # We assume here that the client will push a value named 'playerID' # which is a valid player's ID, or will go through the form 'enlist_tpl' # check if the playerID is valid if isPlayerIDValid(playerID): # check if the player is available to take part into a new game if self.players.isPlayerAvailableToPlay(playerID)['status'] == "ok": # check if the playerID is in the waiting list if (playerID in self.playersWaitingList): # the player already enlisted but the game is not yet # starting (waiting for more players to enlist) result = {'status': "wait", 'nb_players': len(self.playersWaitingList)} else: self.playersWaitingList.append(playerID) # check if the minimum number of players has been reached if len(self.playersWaitingList) < playersMin: # not enough player: stay in wait mode result = {'status': "wait", 'nb_players': len(self.playersWaitingList)} else: # there are enough player: we start a new game # build the players list for the new game game_players = [] for pID in self.playersWaitingList: game_players.append({ 'playerID': pID, 'nickname': self.players.getNickname(pID)['nickname'], 'passwordHash': self.players.getHash(pID)['passwordHash'] }) # initiate the new game game = Game(game_players) gameID = game.getGameID() self.games.append(game) # registers the players in the DB for pID in self.playersWaitingList: self.players.enlist(pID, gameID) # empty the waiting list self.playersWaitingList = [] # returns a 'gameID' result result = {'status': "ok", 'gameID': gameID} else: # the player is already part of a game and cannot enlist. # the server indicates which game (i.e. gameID) the player # is part of. result = {'status': "ok", 'gameID': self.players.getGameID(playerID)['gameID']} else: # the playerID does not exist: result = {'status': "ko", 'reason': "unknown playerID"} # in any case, the server returns 'result' return result def enlistTeam(self, list_playerID): """ This function enable to enlist multiple players in one time into the same game. The list must contain at least 'playersMin' players (usually 4 players) and at max 'playersMax' players (usually 6 players). The list contains dictionaries like: { 'playerID': ObjectId } The list is assumed to be filled with 'valid' player's ID and we check that: - the number of players of correct (from 4 to 6 usually), - the IDs are all unique in the list. """ # remove the duplicates pID_list = [] for pp in list_playerID: pID_list.append(pp['playerID']) pID_list = list(set(pID_list)) list_playerID = [] for pID in pID_list: list_playerID.append({'playerID': pID}) # check the playerID : should be registered and be available to play j = len(list_playerID)-1 while j >= 0: pID = list_playerID[j]['playerID'] if isPlayerIDValid(pID): if self.players.isPlayerAvailableToPlay(pID)['status'] == "ko": del(list_playerID[j]) else: del(list_playerID[j]) j -= 1 # check the 'real' number of players l = len(list_playerID) if (l >= playersMin) and (l <= playersMax): # the players's list is ok (number ok and all are valid, unique and # available) game_players = [] for pp in list_playerID: pID = pp['playerID'] game_players.append({'playerID': pID, 'nickname': self.players.getNickname(pID)['nickname'], 'passwordHash': self.players.getHash(pID)['passwordHash'], 'points': 0}) #initiate a game game = Game(game_players) gameID = game.getGameID() self.games.append(game) # registers the players in the DB for pp in list_playerID: pID = pp['playerID'] self.players.enlist(pID, gameID) # returns the game info return {'status': "ok", 'gameID': gameID} else: return {'status': "ko"} def delistPlayer(self, playerID): """ This function enable to delist a player from the database and also from the game he might be part of. The returned result can be: { 'status': "ok" } or {'status': "ko", 'reason': message } """ # retrieve the player's detail answer = self.players.getPlayer(playerID) if answer['status'] == "ok": # remove the player from all possible games for game in self.games: game.delistPlayer(playerID) # delist the player from the players list result = self.players.delist(playerID) else: result = answer return result def getGameID(self, playerID): """ This method returns the gameID if the player exist and is part of a game. Possible answers are: {'status': "ok", 'gameID': ObjectId } or: {'status': "ko", 'reason': "unknown playerID"} {'status': "ko", 'reason': "invalid playerID"} """ if oidIsValid(playerID): result = self.players.getGameID(playerID) else: result = {'status': "ko", 'reason': "invalid playerID"} return result def getNicknames(self, playerID): """ This function gives back the names of all the players which are part of the same game as the player who identifies itself via his playerID. NB: on purpose, the playerIDs of the other players are not shared openly, and only nicknames are shared. """ # I should find a way to catch errors in case the playerID/gameID are # not valid ObjectId. list_names = [] if isPlayerIDValid(playerID): gameID = self.players.getGameID(playerID)['gameID'] if gameID != None: list_pID = self.players.inGame(gameID)['list'] for pID in list_pID: nickname = self.players.getNickname(pID)['nickname'] list_names.append({'nickname': nickname}) return list_names def stopGame(self, gameID, hard = False): """ This function stops a game and de-registers the players who were part of this game. - hard == True: it will kill the game irrespective of its status. - hard == False: it first checks that the game is finished. """ # check that gameID is valid and the corresponding game exists if oidIsValid(gameID): good_game = None for i in range(0, len(self.games)): if self.games[i].getGameID() == gameID: good_game = self.games[i] break if good_game == None: # gameID is valid but there is no corresponding game result = {'status': "ko", 'reason': "game does not exist"} else: if self.games[i].getGameFinished() or hard: # gameID is valid and correspond to the game 'good_game' # kill the game and delist the corresponding players del(self.games[i]) self.players.delistGame(gameID) result = {'status': "ok"} else: # gameID is ok, but the game is not finished and the 'hard' # flag is not set result = {'status': "ko", 'reason': "game not finished"} else: # gameID is not a valid ObjectId result = {'status': "ko", 'reason': "invalid GameID"} # end of the 'stop' method return result def getDetails(self,gameID): """ The server will answer the clients when they ask about the generic details of the game: cardset, turncounter... It will answer with the data description (JSON): { 'status': "ok", 'gameID': str(ObjectId), 'gameFinished': str(gameFinished), 'cards': cardset.serialize, 'turnCounter': str(turncounter), 'players': list of {'playerID': str(playerID), 'nickname': nickname, 'passwordHash': passwordHash, 'points': str(points) } If the request is not ok, it will return the dictionary: { 'status': "ko", 'reason': msg } """ if oidIsValid(gameID): good_game = None for gg in self.games: if gg.gameID == gameID: good_game = gg break if good_game != None: result = {'status': "ok", 'gameID': str(gameID), 'turnCounter': str(good_game.turnCounter), 'gameFinished': str(good_game.gameFinished), 'cardset': good_game.cards.serialize()} # add the players (local vision from within the game) result["players"] = [] for pp in good_game.players: result["players"].append( { 'playerID': str(pp['playerID']), 'nickname': pp['nickname'], 'passwordHash': pp['passwordHash'], 'points': str(pp['points'])}) else: result = {'status': "ko", 'reason': "unknown gameID"} else: result = {'status': "ko", 'reason': "invalid gameID"} return result def getTurnCounter(self,gameID): """ The server will return the turncounter of the game if it exist. It will answer with the data description (JSON): { 'status': "ok", 'turnCounter': str(turncounter) } If the request is not ok, it will return the dictionary: { 'status': "ko", 'reason': msg } """ answer = self.getDetails(gameID) if answer['status'] == "ok": result = { 'status': "ok", 'turnCounter': answer['turnCounter'] } else: result = answer return result def getGameFinished(self, gameID): """ The server will return the gameFinished flag of the game if it exist. It will answer with the data description (JSON): { 'status': "ok", 'turnCounter': str(gameFinished) } If the request is not ok, it will return the dictionary: { 'status': "ko", 'reason': msg } """ answer = self.getDetails(gameID) if answer['status'] == "ok": result = { 'status': "ok", 'gameFinished': answer['gameFinished'] } else: result = answer return result def getStep(self,gameID): """ The server will answer the clients when they ask about the status of the game: - if the request is successful, it returns: { 'status': "ok", 'step': step.serialize } - if gameID is not a valid ObjectId, it return: {'status': "ko", 'reason': "invalid gameID" } - if gameID is a valid ObjectId but the game does not exist, it returns: {'status': "ko", 'reason': "game does not exist"} """ # check that the gameID is a valid ID if oidIsValid(gameID): # check if the gameID exist good_game = None for gg in self.games: if str(gg.gameID) == str(gameID): good_game = gg break if good_game != None: # gameID is valid and the game exist step = good_game.steps[good_game.turnCounter] result = {'status': "ok", 'step': step.serialize()} else: result = {'status': "ko", 'reason': "game does not exist"} else: result = {'status': "ko", 'reason': "invalid gameID"} return result def getHistory(self,gameID): """ The server will answer the clients when they ask about the full history of a game (active or finished): it will answer with the full description (JSON): - collect the full details of the game: GET url/game/<gameid>/history/ answer { serialized Game } """ # check that the gameID is a valid ID if oidIsValid(gameID): # check if the gameID exist good_game = None for gg in self.games: if str(gg.gameID) == str(gameID): good_game = gg break if good_game != None: # gameID is valid and the game exist result = {'status': "ok", 'game': good_game.serialize()} else: result = {'status': "ko", 'reason': "game does not exist"} else: result = {'status': "ko", 'reason': "invalid gameID"} return result def proposeSet(self, playerID, setlist): """ The method collects a Set proposal to be checked and played: - playerID (ObjectId) indicates the player - setlist ([int0, int1, int2] indicates the positions of the 3 cards on the table for the current step. If the setlist is valid, it is played and the game continues: - the 3 cards are moved to the 'used' - 3 new cards are taken from the 'pick' and put on the 'table' - turnCounter and points are incremented... the method returns: - if PlayerID is an invalid ObjectId: { 'status': "ko", 'reason': "invalid playerID"} - else if playerID is valid but the player does not exist: { 'status': "ko", 'reason': "unknown player" } - else if PlayerID is valid but the setlist syntax is invalid: { 'status': "ko", 'reason': "invalid set" } - else if the playerID is valid, the setlist syntax is valid but does not form a valid set of 3 cards: { 'status': "ko", 'reason': "wrong set" } - else the setlist is valid: { 'status': "ok" } """ def setSyntax(setlist): """ Check that the syntax of the proposed set is ok: - list of integers (not sure we can test this efficiently """ valid = (type(setlist) == list) valid = valid and (len(setlist) == 3) if valid: for i in range(0,3): valid = valid and (type(setlist[i]) == int) if valid: for i in range(0,3): valid = valid and (setlist[i] >= 0) and (setlist[i] < tableMax) valid = valid and (setlist[i] != setlist[(i+1)%3]) return valid if oidIsValid(playerID): #check if playerID exist if isPlayerIDValid(playerID): # check if the set syntax is valid (3 integers between 0 and 11) if setSyntax(setlist): # find the game gameID = self.players.getGameID(playerID)['gameID'] if gameID != None: good_game = None for gg in self.games: if (str(gg.getGameID()) == str(gameID)): good_game = gg break if good_game != None: # push the set to the game valid = good_game.receiveSetProposal(playerID, setlist) if valid: # the set is valid and was already processed result = {'status': "ok"} else: result = {'status': "ko", 'reason': "wrong set"} else: # this case should never happen, unless the DB is # corrupted and playerID are enlisted to wrong games result = {'status': "ko", 'reason': "player not in game"} else: # the player is not enlisted: this should never happen # unless the DB is corrupted. result = {'status': "ko", 'reason': "player not in game"} else: result = {'status': "ko", 'reason': "invalid set"} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result def ForTestOnly_RegisterRefPlayers(self): """ FOR TEST PURPOSE ONLY. This method register 6 reference test players. """ # connects straight to the Mongo database playersColl = getPlayersColl() playersColl.drop() # now register the reference players straight to the DB (bypassing the # normal process = call to the setserver 'register' API) for pp in refPlayers(True): playersColl.insert_one( { '_id': pp['playerID'], 'nickname': pp['nickname'], 'passwordHash': pp['passwordHash'], 'totalScore': 0, 'gameID': None } ) return {'status': "ok"} def ForTestOnly_EnlistRefPlayers(self): """ FOR TEST PURPOSE ONLY. This method enlist 6 reference test players. It assumes that these reference players were already registered. """ # enlist a team of 6 players (in which 1 duplicate): it should succeed list_pid = [] for pp in refPlayers(True): list_pid.append({'playerID': pp['playerID']}) result = self.enlistTeam(list_pid) return result def ForTestOnly_DelistAllPlayers(self): """ FOR TEST PURPOSE ONLY. This method delist all players from any on-going game. If used unwisely, this may induce inconsistency within the server (especially since this will not stop the on-going games, but only delist the players). It returns the number of players which were actually delisted from a game. """ # delist all the players by overwriting the gameID field with 'None'. modified = self.playersColl.update_many({}, {'$set': {'gameID': None}}) return modified.modified_count def ForTestOnly_LoadRefGame(self, test_data_index): """ FOR TEST PURPOSE ONLY. This method initiate the reference test game corresponding to the index passed as argument. The game is fully played and is finished. """ # cleans the DB and registers the reference players if test_data_index in (0,1): # initiate a new game and overwrite it with reference test data self.reset() self.ForTestOnly_RegisterRefPlayers() # initialize a game (just to have a game object available self.games.append(Game(refPlayers(True))) # override this game with the reference test data self.games[0].deserialize(refGames_Dict()[test_data_index]) gID = self.games[0].getGameID() for pp in self.players.getPlayers(): self.players.delist(pp['playerID']) self.players.enlist(pp['playerID'], gID) result = {'status': "ok", 'gameID': gID} else: result = {'status': "ko", 'reason': "wrong test_data_index"} return result def ForTestOnly_GetBackToTurn(self, test_data_index, target_turn): """ FOR TEST PURPOSE ONLY. This method enable to roll the reference played loaded with previous method back to the turn N. """ # rewind the game back to turn 'target_turn' # we know - since the backend was reseted, that the new game is # backend.game[0] => we set the games index i at 0 i = 0 nb_turn_max = self.games[i].turnCounter target_turn = min(target_turn, nb_turn_max) target_turn = max(0, target_turn) original_turn = self.games[i].turnCounter if (target_turn < original_turn): refGames = refGames_Dict()[test_data_index] # adapts the generic details self.games[i].gameFinished = False self.games[i].turnCounter = target_turn # removes the 'future' steps j = original_turn while j > target_turn: del(self.games[i].steps[j]) j -= 1 # resets the 'playerID', nickname' and 'set' to empty if the game is # not finished self.games[i].steps[target_turn].playerID = None self.games[i].steps[target_turn].nickname = "" self.games[i].steps[target_turn].set = [] # set the player's points as from the reference test data # The only way to do so is actually to replay the game and add points to # the players accordingly. for pp in self.games[0].players: pp['points'] = 0 for j in range(0,target_turn): pID_str = refGames['steps'][j]['playerID'] for pp in self.games[0].players: if str(pp['playerID']) == pID_str: pp['points'] += pointsPerStep result = {'status': "ok"} else: result = {'status': "ko"} return result
def test_isPlayerAvailableToPlay(self): """ Test players.isPlayerAvailableToPlay """ vbar() print("Test players.isPlayerAvailableToPlay") vbar() vprint( "We check which players are available to play and compare with the" ) vprint("reference data :") # setup the test data self.setUp() players = Players() id_Donald = players.getPlayerID("Donald")['playerID'] print("bogus: id_Donald =", id_Donald) id_Mickey = players.getPlayerID("Mickey")['playerID'] id_Riri = players.getPlayerID("Riri")['playerID'] id_Fifi = players.getPlayerID("Fifi")['playerID'] id_Loulou = players.getPlayerID("Loulou")['playerID'] id_Daisy = players.getPlayerID("Daisy")['playerID'] gameID1 = ObjectId() gameID2 = ObjectId() # first round of test players.enlist(id_Riri, gameID2) players.enlist(id_Fifi, gameID2) players.enlist(id_Loulou, gameID2) vprint(" > First round, only kids are playing:") result = players.isPlayerAvailableToPlay(id_Donald) print("Bogus: ", result) vprint(" Donald: " + str(result)) self.assertTrue(result['status'] == "ok") result = players.isPlayerAvailableToPlay(id_Mickey) vprint(" Mickey: " + str(result)) self.assertTrue(result['status'] == "ok") result = players.isPlayerAvailableToPlay(id_Riri) vprint(" Riri : " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Fifi) vprint(" Fifi : " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Loulou) vprint(" Loulou: " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Daisy) vprint(" Daisy : " + str(result)) self.assertTrue(result['status'] == "ok") # second round of test players.enlist(id_Daisy, gameID1) players.enlist(id_Donald, gameID1) vprint(" > Second round, two parents are also playing:") result = players.isPlayerAvailableToPlay(id_Donald) vprint(" Donald: " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Mickey) vprint(" Mickey: " + str(result)) self.assertTrue(result['status'] == "ok") result = players.isPlayerAvailableToPlay(id_Riri) vprint(" Riri : " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Fifi) vprint(" Fifi : " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Loulou) vprint(" Loulou: " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") result = players.isPlayerAvailableToPlay(id_Daisy) vprint(" Daisy : " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "player is not available") # test inexistant or invalid playerID vprint(" > Third round, with invalid or unknown playerIDs:") result = players.isPlayerAvailableToPlay(ObjectId()) vprint(" Unknown player: " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "unknown playerID") result = players.isPlayerAvailableToPlay("stupid") vprint(" Invalid playerID: " + str(result)) self.assertTrue(result['status'] == "ko" and result['reason'] == "invalid playerID") # end of the test self.teardown(players)
def test_serialize(self): """ Test players.serialize """ # build the reference data (without the passwrds) target = {'__class__': 'SetPlayers', 'players': []} for pp in refPlayers_Dict(): target['players'].append({ 'playerID': pp['playerID'], 'nickname': pp['nickname'], 'passwordHash': pp['passwordHash'], 'totalScore': pp['totalScore'], 'gameID': pp['gameID'] }) # setup the test data self.setUp() players = Players() gameID1 = ObjectId('57bf224df9a2f36dd206845a') gameID2 = ObjectId('57bf224df9a2f36dd206845b') players.enlist(players.getPlayerID("Daisy")['playerID'], gameID1) players.enlist(players.getPlayerID("Donald")['playerID'], gameID1) players.enlist(players.getPlayerID("Riri")['playerID'], gameID2) players.enlist(players.getPlayerID("Fifi")['playerID'], gameID2) players.enlist(players.getPlayerID("Loulou")['playerID'], gameID2) # runs the test vbar() print("Test players.serialize") vbar() vprint( "We compare the result of the 'serialize' method with the target which is:" ) vprint(target) # check that the class is equal result = players.serialize() self.assertTrue(playersDict_equality(target, result)) # end of the test self.teardown(players)
def test_inGame(self): """ Test players.inGame """ # setup the test data self.setUp() players = Players() gameID1 = ObjectId() gameID2 = ObjectId() players.enlist(players.getPlayerID("Daisy")['playerID'], gameID1) players.enlist(players.getPlayerID("Donald")['playerID'], gameID1) players.enlist(players.getPlayerID("Riri")['playerID'], gameID2) players.enlist(players.getPlayerID("Fifi")['playerID'], gameID2) players.enlist(players.getPlayerID("Loulou")['playerID'], gameID2) vbar() print("Test players.inGame") vbar() vprint( "We gather a list of the players being part of the fist game and check" ) vprint("against the reference data :") list_pid1 = players.inGame(gameID1)['list'] list_pid2 = players.inGame(gameID2)['list'] self.assertTrue(players.getPlayerID("Donald")['playerID'] in list_pid1) self.assertTrue(players.getPlayerID("Daisy")['playerID'] in list_pid1) self.assertEqual(len(list_pid1), 2) vprint(" > GameID 1:") for pid in list_pid1: name = players.getNickname(pid)['nickname'] vprint(" " + name + " (" + str(pid) + ")") self.assertTrue(players.getPlayerID("Riri")['playerID'] in list_pid2) self.assertTrue(players.getPlayerID("Fifi")['playerID'] in list_pid2) self.assertTrue(players.getPlayerID("Loulou")['playerID'] in list_pid2) self.assertEqual(len(list_pid2), 3) vprint(" > GameID 2:") for pid in list_pid2: name = players.getNickname(pid)['nickname'] vprint(" " + name + " (" + str(pid) + ")") # end of the test self.teardown(players)
def test_delistGame(self): """ Test players.delistGame """ # setup the test data self.setUp() players = Players() ref_players = [] for pp in refPlayers(): ref_players.append(player_format_DB(pp)) gameID1 = ref_players[0]['gameID'] gameID2 = ref_players[2]['gameID'] players.enlist(players.getPlayerID("Daisy")['playerID'], gameID1) players.enlist(players.getPlayerID("Donald")['playerID'], gameID1) players.enlist(players.getPlayerID("Riri")['playerID'], gameID2) players.enlist(players.getPlayerID("Fifi")['playerID'], gameID2) players.enlist(players.getPlayerID("Loulou")['playerID'], gameID2) # will delist few players vbar() print("Test players.delistGame") vbar() vprint("Test registering several players on two games:") vprint(" - Riri, Fifi and Loulou are part of a first game.") vprint(" - Daisy and Donald are part of another game.") vprint(" - Mickey does not play a game.") vprint(" Here are the players after we deregister the second game:") gid = players.getGameID( players.getPlayerID("Riri")['playerID'])['gameID'] players.delistGame(gid) riri_gid = players.getGameID( players.getPlayerID("Riri")['playerID'])['gameID'] fifi_gid = players.getGameID( players.getPlayerID("Fifi")['playerID'])['gameID'] loulou_gid = players.getGameID( players.getPlayerID("Loulou")['playerID'])['gameID'] self.assertTrue(riri_gid == fifi_gid == loulou_gid == None) for pp in players.playersColl.find({}): vprint(" " + pp['nickname'] + " - gameID: " + str(pp['gameID'])) # end of the test self.teardown(players)
def test_delistPlayer(self): """ Test players.delistPlayer """ # setup the test data self.setUp() players = Players() ref_players = [] for pp in refPlayers(): ref_players.append(player_format_DB(pp)) gameID1 = ref_players[0]['gameID'] gameID2 = ref_players[2]['gameID'] players.enlist(players.getPlayerID("Daisy")['playerID'], gameID1) players.enlist(players.getPlayerID("Donald")['playerID'], gameID1) players.enlist(players.getPlayerID("Riri")['playerID'], gameID2) players.enlist(players.getPlayerID("Fifi")['playerID'], gameID2) players.enlist(players.getPlayerID("Loulou")['playerID'], gameID2) donald = players.playersColl.find_one({'nickname': "Donald"}) riri = players.playersColl.find_one({'nickname': "Riri"}) fifi = players.playersColl.find_one({'nickname': "Fifi"}) loulou = players.playersColl.find_one({'nickname': "Loulou"}) daisy = players.playersColl.find_one({'nickname': "Daisy"}) # will deregister few players vbar() print("Test players.delistPlayer") vbar() vprint("Test registering several players on two games:") vprint(" - Riri, Fifi and Loulou are part of a first game.") vprint(" - Daisy and Donald are part of another game.") vprint(" - Mickey does not play a game.") vprint(" Here are the players after we deregister them:") DonaldID = players.getPlayerID("Donald")['playerID'] players.delist(DonaldID) DaisyID = players.getPlayerID("Daisy")['playerID'] players.delist(DaisyID) donald_gid = players.getGameID(donald['_id'])['gameID'] daisy_gid = players.getGameID(daisy['_id'])['gameID'] self.assertTrue(donald_gid == daisy_gid == None) players.delist(players.getPlayerID("Riri")['playerID']) players.delist(players.getPlayerID("Fifi")['playerID']) players.delist(players.getPlayerID("Loulou")['playerID']) riri_gid = players.getGameID(riri['_id'])['gameID'] fifi_gid = players.getGameID(fifi['_id'])['gameID'] loulou_gid = players.getGameID(loulou['_id'])['gameID'] self.assertTrue(riri_gid == fifi_gid == loulou_gid == None) for pp in players.playersColl.find({}): vprint(" " + pp['nickname'] + " - gameID: " + str(pp['gameID'])) # end of the test self.teardown(players)
def test_enlist(self): """ Test players.enlist """ # setup the test data self.setUp() players = Players() ref_players = [] for pp in refPlayers(): ref_players.append(player_format_DB(pp)) gameID1 = ref_players[0]['gameID'] gameID2 = ref_players[2]['gameID'] print("Bogus: ", ref_players[0]) print("Bogus: ", ref_players[2]) # modifies few gameID values vbar() print("Test players.enlist") vbar() vprint("Test registering several players on two games:") vprint(" - Riri, Fifi and Loulou are part of a first game.") vprint(" - Daisy and Donald are part of another game.") vprint(" - Mickey does not play a game.") vprint(" Here are the players:") playerID = players.getPlayerID("Daisy")['playerID'] result = players.enlist(playerID, gameID1) self.assertEqual(result['status'], "ok") playerID = players.getPlayerID("Donald")['playerID'] result = players.enlist(playerID, gameID1) self.assertEqual(result['status'], "ok") playerID = players.getPlayerID("Riri")['playerID'] result = players.enlist(playerID, gameID2) self.assertEqual(result['status'], "ok") playerID = players.getPlayerID("Fifi")['playerID'] result = players.enlist(playerID, gameID2) self.assertEqual(result['status'], "ok") playerID = players.getPlayerID("Loulou")['playerID'] result = players.enlist(playerID, gameID2) self.assertEqual(result['status'], "ok") # self.list_test_players(players.playersColl) pp = [] for p in players.playersColl.find({'gameID': gameID1}): pp.append(p) result = (ref_players[0] in pp) and (ref_players[5] in pp) and (len(pp) == 2) self.assertTrue(result) pp = [] for p in players.playersColl.find({'gameID': gameID2}): pp.append(p) result = (ref_players[2] in pp) and (ref_players[3] in pp) and ( ref_players[4] in pp) and (len(pp) == 3) self.assertTrue(result) for pp in players.playersColl.find({}): vprint(" " + pp['nickname'] + " - gameID: " + str(pp['gameID'])) # end of the test self.teardown(players)