From f9aad400e0cc93d98e4610f75e1b0554e1d1daa2 Mon Sep 17 00:00:00 2001 From: lvrossem Date: Wed, 12 Apr 2023 12:02:25 -0600 Subject: [PATCH] More highscore endpoint functionality & tests --- src/crud/highscores.py | 39 ++++++++++++++----- src/main.py | 18 +++------ tests/test_highscores.py | 83 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 23 deletions(-) diff --git a/src/crud/highscores.py b/src/crud/highscores.py index a7eee7b..7a8d1a2 100644 --- a/src/crud/highscores.py +++ b/src/crud/highscores.py @@ -7,20 +7,41 @@ from src.models import HighScore, User from src.schemas.highscores import HighScoreBase from src.schemas.users import UserHighScore -DEFAULT_NR_HIGH_SCORES = 10 - -def get_high_scores(db: Session, minigame: MinigameEnum, nr_highest: int): +def get_high_scores( + db: Session, minigame: MinigameEnum, user: User, nr_highest: int, mine_only: bool +): """Get the n highest scores of a given minigame""" - if nr_highest: - if nr_highest < 1: - raise HTTPException(status_code=400, detail="Invalid number of high scores") + if nr_highest < 1: + raise HTTPException(status_code=400, detail="Invalid number of high scores") + + if mine_only: + if nr_highest > 1: + raise HTTPException( + status_code=400, + detail="nr_highest should be 1 when requesting high score of current user only", + ) + else: + high_score = ( + db.query(HighScore) + .filter( + HighScore.minigame == minigame, HighScore.owner_id == user.user_id + ) + .first() + ) + if high_score: + return [ + UserHighScore( + username=user.username, + score_value=high_score.score_value, + avatar=user.avatar, + ) + ] + else: + return [] user_high_scores = [] - if not nr_highest: - nr_highest = DEFAULT_NR_HIGH_SCORES - if not minigame: minigame = MinigameEnum.SpellingBee diff --git a/src/main.py b/src/main.py index 08f6e6f..320d627 100644 --- a/src/main.py +++ b/src/main.py @@ -64,11 +64,15 @@ async def login(user: users.UserCreate, db: Session = Depends(get_db)): @app.get("/highscores/{minigame}", response_model=List[users.UserHighScore]) async def get_high_scores( minigame: MinigameEnum, - nr_highest: Optional[int] = None, + nr_highest: Optional[int] = 1, + mine_only: Optional[bool] = True, 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) + print(str(nr_highest)) + print(str(mine_only)) + user = crud_users.get_user_by_username(db, current_user_name) + return crud_highscores.get_high_scores(db, minigame, user, nr_highest, mine_only) @app.put("/highscores/{minigame}", response_model=highscores.HighScore) @@ -107,13 +111,3 @@ async def patch_course_progress( return crud_courseprogress.patch_course_progress( db, current_user, course, course_progress ) - - -#### TESTING!! DELETE LATER - - -@app.get("/protected") -async def protected_route( - current_user_name: str = Depends(crud_authentication.get_current_user_name), -): - return {"message": f"Hello, {current_user_name}!"} diff --git a/tests/test_highscores.py b/tests/test_highscores.py index 5b56291..1eadcd1 100644 --- a/tests/test_highscores.py +++ b/tests/test_highscores.py @@ -134,6 +134,13 @@ async def test_get_highscores_without_auth_should_fail(): assert response.status_code == 403 + response = client.get( + f"/highscores/{minigame}?mine_only=false&nr_highest={random.randint(1, 50)}", + headers=headers, + ) + + assert response.status_code == 403 + @pytest.mark.asyncio async def test_get_highscore_for_nonexisting_minigame_should_fail(): @@ -152,6 +159,13 @@ async def test_get_highscore_for_nonexisting_minigame_should_fail(): assert response.status_code == 422 + response = client.get( + f"/highscores/{fake_minigame}?mine_only=false&nr_highest={random.randint(1, 50)}", + headers=headers, + ) + + assert response.status_code == 422 + @pytest.mark.asyncio async def test_get_invalid_number_of_highscores_should_fail(): @@ -189,6 +203,7 @@ async def test_get_highscores_should_work_with_default_value(): @pytest.mark.asyncio async def test_get_highscores_returns_sorted_list_with_correct_length(): + """Test whether getting a list of high scores gets a list in descending order and of the correct length""" clear_db() token = await register_user() @@ -197,6 +212,7 @@ async def test_get_highscores_returns_sorted_list_with_correct_length(): for minigame in MinigameEnum: clear_db() nr_entries = random.randint(5, 50) + token = "" users_score_tuples = [ (f"user{i + 1}", random.random()) for i in range(nr_entries) @@ -214,7 +230,7 @@ async def test_get_highscores_returns_sorted_list_with_correct_length(): token = response.json()["access_token"] response = client.put( - f"/highscores/{minigame}?nr_highest={random.randint(1, nr_entries)}", + f"/highscores/{minigame}", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", @@ -225,7 +241,7 @@ async def test_get_highscores_returns_sorted_list_with_correct_length(): assert response.status_code == 200 response = client.get( - f"/highscores/{minigame}", + f"/highscores/{minigame}?mine_only=false&nr_highest={int(nr_entries)}", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", @@ -235,5 +251,68 @@ async def test_get_highscores_returns_sorted_list_with_correct_length(): assert response.status_code == 200 response = response.json() + assert len(response) == nr_entries + for i in range(1, len(response)): assert response[i]["score_value"] <= response[i - 1]["score_value"] + + +@pytest.mark.asyncio +async def test_get_own_existing_high_score_should_return_high_score(): + """Test whether fetching your own high score of a game succeeds""" + clear_db() + token = await register_user() + + headers = {"Authorization": f"Bearer {token}", "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 == 200 + + response = client.get( + f"/highscores/{minigame}", + headers=headers, + ) + + assert response.status_code == 200 + assert len(response.json()) == 1 + + +@pytest.mark.asyncio +async def test_get_own_nonexisting_high_score_should_return_empty_list(): + """Test whether fetching the high score of a game you haven't played returns an empty list""" + 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 + assert len(response.json()) == 0 + + +@pytest.mark.asyncio +async def test_get_multiple_own_high_scores_of_same_game_should_fail(): + """Test whether asking more than one of your high scores on a single game 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(2, 20)}", + headers=headers, + ) + + assert response.status_code == 400