From 23ef34e4cff09973ff4463b5272193b3acd1dc1d Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:48:05 +0100 Subject: [PATCH 01/11] replace "status" with "error" and "msg" in error codes --- tests/test_api.py | 20 ++++++++++---------- truthseeker/routes/routes_api.py | 20 +++++++++----------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 52b6546..6b23d13 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -27,8 +27,8 @@ def createGame(user:User): if content is None: print("content is none") raise Exception("Response is null") - if content["status"] != "ok": - print(content["status"]) + if content["error"] != 0: + print(content["msg"]) raise Exception("Status is not ok") user.isAdmin = True return content["game_id"] @@ -43,8 +43,8 @@ def joinGame(user:User,game_id:str): content = responseObject.json if content is None: raise Exception("Response is null") - if content["status"] != "ok": - print(content["status"]) + if content["error"] != 0: + print(content["msg"]) raise Exception("Status is not ok") return True @@ -56,8 +56,8 @@ def startGame(user:User): content = responseObject.json if content is None: raise Exception("Response is null") - if content["status"] != "ok": - print(content["status"]) + if content["error"] != 0: + print(content["msg"]) raise Exception("Status is not ok") return True @@ -96,7 +96,7 @@ def test_that_two_person_having_the_same_pseudo_creating_two_games_results_in_tw def test_that_not_sending_a_username_results_in_an_error(): responseObject = test_app.post("/api/v1/createGame") assert responseObject.status_code == 200 - assert responseObject.json["status"] != "ok" + assert responseObject.json["error"] != 0 def test_that_sending_a_empty_username_results_in_an_error(): @@ -144,20 +144,20 @@ def test_that_people_joining_without_sending_any_data_results_in_an_error(): game_id = createGame(User("neoxyde")) responseObject = test_app.post("/api/v1/joinGame") assert responseObject.status_code == 200 - assert responseObject.json["status"] != "ok" + assert responseObject.json["error"] != 0 def test_that_people_joining_without_sending_a_game_id_results_in_an_error(): data={"username":"neomblic"} responseObject = test_app.post("/api/v1/joinGame",data=data) assert responseObject.status_code == 200 - assert responseObject.json["status"] != "ok" + assert responseObject.json["error"] != 0 def test_that_people_joining_without_sending_an_username_still_results_in_an_error(): game_id = createGame(User("neonyx")) data={"game_id":game_id} responseObject = test_app.post("/api/v1/joinGame",data=data) assert responseObject.status_code == 200 - assert responseObject.json["status"] != "ok" + assert responseObject.json["error"] != 0 def test_that_people_joining_with_an_empty_username_still_results_in_an_error(): game_id = createGame(User("neodeur")) diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index ae0a416..647b3a1 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -10,11 +10,11 @@ routes_api = flask.Blueprint("api", __name__) def create_game(): username = flask.request.values.get("username") if username==None: - return {"status": "error, username not set"} + return {"error": 1, "msg": "username not set"} response = {} - response["status"] = "ok" + response["error"] = 0 game = game_logic.create_game(owner=username) response["game_id"] = game.game_id @@ -29,11 +29,11 @@ def join_game(): game_id = flask.request.values.get("game_id") username = flask.request.values.get("username") if game_id==None or username==None: - return {"status": "error, username or game id not set"} + return {"error": 1, "msg": "username or game id not set"} game = game_logic.get_game(game_id) if game == None: - return {"status": "error, game does not exist"} + return {"error": 1, "msg": "game does not exist"} game.add_member(username) @@ -42,17 +42,15 @@ def join_game(): flask.session["is_owner"] = False flask.session["username"] = username - response = {} - response["status"] = "ok" - return response + return {"error": 0} @routes_api.route("/startGame", methods=["GET", "POST"]) def start_game(): if not flask.session: - return {"status": "No session"} + return {"error": 1, "msg": "No session"} if not flask.session["is_owner"]: - return {"status": "Error, you are not the owner of this game"} + return {"error": 1, "msg": "you are not the owner of this game"} if game_logic.get_game(flask.session["game_id"]) == None: - return {"status": "Error, this game doesn't exist"} + return {"error": 1, "msg": "this game doesn't exist"} - return {"status": "ok"} + return {"error": 0} From 56a77b31c4e108390db65fdb9005ee8627e70f06 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:00:36 +0100 Subject: [PATCH 02/11] remove prints in tests --- tests/test_api.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 6b23d13..b57f196 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -21,15 +21,12 @@ def createGame(user:User): data = {"username":user.username} responseObject = test_app.post("/api/v1/createGame",data=data) if responseObject.status_code != 200: - print("status code is not 200") raise Exception("status code is not 200") content = responseObject.json if content is None: - print("content is none") raise Exception("Response is null") if content["error"] != 0: - print(content["msg"]) - raise Exception("Status is not ok") + raise Exception("backend returned an error: "+content["msg"]) user.isAdmin = True return content["game_id"] @@ -38,27 +35,23 @@ def joinGame(user:User,game_id:str): data = {"username":user.username,"game_id":game_id} responseObject = test_app.post("/api/v1/joinGame",data=data) if responseObject.status_code != 200: - print("status code is not 200") raise Exception("status code is not 200") content = responseObject.json if content is None: raise Exception("Response is null") if content["error"] != 0: - print(content["msg"]) - raise Exception("Status is not ok") + raise Exception("backend returned an error: "+content["msg"]) return True def startGame(user:User): responseObject = test_app.post("/api/v1/startGame") if responseObject.status_code != 200: - print("status code is not 200") raise Exception("status code is not 200") content = responseObject.json if content is None: raise Exception("Response is null") if content["error"] != 0: - print(content["msg"]) - raise Exception("Status is not ok") + raise Exception("backend returned an error: "+content["msg"]) return True From 45dface44b8749a0dd3ef2477a580a272e26b73a Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:03:04 +0100 Subject: [PATCH 03/11] check if username is valid --- truthseeker/routes/routes_api.py | 6 +++++- truthseeker/utils.py | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 truthseeker/utils.py diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index 647b3a1..def7d41 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -2,6 +2,7 @@ import flask import truthseeker from truthseeker.logic import game_logic +from truthseeker.utils import check_username routes_api = flask.Blueprint("api", __name__) @@ -11,7 +12,8 @@ def create_game(): username = flask.request.values.get("username") if username==None: return {"error": 1, "msg": "username not set"} - + if not check_username(username): + return {"error": 1, "msg": "invalid username"} response = {} response["error"] = 0 @@ -30,6 +32,8 @@ def join_game(): username = flask.request.values.get("username") if game_id==None or username==None: return {"error": 1, "msg": "username or game id not set"} + if not check_username(username): + return {"error": 1, "msg": "invalid username"} game = game_logic.get_game(game_id) if game == None: diff --git a/truthseeker/utils.py b/truthseeker/utils.py new file mode 100644 index 0000000..7cce76e --- /dev/null +++ b/truthseeker/utils.py @@ -0,0 +1,9 @@ +def check_username(username): + if not username: + return False + if not username == username.strip(): + return False + if not len(username) < 16: + return False + + return True From a926206db862328f72952f87e9f9d46fa8423361 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:03:57 +0100 Subject: [PATCH 04/11] update tests to detect exceptions --- tests/test_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index b57f196..efb3404 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -97,15 +97,15 @@ def test_that_sending_a_empty_username_results_in_an_error(): with pytest.raises(Exception) as e: createGame(user) - assert "Status is not ok" in str(e.value) - def test_that_a_too_long_username_results_in_an_error(): user = User("Le test unitaire est un moyen de vérifier qu’un extrait de code fonctionne correctement. C’est l’une des procédures mises en oeuvre dans le cadre d’une méthodologie de travail agile. ") - assert createGame(user) == None + with pytest.raises(Exception) as e: + createGame(user) def test_that_username_that_contains_non_alphanumerics_results_in_an_error(): user = User("я русский пират") - assert createGame(user) == None + with pytest.raises(Exception) as e: + createGame(user) ############################################################################### # # From f8783885f607fce8ed46a31b19af343a7b82251e Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:05:31 +0100 Subject: [PATCH 05/11] use a custom error class in tests --- tests/test_api.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index efb3404..6dcb596 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -4,6 +4,14 @@ from truthseeker import app test_app = app.test_client() +class TestException(Exception): + + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + ############################################################################### # # # # @@ -21,12 +29,12 @@ def createGame(user:User): data = {"username":user.username} responseObject = test_app.post("/api/v1/createGame",data=data) if responseObject.status_code != 200: - raise Exception("status code is not 200") + raise TestException("status code is not 200") content = responseObject.json if content is None: - raise Exception("Response is null") + raise TestException("Response is null") if content["error"] != 0: - raise Exception("backend returned an error: "+content["msg"]) + raise TestException("backend returned an error: "+content["msg"]) user.isAdmin = True return content["game_id"] @@ -35,23 +43,23 @@ def joinGame(user:User,game_id:str): data = {"username":user.username,"game_id":game_id} responseObject = test_app.post("/api/v1/joinGame",data=data) if responseObject.status_code != 200: - raise Exception("status code is not 200") + raise TestException("status code is not 200") content = responseObject.json if content is None: - raise Exception("Response is null") + raise TestException("Response is null") if content["error"] != 0: - raise Exception("backend returned an error: "+content["msg"]) + raise TestException("backend returned an error: "+content["msg"]) return True def startGame(user:User): responseObject = test_app.post("/api/v1/startGame") if responseObject.status_code != 200: - raise Exception("status code is not 200") + raise TestException("status code is not 200") content = responseObject.json if content is None: - raise Exception("Response is null") + raise TestException("Response is null") if content["error"] != 0: - raise Exception("backend returned an error: "+content["msg"]) + raise TestException("backend returned an error: "+content["msg"]) return True @@ -94,17 +102,17 @@ def test_that_not_sending_a_username_results_in_an_error(): def test_that_sending_a_empty_username_results_in_an_error(): user = User("") - with pytest.raises(Exception) as e: + with pytest.raises(TestException) as e: createGame(user) def test_that_a_too_long_username_results_in_an_error(): user = User("Le test unitaire est un moyen de vérifier qu’un extrait de code fonctionne correctement. C’est l’une des procédures mises en oeuvre dans le cadre d’une méthodologie de travail agile. ") - with pytest.raises(Exception) as e: + with pytest.raises(TestException) as e: createGame(user) def test_that_username_that_contains_non_alphanumerics_results_in_an_error(): user = User("я русский пират") - with pytest.raises(Exception) as e: + with pytest.raises(TestException) as e: createGame(user) ############################################################################### @@ -188,14 +196,14 @@ def test_that_people_can_start_a_game(): def test_that_a_started_game_cannot_be_started_again(): - with pytest.raises(Exception) as e: + with pytest.raises(TestException) as e: owner = User("neosteopathie") game_id = createGame(owner) startGame(owner) assert "Status is not ok" in str(e.value) def test_that_non_owners_cant_start_a_game(): - with pytest.raises(Exception) as e: + with pytest.raises(TestException) as e: owner = User("neosteopathie") notOwner = User("neorphelin") game_id = createGame(owner) From 39377d8c9be4745188d137ca7b8826845986d91b Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:07:34 +0100 Subject: [PATCH 06/11] check if username is alphanumeric --- truthseeker/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/truthseeker/utils.py b/truthseeker/utils.py index 7cce76e..048dd29 100644 --- a/truthseeker/utils.py +++ b/truthseeker/utils.py @@ -1,6 +1,8 @@ def check_username(username): if not username: return False + if not username.isalnum(): + return False if not username == username.strip(): return False if not len(username) < 16: From 824190348d5a14052c7d8660522bf34080a0889e Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:10:45 +0100 Subject: [PATCH 07/11] check if username is already added to the game --- truthseeker/logic/game_logic.py | 7 +++++++ truthseeker/routes/routes_api.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/truthseeker/logic/game_logic.py b/truthseeker/logic/game_logic.py index b4aeaf7..e0ed3a1 100644 --- a/truthseeker/logic/game_logic.py +++ b/truthseeker/logic/game_logic.py @@ -56,7 +56,14 @@ class Game: self.members.append(self.owner) return self.owner + def get_member(self, username): + for member in self.members: + if member.username == username: + return member + def add_member(self, username): + if self.get_member(username): + return None member = Member(username) self.members.append(member) return member diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index def7d41..76180d4 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -39,8 +39,8 @@ def join_game(): if game == None: return {"error": 1, "msg": "game does not exist"} - - game.add_member(username) + if not game.add_member(username): + return {"error": 1, "msg": f"Username '{username}' already used in game {game.game_id}"} flask.session["game_id"] = game.game_id flask.session["is_owner"] = False From 9829f137434f3b44bfbaacb5216e75806ede0c49 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:32:39 +0100 Subject: [PATCH 08/11] replace more asserts with exception checks in tests --- tests/test_api.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 6dcb596..6500e37 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -139,7 +139,8 @@ def test_that_two_person_can_join_a_game(): def test_that_people_cant_join_if_the_username_is_already_used(): game_id = createGame(User("neoreille")) joinGame(User("neosomse"),game_id) - assert joinGame(User("neosomse"),game_id) == False + with pytest.raises(TestException) as e: + joinGame(User("neosomse"),game_id) def test_that_people_joining_without_sending_any_data_results_in_an_error(): game_id = createGame(User("neoxyde")) @@ -163,17 +164,23 @@ def test_that_people_joining_without_sending_an_username_still_results_in_an_err def test_that_people_joining_with_an_empty_username_still_results_in_an_error(): game_id = createGame(User("neodeur")) user = User("") - assert joinGame(user,game_id) == False + + with pytest.raises(TestException) as e: + joinGame(user,game_id) def test_that_people_joining_aving_an_username_that_contains_non_alphanumerics_still_results_in_an_error(): game_id = createGame(User("neobservateur")) user = User("Я брат русского пирата") - assert joinGame(user,game_id) == False + + with pytest.raises(TestException) as e: + joinGame(user,game_id) def test_that_people_joining_aving_a_too_long_username_still_results_in_an_error(): game_id = createGame(User("neordre")) user = User("Les tests unitaires sont généralement effectués pendant la phase de développement des applications mobiles ou logicielles. Ces tests sont normalement effectués par les développeurs, bien qu’à toutes fins pratiques, ils puissent également être effectués par les responsables en assurance QA.") - assert joinGame(user,game_id) == False + + with pytest.raises(TestException) as e: + joinGame(user,game_id) ############################################################################### @@ -192,21 +199,18 @@ def test_that_people_joining_aving_a_too_long_username_still_results_in_an_error def test_that_people_can_start_a_game(): owner = User("neAUBERGINE") game_id = createGame(owner) - assert startGame(owner) == True - + startGame(owner) def test_that_a_started_game_cannot_be_started_again(): + owner = User("neosteopathie") + game_id = createGame(owner) with pytest.raises(TestException) as e: - owner = User("neosteopathie") - game_id = createGame(owner) startGame(owner) - assert "Status is not ok" in str(e.value) def test_that_non_owners_cant_start_a_game(): - with pytest.raises(TestException) as e: - owner = User("neosteopathie") - notOwner = User("neorphelin") - game_id = createGame(owner) - joinGame(notOwner,game_id) - assert startGame(notOwner) == False - assert "Status is not ok" in str(e.value) + owner = User("neosteopathie") + notOwner = User("neorphelin") + game_id = createGame(owner) + joinGame(notOwner,game_id) + with pytest.raises(TestException) as e: + startGame(notOwner) From 285ca1171bab025c1cf89f0d493e5f95ab332473 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:34:20 +0100 Subject: [PATCH 09/11] do not collect exception class --- tests/test_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_api.py b/tests/test_api.py index 6500e37..fe47ab8 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -5,6 +5,7 @@ from truthseeker import app test_app = app.test_client() class TestException(Exception): + __test__ = False def __init__(self, message): self.message = message From 2f29aa48bcb011703c59696fe8d3fd3a2842bc5b Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:37:07 +0100 Subject: [PATCH 10/11] prevent start from starting twice --- truthseeker/logic/game_logic.py | 1 + truthseeker/routes/routes_api.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/truthseeker/logic/game_logic.py b/truthseeker/logic/game_logic.py index e0ed3a1..c443bf3 100644 --- a/truthseeker/logic/game_logic.py +++ b/truthseeker/logic/game_logic.py @@ -50,6 +50,7 @@ class Game: self.game_id = None self.owner = None self.members = [] + self.has_started = True def set_owner(self, username): self.owner = Member(username) diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index 76180d4..5a1f3ee 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -54,7 +54,16 @@ def start_game(): return {"error": 1, "msg": "No session"} if not flask.session["is_owner"]: return {"error": 1, "msg": "you are not the owner of this game"} - if game_logic.get_game(flask.session["game_id"]) == None: + + game = game_logic.get_game(flask.session["game_id"]) + + if game == None: return {"error": 1, "msg": "this game doesn't exist"} + if game.has_started: + return {"error": 1, "msg": "this game is already started"} + + game.has_started = None + + return {"error": 0} From 222a119a2100bfd7e703718423d1161f40b3b62d Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:38:24 +0100 Subject: [PATCH 11/11] fix test that game cannot be started twice --- tests/test_api.py | 1 + truthseeker/logic/game_logic.py | 2 +- truthseeker/routes/routes_api.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index fe47ab8..4235864 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -205,6 +205,7 @@ def test_that_people_can_start_a_game(): def test_that_a_started_game_cannot_be_started_again(): owner = User("neosteopathie") game_id = createGame(owner) + startGame(owner) with pytest.raises(TestException) as e: startGame(owner) diff --git a/truthseeker/logic/game_logic.py b/truthseeker/logic/game_logic.py index c443bf3..b46f2f4 100644 --- a/truthseeker/logic/game_logic.py +++ b/truthseeker/logic/game_logic.py @@ -50,7 +50,7 @@ class Game: self.game_id = None self.owner = None self.members = [] - self.has_started = True + self.has_started = False def set_owner(self, username): self.owner = Member(username) diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index 5a1f3ee..c18b6e1 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -59,10 +59,11 @@ def start_game(): if game == None: return {"error": 1, "msg": "this game doesn't exist"} + print(game.has_started) if game.has_started: return {"error": 1, "msg": "this game is already started"} - game.has_started = None + game.has_started = True