Fix tests for users and highscores

This commit is contained in:
lvrossem
2023-04-17 14:52:36 -06:00
parent 3596394f3f
commit 81e9eb154b
11 changed files with 156 additions and 201 deletions

View File

@@ -6,8 +6,7 @@ from sqlalchemy.orm import Session
from src.enums import MinigameEnum
from src.models import HighScore, User
from src.schemas.highscores import HighScoreBase
from src.schemas.saved_data import Score
from src.schemas.highscores import HighScoreBase, Score
def get_most_recent_high_scores(db: Session, minigame: MinigameEnum, amount: int):
@@ -27,7 +26,7 @@ def get_most_recent_high_scores(db: Session, minigame: MinigameEnum, amount: int
for high_score in high_scores_query:
high_scores.append(
Score(score_value=high_score.score_value, time=high_score.time)
Score(score_value=high_score.score_value, time=str(high_score.time))
)
return high_scores
@@ -54,10 +53,11 @@ def get_highest_high_scores(
.first()
)
if high_score:
print(str(high_score.time))
return [
Score(
score_value=high_score.score_value,
time=high_score.time,
time=str(high_score.time),
)
]
else:
@@ -76,7 +76,7 @@ def get_highest_high_scores(
for high_score in high_scores_query:
high_scores.append(
Score(score_value=high_score.score_value, time=high_score.time)
Score(score_value=high_score.score_value, time=str(high_score.time))
)
return high_scores
@@ -97,7 +97,7 @@ def create_high_score(
db.add(db_high_score)
db.commit()
db.refresh(db_high_score)
return db_high_score
return Score(score_value=db_high_score.score_value, time=str(db_high_score.time))
old_high_score = (
db.query(HighScore)
@@ -112,6 +112,6 @@ def create_high_score(
db.delete(old_high_score)
return add_to_db()
else:
return old_high_score
return Score(score_value=old_high_score.score_value, time=str(old_high_score.time))
else:
return add_to_db()

View File

@@ -1,81 +0,0 @@
from sqlalchemy.orm import Session
from src.crud.highscores import (get_highest_high_scores,
get_most_recent_high_scores)
from src.crud.users import get_user_by_username
from src.enums import CourseEnum, MinigameEnum
from src.models import CourseProgress, LearnableProgress
from src.schemas.saved_data import *
def get_saved_data(db: Session, username: str):
"""Fetches all saved progress for the current user from the database"""
user = get_user_by_username(db, username)
minigames = []
courses = []
for minigame in MinigameEnum:
minigames.append(
SavedMinigameProgress(
minigame_index=minigame,
latest_scores=[
score for score in get_most_recent_high_scores(db, minigame, 10)
],
highest_scores=[
score
for score in get_highest_high_scores(db, minigame, user, 10, False)
],
)
)
course_progress_query = (
db.query(CourseProgress).filter(CourseProgress.owner_id == user.user_id).all()
)
for course_progress in course_progress_query:
learnable_progress_query = (
db.query(LearnableProgress)
.filter(
LearnableProgress.course_progress_id
== course_progress.course_progress_id
)
.all()
)
learnables = [
SavedLearnableProgress(
index=lp.index, in_use=lp.in_use, name=lp.name, progress=lp.progress
)
for lp in learnable_progress_query
]
completed_learnables = sum(
[1 if learnable.progress == 5.0 else 0 for learnable in learnables]
)
in_use_learnables = sum(
[1 if learnable.in_use else 0 for learnable in learnables]
)
total_learnables = len(learnables)
courses.append(
SavedCourseProgress(
course_index=course_progress.course,
progress=course_progress.progress,
completed_learnables=completed_learnables,
in_use_learnables=in_use_learnables,
total_learnables=total_learnables,
learnables=learnables,
)
)
user_progress = SavedUser(
username=user.username,
avatar_index=user.avatar_index,
playtime=user.playtime,
minigames=minigames,
courses=courses,
)
return user_progress

View File

@@ -3,7 +3,15 @@ from passlib.context import CryptContext
from sqlalchemy.orm import Session
from src.models import User
from src.crud.highscores import (get_highest_high_scores,
get_most_recent_high_scores)
from src.schemas.users import UserCreate
from src.enums import CourseEnum, MinigameEnum
from src.models import CourseProgress, LearnableProgress
from src.schemas.highscores import SavedMinigameProgress
from src.schemas.courseprogress import SavedCourseProgress
from src.schemas.learnableprogress import SavedLearnableProgress
from src.schemas.users import SavedUser
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -20,15 +28,26 @@ def check_empty_fields(username: str, password: str, avatar_index: int):
def patch_user(db: Session, username: str, user: UserCreate):
"""Changes the username and/or the password of a User"""
check_empty_fields(user.username, user.password, user.avatar_index)
#check_empty_fields(user.username, user.password, user.avatar_index)
db_user = get_user_by_username(db, username)
potential_duplicate = get_user_by_username(db, user.username)
if potential_duplicate:
if potential_duplicate.user_id != db_user.user_id:
raise HTTPException(status_code=400, detail="Username already registered")
if user.playtime < 0:
raise HTTPException(status_code=400, detail="Negative playtime is invalid")
if len(user.username) > 0:
db_user.username = user.username
if len(user.password) > 0:
db_user.hashed_password = pwd_context.hash(user.password)
if user.avatar_index > -1:
db_user.avatar_index = user.avatar_index
db_user.playtime += user.playtime
db.commit()
@@ -40,3 +59,76 @@ def get_user_by_username(db: Session, username: str):
def get_users(db: Session):
"""Fetch a list of all users"""
return db.query(User).all()
def get_saved_data(db: Session, username: str):
"""Fetches all saved progress for the current user from the database"""
user = get_user_by_username(db, username)
minigames = []
courses = []
for minigame in MinigameEnum:
minigames.append(
SavedMinigameProgress(
minigame_index=minigame,
latest_scores=[
score for score in get_most_recent_high_scores(db, minigame, 10)
],
highest_scores=[
score
for score in get_highest_high_scores(db, minigame, user, 10, False)
],
)
)
course_progress_query = (
db.query(CourseProgress).filter(CourseProgress.owner_id == user.user_id).all()
)
for course_progress in course_progress_query:
learnable_progress_query = (
db.query(LearnableProgress)
.filter(
LearnableProgress.course_progress_id
== course_progress.course_progress_id
)
.all()
)
learnables = [
SavedLearnableProgress(
index=lp.index, in_use=lp.in_use, name=lp.name, progress=lp.progress
)
for lp in learnable_progress_query
]
completed_learnables = sum(
[1 if learnable.progress == 5.0 else 0 for learnable in learnables]
)
in_use_learnables = sum(
[1 if learnable.in_use else 0 for learnable in learnables]
)
total_learnables = len(learnables)
courses.append(
SavedCourseProgress(
course_index=course_progress.course,
progress=course_progress.progress,
completed_learnables=completed_learnables,
in_use_learnables=in_use_learnables,
total_learnables=total_learnables,
learnables=learnables,
)
)
user_progress = SavedUser(
username=user.username,
avatar_index=user.avatar_index,
playtime=user.playtime,
minigames=minigames,
courses=courses,
)
return user_progress

View File

@@ -9,7 +9,6 @@ sys.path.append("..")
from src.crud import authentication as crud_authentication
from src.crud import courseprogress as crud_courseprogress
from src.crud import highscores as crud_highscores
from src.crud import saved_data as crud_saved_data
from src.crud import users as crud_users
from src.database import Base, engine, get_db
from src.enums import CourseEnum, MinigameEnum
@@ -53,7 +52,7 @@ async def read_saved_data(
current_user_name: str = Depends(crud_authentication.get_current_user_name),
db: Session = Depends(get_db),
):
return crud_saved_data.get_saved_data(db, current_user_name)
return crud_users.get_saved_data(db, current_user_name)
@app.post("/register")
@@ -88,7 +87,7 @@ async def get_high_scores(
)
@app.put("/highscores/{minigame}", response_model=highscores.HighScore)
@app.put("/highscores/{minigame}", response_model=highscores.Score)
async def create_high_score(
minigame: MinigameEnum,
high_score: highscores.HighScoreBase,

View File

@@ -28,3 +28,6 @@ class SavedCourseProgress(BaseModel):
in_use_learnables: int
total_learnables: int
learnables: List[SavedLearnableProgress]
class Config:
orm_mode = True

View File

@@ -9,28 +9,17 @@ class HighScoreBase(BaseModel):
score_value: float
class HighScore(HighScoreBase):
high_score_id: int
owner_id: int
minigame: MinigameEnum
class Config:
orm_mode = True
class Score(BaseModel):
score_id: int
score_value: float
class Score(HighScoreBase):
time: str
class Config:
orm_mode = True
class Score(BaseModel):
score_value: int
time: str
class SavedMinigameProgress(BaseModel):
minigame_index: MinigameEnum
latest_scores: List[Score]
highest_scores: List[Score]
class Config:
orm_mode = True

View File

@@ -1,21 +0,0 @@
from typing import List
from pydantic import BaseModel
from src.enums import CourseEnum, MinigameEnum
class Score(BaseModel):
score_value: int
time: str
class SavedLearnableProgress(BaseModel):
index: int
in_use: bool
name: str
progress: float

View File

@@ -5,17 +5,16 @@ from src.schemas.courseprogress import SavedCourseProgress
class UserBase(BaseModel):
username: str
username: str = ""
avatar_index: int = -1
class UserCreate(UserBase):
password: str
password: str = ""
playtime: float = 0.0
class SavedUser(BaseModel):
username: str
avatar_index: int = -1
class SavedUser(UserBase):
playtime: float
minigames: List[SavedMinigameProgress]
courses: List[SavedCourseProgress]

View File

@@ -46,7 +46,7 @@ async def test_register_without_username_should_fail():
json={"password": password, "avatar_index": avatar_index},
)
assert response.status_code == 422
assert response.status_code == 400
assert "access_token" not in response.json()
@@ -61,7 +61,7 @@ async def test_register_without_password_should_fail():
json={"username": username, "avatar_index": avatar_index},
)
assert response.status_code == 422
assert response.status_code == 400
assert "access_token" not in response.json()
@@ -76,7 +76,6 @@ async def test_register_without_avatar_should_fail():
json={"username": username, "password": password},
)
# Not ideal that this is 400 instead of 422, but had no other choice than to give this field a default value
assert response.status_code == 400
assert "access_token" not in response.json()
@@ -115,7 +114,7 @@ async def test_login_wrong_password_should_fail():
@pytest.mark.asyncio
async def test_login_without_username_should_fail():
"""Test whether logging in without passing a username fails"""
"""Test whether logging in without passing a username fails, since the default is an empty string"""
clear_db()
await register_user()
@@ -125,13 +124,13 @@ async def test_login_without_username_should_fail():
json={"password": password},
)
assert response.status_code == 422
assert response.status_code == 401
assert "access_token" not in response.json()
@pytest.mark.asyncio
async def test_login_without_password_should_fail():
"""Test whether logging in without passing a password fails"""
"""Test whether logging in without passing a password fails, since the default is an empty string"""
clear_db()
await register_user()
@@ -141,5 +140,5 @@ async def test_login_without_password_should_fail():
json={"username": username},
)
assert response.status_code == 422
assert response.status_code == 401
assert "access_token" not in response.json()

View File

@@ -3,7 +3,7 @@ import random
import pytest
from src.enums import MinigameEnum
from tests.base import client, password, register_user
from tests.base import client, password, avatar_index, register_user
from tests.config.database import clear_db
@@ -27,7 +27,6 @@ async def test_put_highscore():
response = response.json()
assert response["minigame"] == minigame
assert response["score_value"] == score_value
@@ -51,7 +50,6 @@ async def test_put_lower_highscore_does_not_change_old_value():
response = response.json()
assert response["minigame"] == minigame
assert response["score_value"] == score_value
lower_score_value = score_value - 100
@@ -65,7 +63,6 @@ async def test_put_lower_highscore_does_not_change_old_value():
response = response.json()
assert response["minigame"] == minigame
assert response["score_value"] == score_value
@@ -121,7 +118,7 @@ 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)}",
f"/highscores/{minigame}?mine_only=false&amount={random.randint(1, 50)}",
headers=headers,
)
@@ -146,7 +143,7 @@ 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)}",
f"/highscores/{fake_minigame}?mine_only=false&amount={random.randint(1, 50)}",
headers=headers,
)
@@ -163,7 +160,7 @@ async def test_get_invalid_number_of_highscores_should_fail():
for minigame in MinigameEnum:
response = client.get(
f"/highscores/{minigame}?nr_highest={random.randint(-100, 0)}",
f"/highscores/{minigame}?amount={random.randint(-100, 0)}",
headers=headers,
)
@@ -231,7 +228,7 @@ async def test_get_highscores_returns_sorted_list_with_correct_length():
assert response.status_code == 200
response = client.get(
f"/highscores/{minigame}?mine_only=false&nr_highest={int(nr_entries)}",
f"/highscores/{minigame}?mine_only=false&amount={int(nr_entries)}",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
@@ -301,7 +298,7 @@ async def test_get_multiple_own_high_scores_of_same_game_should_fail():
for minigame in MinigameEnum:
response = client.get(
f"/highscores/{minigame}?nr_highest={random.randint(2, 20)}",
f"/highscores/{minigame}?amount={random.randint(2, 20)}",
headers=headers,
)

View File

@@ -8,30 +8,6 @@ patched_password = "New password"
patched_avatar_index = 2
@pytest.mark.asyncio
async def test_get_current_user():
"""Test the GET /users endpoint to get info about the current user"""
clear_db()
token = await register_user()
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
response = client.get("/users", headers=headers)
assert response.status_code == 200
response = response.json()
assert response["username"] == username
assert response["avatar_index"] == avatar_index
@pytest.mark.asyncio
async def test_get_current_user_without_auth():
"""Getting the current user without a token should fail"""
clear_db()
response = client.get("/users", headers={"Content-Type": "application/json"})
assert response.status_code == 403
@pytest.mark.asyncio
async def test_patch_user():
@@ -64,7 +40,7 @@ async def test_patch_user():
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
response = client.get("/users", headers=headers)
response = client.get("/saveddata", headers=headers)
assert response.status_code == 200
# Correctness of password and username is already asserted by the login
@@ -80,35 +56,38 @@ async def test_patch_user_with_empty_fields():
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
response = client.patch(
"/users",
json={
"username": patched_username,
"password": patched_password,
"avatar_index": "",
},
headers=headers,
)
assert response.status_code == 422
response = client.patch(
"/users",
json={
"username": patched_username,
"password": "",
"avatar_index": patched_avatar_index,
},
headers=headers,
)
assert response.status_code == 400
response = client.patch(
"/users",
json={
"username": "",
"password": patched_password,
"avatar_index": patched_avatar_index,
"playtime": 0.0
},
headers=headers,
)
assert response.status_code == 400
assert response.status_code == 200
response = client.patch(
"/users",
json={
"username": username,
"password": patched_password,
"avatar_index": -1,
"playtime": 0.0
},
headers=headers,
)
assert response.status_code == 200
response = client.patch(
"/users",
json={
"username": username,
"password": "",
"avatar_index": patched_avatar_index,
"playtime": 0.0
},
headers=headers,
)
assert response.status_code == 200