diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index acdb6f6..7f1459f 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -95,7 +95,6 @@ async def upload_image(edit_id: str, pow_data: str, file: UploadFile = File()): raise HTTPException(status_code=401, detail="Edit ID not found!") if session_data is None: raise HTTPException(status_code=401, detail="Edit ID not found!") - if uploaded_images == 0 and not check_hashcash(pow_data, pow_data_server, "8"): raise HTTPException(status_code=401, detail="Edit ID not found!") if uploaded_images != 0 and not check_hashcash(pow_data, pow_data_server, "8"): @@ -103,7 +102,10 @@ async def upload_image(edit_id: str, pow_data: str, file: UploadFile = File()): file_bytes = await file.read() if len(file_bytes) > 2000000: raise HTTPException(status_code=400, detail="File too large") - pm_data = puremagic.magic_string(file_bytes)[0] + try: + pm_data = puremagic.magic_string(file_bytes)[0] + except puremagic.PureError: + raise HTTPException(status_code=400, detail="Image couldn't be identified!") if pm_data.extension not in allowed_image_extensions: raise HTTPException(status_code=400, detail="Image-type now allowed!") session_data = EditSessionData.parse_raw(session_data) diff --git a/classquiz/routers/eximport.py b/classquiz/routers/eximport.py index 2bb7fc3..b9b74c9 100644 --- a/classquiz/routers/eximport.py +++ b/classquiz/routers/eximport.py @@ -5,8 +5,9 @@ import json import uuid from datetime import datetime +import ormar.exceptions from aiohttp import ClientSession -from fastapi import APIRouter, Depends, Response, File, UploadFile +from fastapi import APIRouter, Depends, Response, File, UploadFile, HTTPException from classquiz.auth import get_current_user from classquiz.config import storage, settings @@ -22,7 +23,10 @@ image_index_delimiter = b"\xc5\xc5\x00" @router.get("/{quiz_id}") async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user)): - quiz: Quiz = await Quiz.objects.filter(Quiz.id == quiz_id).first() + try: + quiz: Quiz = await Quiz.objects.filter(Quiz.id == quiz_id).first() + except ormar.exceptions.NoMatch: + raise HTTPException(status_code=404, detail="Quiz not found") image_urls = {} for i, question in enumerate(quiz.questions): if question["image"] is None: diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index 901e78d..eeb1eb2 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -185,7 +185,6 @@ async def update_quiz(quiz_id: str, quiz_input: QuizInput, user: User = Depends( raise HTTPException(status_code=400, detail="badly formed quiz id") # Check Cover-Image - print(quiz_input.cover_image) if quiz_input.cover_image == "": quiz_input.cover_image = None diff --git a/classquiz/tests/__init__.py b/classquiz/tests/__init__.py index 0729c0d..8416387 100644 --- a/classquiz/tests/__init__.py +++ b/classquiz/tests/__init__.py @@ -30,6 +30,9 @@ class ValueStorage: imported_quizzes = [] game_pin = None game_id = None + exported_quiz_data = None + edit_id = None + image_id = None example_quiz = { diff --git a/classquiz/tests/test_server.py b/classquiz/tests/test_server.py index c1055cd..603482a 100644 --- a/classquiz/tests/test_server.py +++ b/classquiz/tests/test_server.py @@ -5,10 +5,12 @@ import uuid import pytest +from httpx import AsyncClient from redis import Redis from classquiz.config import settings from classquiz.tests import test_user_email, test_user_password from classquiz.tests import test_client, example_quiz, ValueStorage # noqa : F401 +from classquiz.helpers.hashcash import mint # @pytest.fixture @@ -414,14 +416,17 @@ class TestPlayQuiz: ) token = resp.cookies["access_token"] resp = test_client.post( - "/api/v1/quiz/start/fb5adc91-629e-416e-8b98-ae400e36417c", cookies={"access_token": token} + "/api/v1/quiz/start/fb5adc91-629e-416e-8b98-ae400e36417c?game_mode=kahoot", cookies={"access_token": token} ) assert resp.status_code == 404 resp = test_client.post( - "/api/v1/quiz/start/fb5adc91-629e-416e-8b98-ae400sdadsasadsadasddsae36417c", cookies={"access_token": token} + "/api/v1/quiz/start/fb5adc91-629e-416e-8b98-ae400sdadsasadsadasddsae36417c?game_mode=kahoot", + cookies={"access_token": token}, ) assert resp.status_code == 400 - resp = test_client.post(f"/api/v1/quiz/start/{ValueStorage.quiz_id}", cookies={"access_token": token}) + resp = test_client.post( + f"/api/v1/quiz/start/{ValueStorage.quiz_id}?game_mode=kahoot", cookies={"access_token": token} + ) ValueStorage.game_pin = resp.json()["game_pin"] ValueStorage.game_id = resp.json()["game_id"] @@ -462,6 +467,66 @@ class TestCache: """ +class TestEditor: + @pytest.mark.asyncio + async def test_start(self, test_client): # noqa : F811 + resp = test_client.post( + "/api/v1/users/token/cookie", data={"username": test_user_email, "password": test_user_password} + ) + token = resp.cookies["access_token"] + + resp = test_client.post("/api/v1/editor/start?edit=false", cookies={"access_token": token}) + assert resp.status_code == 200 + edit_id = resp.json()["token"] + resp = test_client.get(f"/api/v1/editor/pow?edit_id={edit_id}") + assert resp.status_code == 200 + pow_data = resp.json()["data"] + resp = test_client.get("/api/v1/editor/pow?edit_id=loladdfs") + assert resp.status_code == 401 + pow_res = mint(pow_data, 8, None, "", 8, False) + print("POW-Res", pow_res) + + async with AsyncClient() as ac: + resp = await ac.get("https://i.imgur.com/OE22DNZ.png") + image_bytes = resp.read() + + resp = test_client.post( + f"/api/v1/editor/image?edit_id={edit_id}&pow_data={pow_res}", files={"file": image_bytes} + ) + assert resp.status_code == 200 + ValueStorage.edit_id = edit_id + ValueStorage.image_id = resp.json()["id"] + + @pytest.mark.asyncio + async def test_finish(self, test_client): # noqa : F811 + local_example_quiz = example_quiz + local_example_quiz["questions"][1][ + "image" + ] = f"http://localhost:8080/api/v1/storage/download/{ValueStorage.image_id}" + resp = test_client.post(f"/api/v1/editor/finish?edit_id={ValueStorage.edit_id}", json=example_quiz) + assert resp.status_code == 200 + + +class TestExImport: + @pytest.mark.asyncio + async def test_export_quiz(self, test_client): # noqa : F811 + + resp = test_client.get("/api/v1/eximport/jgfgufgfgfzftzi") + assert resp.status_code == 422 + resp = test_client.get("/api/v1/eximport/8bd77201-65ed-46fe-9160-cfe71dad501f") + assert resp.status_code == 404 + resp = test_client.get(f"/api/v1/eximport/{ValueStorage.quiz_id}") + assert resp.status_code == 200 + exported_data = resp.content + assert len(exported_data) > 3000 + ValueStorage.exported_quiz_data = exported_data + + @pytest.mark.asyncio + async def test_import_quiz(self, test_client): # noqa : F811 + resp = test_client.post("/api/v1/eximport/", files={"file": ValueStorage.exported_quiz_data}) + assert resp.status_code == 200 + + class TestDeleteStuff: @pytest.mark.asyncio async def test_delete_quiz(self, test_client): # noqa : F811 diff --git a/frontend/src/lib/hashcash.ts b/frontend/src/lib/hashcash.ts index ddb9ee7..620dcbb 100644 --- a/frontend/src/lib/hashcash.ts +++ b/frontend/src/lib/hashcash.ts @@ -18,7 +18,7 @@ const gen_salt = (l: number): string => { export const mint = async ( resource: string, - bits = 16, + bits = 8, // now = null, ext = '', saltchars = 8, diff --git a/run_tests.sh b/run_tests.sh index be0a5fa..991403d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -33,6 +33,11 @@ a) run_tests stop ;; +prepare) + stop + docker volume rm classquiz_db_data + init + ;; *) echo "Invalid option: -$OPTARG" >&2 exit 1