From 73ce1bf2e03cfb394fe99de95cb83b3354fb6fa1 Mon Sep 17 00:00:00 2001 From: lvrossem Date: Mon, 10 Apr 2023 16:07:25 -0600 Subject: [PATCH] Finish off backend tests --- src/crud/courseprogress.py | 6 +- src/crud/highscores.py | 9 +- src/main.py | 13 +- tests/base.py | 16 +++ tests/test_authentication.py | 13 +- tests/test_courseprogress.py | 42 +++++-- tests/test_highscores.py | 230 +++++++++++++++++++++++++++++++++-- tests/test_users.py | 13 +- tests/usertests.py | 15 --- 9 files changed, 285 insertions(+), 72 deletions(-) create mode 100644 tests/base.py delete mode 100644 tests/usertests.py diff --git a/src/crud/courseprogress.py b/src/crud/courseprogress.py index 3000804..67a488c 100644 --- a/src/crud/courseprogress.py +++ b/src/crud/courseprogress.py @@ -1,5 +1,5 @@ -from sqlalchemy.orm import Session from fastapi import HTTPException +from sqlalchemy.orm import Session from src.enums import CourseEnum from src.models import CourseProgress, User @@ -50,7 +50,9 @@ def initialize_user(db: Session, user: User): db.commit() -def patch_course_progress(db: Session, user: User, course: CourseEnum, course_progress: CourseProgressBase): +def patch_course_progress( + db: Session, user: User, course: CourseEnum, course_progress: CourseProgressBase +): """Change the progress value for a given course""" if course_progress.progress_value > 1 or course_progress.progress_value < 0: raise HTTPException(status_code=400, detail="Invalid progress value") diff --git a/src/crud/highscores.py b/src/crud/highscores.py index ffb2961..a7eee7b 100644 --- a/src/crud/highscores.py +++ b/src/crud/highscores.py @@ -12,8 +12,9 @@ DEFAULT_NR_HIGH_SCORES = 10 def get_high_scores(db: Session, minigame: MinigameEnum, nr_highest: int): """Get the n highest scores of a given minigame""" - if nr_highest < 1: - raise HTTPException(status_code=400, detail="Invalid number of high scores") + if nr_highest: + if nr_highest < 1: + raise HTTPException(status_code=400, detail="Invalid number of high scores") user_high_scores = [] @@ -42,7 +43,9 @@ def get_high_scores(db: Session, minigame: MinigameEnum, nr_highest: int): return user_high_scores -def create_high_score(db: Session, user: User, minigame: MinigameEnum, high_score: HighScoreBase): +def create_high_score( + db: Session, user: User, minigame: MinigameEnum, high_score: HighScoreBase +): """Create a new high score for a given minigame""" def add_to_db(): diff --git a/src/main.py b/src/main.py index 93cbd01..08f6e6f 100644 --- a/src/main.py +++ b/src/main.py @@ -65,16 +65,17 @@ async def login(user: users.UserCreate, db: Session = Depends(get_db)): async def get_high_scores( minigame: MinigameEnum, nr_highest: Optional[int] = None, + current_user_name: str = Depends(crud_authentication.get_current_user_name), db: Session = Depends(get_db), ): return crud_highscores.get_high_scores(db, minigame, nr_highest) -@app.post("/highscores/{minigame}", response_model=highscores.HighScore) +@app.put("/highscores/{minigame}", response_model=highscores.HighScore) async def create_high_score( minigame: MinigameEnum, high_score: highscores.HighScoreBase, - current_user_name=Depends(crud_authentication.get_current_user_name), + current_user_name: str = Depends(crud_authentication.get_current_user_name), db: Session = Depends(get_db), ): current_user = crud_users.get_user_by_username(db, current_user_name) @@ -93,7 +94,9 @@ async def get_course_progress( return crud_courseprogress.get_course_progress(db, current_user, course) -@app.patch("/courseprogress/{course}", response_model=List[courseprogress.CourseProgressParent]) +@app.patch( + "/courseprogress/{course}", response_model=List[courseprogress.CourseProgressParent] +) async def patch_course_progress( course: CourseEnum, course_progress: courseprogress.CourseProgressBase, @@ -101,7 +104,9 @@ async def patch_course_progress( db: Session = Depends(get_db), ): current_user = crud_users.get_user_by_username(db, current_user_name) - return crud_courseprogress.patch_course_progress(db, current_user, course, course_progress) + return crud_courseprogress.patch_course_progress( + db, current_user, course, course_progress + ) #### TESTING!! DELETE LATER diff --git a/tests/base.py b/tests/base.py new file mode 100644 index 0000000..686bebb --- /dev/null +++ b/tests/base.py @@ -0,0 +1,16 @@ +import sys + +from fastapi.testclient import TestClient + +sys.path.append("..") + +from src.main import app, get_db +from tests.config.database import override_get_db + +app.dependency_overrides[get_db] = override_get_db + +client = TestClient(app) + +username = "user1" +password = "password" +avatar = "lion" diff --git a/tests/test_authentication.py b/tests/test_authentication.py index d13cf05..8be7cea 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -1,21 +1,10 @@ -import sys - import pytest from fastapi.testclient import TestClient -sys.path.append("..") - from src.main import app, get_db +from tests.base import avatar, client, password, username from tests.config.database import clear_db, override_get_db -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - -username = "user1" -password = "password" -avatar = "lion" - async def register_user(): response = client.post( diff --git a/tests/test_courseprogress.py b/tests/test_courseprogress.py index 33cf43f..d6ee649 100644 --- a/tests/test_courseprogress.py +++ b/tests/test_courseprogress.py @@ -1,23 +1,13 @@ import random -import sys import pytest from fastapi.testclient import TestClient -sys.path.append("..") - from src.enums import CourseEnum from src.main import app, get_db +from tests.base import avatar, client, password, username from tests.config.database import clear_db, override_get_db -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - -username = "user1" -password = "password" -avatar = "lion" - async def register_user(): response = client.post( @@ -67,6 +57,19 @@ async def test_get_all_returns_all(): assert {"progress_value": 0.0, "course": course} in response +@pytest.mark.asyncio +async def test_get_course_progress_value_without_auth_should_fail(): + """Test whether fetching a course progress value without authentication fails""" + clear_db() + + headers = {"Content-Type": "application/json"} + + for course in CourseEnum: + response = client.get(f"/courseprogress/{course}", headers=headers) + + assert response.status_code == 403 + + @pytest.mark.asyncio async def test_get_nonexisting_course_should_fail(): """Test whether fetching the progress of a nonexisting course fails""" @@ -178,3 +181,20 @@ async def test_patch_course_with_invalid_value_should_fail(): ) assert response.status_code == 400 + + +@pytest.mark.asyncio +async def test_patch_course_progress_value_without_auth_should_fail(): + """Test whether updating a course progress value without authentication fails""" + clear_db() + + headers = {"Content-Type": "application/json"} + + for course in CourseEnum: + response = client.patch( + f"/courseprogress/{course}", + headers=headers, + json={"progress_value": random.uniform(0, 1)}, + ) + + assert response.status_code == 403 diff --git a/tests/test_highscores.py b/tests/test_highscores.py index a5a9a48..5b56291 100644 --- a/tests/test_highscores.py +++ b/tests/test_highscores.py @@ -1,21 +1,13 @@ -import sys +import random import pytest from fastapi.testclient import TestClient -sys.path.append("..") - +from src.enums import MinigameEnum from src.main import app, get_db +from tests.base import avatar, client, password, username from tests.config.database import clear_db, override_get_db -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - -username = "user1" -password = "password" -avatar = "lion" - async def register_user(): response = client.post( @@ -30,6 +22,218 @@ async def register_user(): @pytest.mark.asyncio -async def test_post_highscore(): - """Test whether posting a new high score succeeds""" +async def test_put_highscore(): + """Test whether putting a new high score succeeds""" clear_db() + token = await register_user() + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + for minigame in MinigameEnum: + score_value = random.random() + response = client.put( + f"/highscores/{minigame}", + headers=headers, + json={"score_value": score_value}, + ) + + assert response.status_code == 200 + + response = response.json() + + assert response["minigame"] == minigame + assert response["score_value"] == score_value + + +@pytest.mark.asyncio +async def test_put_lower_highscore_does_not_change_old_value(): + """Test whether putting a new high score lower than the current one doesn't change the old one""" + clear_db() + token = await register_user() + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + for minigame in MinigameEnum: + score_value = random.random() + response = client.put( + f"/highscores/{minigame}", + headers=headers, + json={"score_value": score_value}, + ) + + assert response.status_code == 200 + + response = response.json() + + assert response["minigame"] == minigame + assert response["score_value"] == score_value + + lower_score_value = score_value - 100 + response = client.put( + f"/highscores/{minigame}", + headers=headers, + json={"score_value": lower_score_value}, + ) + + assert response.status_code == 200 + + response = response.json() + + assert response["minigame"] == minigame + assert response["score_value"] == score_value + + +@pytest.mark.asyncio +async def test_put_highscore_for_nonexisting_minigame_should_fail(): + """Test whether putting a new high score for a nonexisting minigame fails""" + clear_db() + token = await register_user() + + fake_minigame = "FakeGame" + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + response = client.put( + f"/highscores/{fake_minigame}", + headers=headers, + json={"score_value": random.random()}, + ) + + assert response.status_code == 422 + + +@pytest.mark.asyncio +async def test_put_highscores_without_auth_should_fail(): + """Test whether putting high scores without authentication fails""" + clear_db() + + headers = {"Content-Type": "application/json"} + + for minigame in MinigameEnum: + response = client.put( + f"/highscores/{minigame}", + headers=headers, + json={"score_value": random.random()}, + ) + + assert response.status_code == 403 + + +@pytest.mark.asyncio +async def test_get_highscores_without_auth_should_fail(): + """Test whether fetching high scores without authentication fails""" + clear_db() + + headers = {"Content-Type": "application/json"} + + for minigame in MinigameEnum: + response = client.get( + f"/highscores/{minigame}", + headers=headers, + ) + + assert response.status_code == 403 + + +@pytest.mark.asyncio +async def test_get_highscore_for_nonexisting_minigame_should_fail(): + """Test whether fetching a new high score for a nonexisting minigame fails""" + clear_db() + token = await register_user() + + fake_minigame = "FakeGame" + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + response = client.get( + f"/highscores/{fake_minigame}", + headers=headers, + ) + + assert response.status_code == 422 + + +@pytest.mark.asyncio +async def test_get_invalid_number_of_highscores_should_fail(): + """Test whether getting a numbe rof high scores lower than 1 fails""" + clear_db() + token = await register_user() + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + for minigame in MinigameEnum: + response = client.get( + f"/highscores/{minigame}?nr_highest={random.randint(-100, 0)}", + headers=headers, + ) + + assert response.status_code == 400 + + +@pytest.mark.asyncio +async def test_get_highscores_should_work_with_default_value(): + """Test whether fetching high scores without passing an explicit amount still succeeds""" + clear_db() + token = await register_user() + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + for minigame in MinigameEnum: + response = client.get( + f"/highscores/{minigame}", + headers=headers, + ) + + assert response.status_code == 200 + + +@pytest.mark.asyncio +async def test_get_highscores_returns_sorted_list_with_correct_length(): + clear_db() + token = await register_user() + + headers = {"Content-Type": "application/json"} + + for minigame in MinigameEnum: + clear_db() + nr_entries = random.randint(5, 50) + + users_score_tuples = [ + (f"user{i + 1}", random.random()) for i in range(nr_entries) + ] + + for user, score in users_score_tuples: + response = client.post( + "/register", + headers=headers, + json={"username": user, "password": password, "avatar": avatar}, + ) + + assert response.status_code == 200 + + token = response.json()["access_token"] + + response = client.put( + f"/highscores/{minigame}?nr_highest={random.randint(1, nr_entries)}", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + }, + json={"score_value": score}, + ) + + assert response.status_code == 200 + + response = client.get( + f"/highscores/{minigame}", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + }, + ) + + assert response.status_code == 200 + response = response.json() + + for i in range(1, len(response)): + assert response[i]["score_value"] <= response[i - 1]["score_value"] diff --git a/tests/test_users.py b/tests/test_users.py index 5a169c4..f8ee485 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -1,21 +1,10 @@ -import sys - import pytest from fastapi.testclient import TestClient -sys.path.append("..") - from src.main import app, get_db +from tests.base import avatar, client, password, username from tests.config.database import clear_db, override_get_db -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - -username = "user1" -password = "password" -avatar = "lion" - patched_username = "New name" patched_password = "New password" patched_avatar = "New avatar" diff --git a/tests/usertests.py b/tests/usertests.py deleted file mode 100644 index 1f79c86..0000000 --- a/tests/usertests.py +++ /dev/null @@ -1,15 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from main import app - -client = TestClient(app) - - -def test_add_user(): - response = client.post( - "/users", - headers={"Content-Type": "application/json"}, - json={"username": "Lukas", "password": "mettn"}, - ) - assert response.status_code == 200