def test_oidIsValid(self): """ This method will test that the reference playerID and gameID are recognized as valid ObjectIds, and that other strings are not. """ vbar() print("Test constants.oidIsValid") vbar() # check that playerIDs are tested ok (in both formats) vprint( "We check that playerIDs for all reference test players are recognized:" ) for pp in refPlayers(): self.assertTrue(oidIsValid(pp['playerID'])) vprint(" > " + pp['nickname'] + "'s playerID is recognized ok.") for pp in refPlayers_Dict(): self.assertTrue(oidIsValid(pp['playerID'])) vprint(" > " + pp['nickname'] + "'s stringified playerID is recognized ok.") vprint("We check that the reference game's GameID are recognized:") for i in range(0, 2): gameID = refGames_Dict()[i]['gameID'] self.assertTrue(oidIsValid(gameID)) vprint(" > game " + str(i) + " stringified gameID is recognized ok.") # We now test that random strings are not recognized as valid ObjectIds. vprint("We test that 'false' ObjectIds are not recognized:") wrong_list = ["REzcozienz34", "d*zdojEZFFE", "#`{#]^rdgrgre"] for pp in wrong_list: self.assertFalse(oidIsValid(pp)) vprint(" > " + pp + " is not recognized")
def stopGame(gameid_str): # it needs (amongst other things) to read the 'hard' flag. if oidIsValid(gameid_str): result = backend.stopGame(ObjectId(gameid_str), True) 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 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 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 getGameID(playerid_str): if oidIsValid(playerid_str): result = backend.getGameID(ObjectId(playerid_str)) if result['status'] == "ok": result['gameID'] = str(result['gameID']) else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def getNicknames(playerid_str): # check that the string passed is a valid ObjectId, and if so # call the backend. if oidIsValid(playerid_str): playerID = ObjectId(playerid_str) result = {'status': "ok", 'nicknames': backend.getNicknames(playerID)} else: result = {'status': "ko"} return result
def enlistPlayer(playerid_str): # check that the string passed is a valid ObjectId, and if so # call the backend. if oidIsValid(playerid_str): result = backend.enlistPlayer(ObjectId(playerid_str)) if result['status'] == "ok": gameid_str = str(result['gameID']) result = {'status': "ok", 'gameID': gameid_str} else: result = {'status': "ko"} return result
def enlist(self, playerID, gameID): """ This method receives two ObjectId. If they are valid playerID and gameID, it will store this gameID in the players record, and return this gameID: the player is enlist on the game. If it is not possible (for instance, the player is already part of a game), it will return None. """ if oidIsValid(playerID) and oidIsValid(gameID): pp = self.playersColl.find_one_and_update( {'_id': playerID, 'gameID': None}, {'$set': {'gameID': gameID}}, return_document=ReturnDocument.AFTER) if pp != None: result = {'status': "ok", 'gameID': gameID} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def enlistTeam(): pid_list = [] result = request.query.getall('playerIDlist') # check that the strings passed are valid ObjectId, and if so # add them into the list of players to be enlisted. for playerid_str in result: if oidIsValid(playerid_str): pid_list.append({'playerID': ObjectId(playerid_str)}) result2 = backend.enlistTeam(pid_list) if result2['status'] == "ok": gameid_str = str(result2['gameID']) result2 = {'status': "ok", 'gameID': gameid_str} return result2
def deRegister(self, playerID): """ This method check that the playerID exists, and if so removes it from both the memory and the DB. Returns True if the playerID was successfully removed from the DB. """ if oidIsValid(playerID): if self.playersColl.find_one_and_delete({'_id': playerID}) != None: result = {'status': "ok"} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def getNickname(self, playerID): """ This method return the nickname of the player. We assume that the playerID is a valid ObjectId. """ if oidIsValid(playerID): pp = self.playersColl.find_one({'_id': playerID}) if pp != None: result = {'status': "ok", 'nickname': pp['nickname']} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def proposeSet(playerid_str): if oidIsValid(playerid_str): playerID = ObjectId(playerid_str) set_dict = request.query.getall('set') set_list = [] for s in set_dict: try: set_list.append(int(s)) except: result = {'status': "ko", 'reason': "invalid set"} result = backend.proposeSet(playerID, set_list) else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def getHash(self, playerID): """ This method enable a client to retrieve the password hash, so that it will locally check the password entered by the player to log into the client. """ if oidIsValid(playerID): pp = self.playersColl.find_one({'_id': playerID}) if pp != None: result = {'status': "ok", 'passwordHash': pp['passwordHash']} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def delist(self, playerID): """ This method de-enlist the player from any game he would be part of (i.e. it overwrites the gameID with 'None'). It return: - the former gameID in case of success (which may be None if the player was not already part of a game) - None in other cases """ result = None if oidIsValid(playerID): pp = self.playersColl.update_one({'_id': playerID}, {'$set': {'gameID': None}}) result = (pp.modified_count == 1) return result
def getGameID(self, playerID): """ This method returns: - the gameID if the player exist and is part of a game, - None if the playerID is invalid, or does not exist in the DB, or is not attending a game. """ if oidIsValid(playerID): pp = self.playersColl.find_one({'_id': playerID}) if pp != None: result = {'status': "ok", 'gameID': pp['gameID']} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def delistGame(self, gameID): """ This method de-enlists all the players playing the game identified by its gameID (i.e. it overwrites the gameID with 'None'). The argument 'gameID' is assumed to be a valid ObjectId. It will return: - the number of modified players if successful - None in case of problem (gameID is invalid or does not exist in the DB). """ result = None if oidIsValid(gameID): modified = self.playersColl.update_many({'gameID':gameID}, {"$set": {'gameID': None}}) result = modified.modified_count return result
def changeHash(self, playerID, newHash): """ This method enable a remote client to update the hash in case the player need to change its password. """ if oidIsValid(playerID): pp = self.playersColl.find_one({'_id': playerID}) if pp != None: self.playersColl.update_one({'_id': playerID}, {'$set': {'passwordHash': newHash}}) result = {'status': "ok", 'passwordHash': newHash} else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} 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 getGameFinished(gameid_str): # check that the string passed is a valid ObjectId, and if so # call the backend. if oidIsValid(gameid_str): gameID = ObjectId(gameid_str) answer = backend.getGameFinished(gameID) if answer['status'] == "ok": result = { 'status': "ok", 'gameFinished': str(answer['gameFinished']) } else: result = answer else: result = {'status': "ko", 'reason': "invalid gameID"} return result
def isPlayerAvailableToPlay(self, playerID): """ This method checks that the playerID is valid (ie. it is a valid ObjectId and the corresponding player exists in the DB) and that the player is not already part of a game (i.e. his 'gameID' in the DB is 'None'). """ if oidIsValid(playerID): pp = self.playersColl.find_one({'_id': playerID}) if pp == None: result = {'status': "ko", 'reason': "unknown playerID"} else: if pp['gameID'] == None: result = {'status': "ok"} else: result = {'status': "ko", 'reason': "player is not available"} else: result = {'status': "ko", 'reason': "invalid playerID"} 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 inGame(self, gameID): """ This method returns a list of player's playerIDs (ObjectID) who are part of the game identified by gameID. It return: - None if the gameID is invalid - an empty list if the gameID is valid but does not appear in the DB - a list of playerIDs (ObjectId) of all players part of the game if gameID exists in the DB """ result = None if oidIsValid(gameID): list_pid = [] for pp in self.playersColl.find({'gameID': gameID}): list_pid.append(pp['_id']) if (len(pp) == 0): result = {'status': "ko", 'reason': "unknown gameID"} else: result = {'status': "ok", 'list': list_pid} else: result = {'status': "ko", 'reason': "invalid gameID"} return result
def getPlayer(self, playerID): """ If playerID is valid, this method return a dictionary with all player's details (except its password hash): { 'playerID': ObjectId, 'nickname': string, 'totalScore': int, 'gameID': ObjectId } Else it will return 'None'. """ if oidIsValid(playerID): pp_db= self.playersColl.find_one({'_id': playerID}) if pp_db != None: result = { 'status': "ok", 'playerID': pp_db['_id'], 'nickname': pp_db['nickname'], 'passwordHash': pp_db['passwordHash'], 'totalScore': pp_db['totalScore'], 'gameID': pp_db['gameID'] } else: result = {'status': "ko", 'reason': "unknown playerID"} else: result = {'status': "ko", 'reason': "invalid playerID"} return result
def getHistory(gameid_str): if oidIsValid(gameid_str): result = backend.getHistory(ObjectId(gameid_str)) 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 deRegisterPlayer(playerid_str): if oidIsValid(playerid_str): result = backend.deRegisterPlayer(ObjectId(playerid_str)) else: result = {'status': "ko", 'reason': "invalid playerID"} return result