Fix tests for users and highscores
This commit is contained in:
parent
3596394f3f
commit
81e9eb154b
@ -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()
|
||||
|
||||
@ -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
|
||||
@ -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")
|
||||
db_user.username = user.username
|
||||
db_user.hashed_password = pwd_context.hash(user.password)
|
||||
db_user.avatar_index = user.avatar_index
|
||||
|
||||
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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -27,4 +27,7 @@ class SavedCourseProgress(BaseModel):
|
||||
completed_learnables: int
|
||||
in_use_learnables: int
|
||||
total_learnables: int
|
||||
learnables: List[SavedLearnableProgress]
|
||||
learnables: List[SavedLearnableProgress]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user