async def test_create_user(client, users): new_user = get_fake_user() new_user.pop("id") res = await client.post("/users", json=new_user) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) all_users = await User.query.gino.all() assert len(all_users) == len(users) + 1 assert profile_created_from_origin(new_user, all_users[-1].to_dict()) # Ignore param args # POST request will have its query parameter (args) ignored. new_user = get_fake_user() new_user.pop("id") res = await client.post("/users", json=new_user) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) all_users = await User.query.gino.all() assert len(all_users) == len(users) + 2 assert profile_created_from_origin(new_user, all_users[-1].to_dict())
async def test_get_quizzes_with_after_id(client, quizzes, token_user): # Use after_id in query parameter. res = await client.get( "/quizzes?after_id={}".format(quizzes[2]["id"]), headers={"Authorization": token_user}, ) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for quiz is 15 # Check if all profiles match from id 3 to 18 assert all( profile_created_from_origin(origin, created) for origin, created in zip(quizzes[3:18], body["data"])) # Check that next page is valid next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link, headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == len( quizzes[18:]) # Default offset for quiz is 15 # Check if all profiles match from id 4 to 19 assert all( profile_created_from_origin(origin, created) for origin, created in zip(quizzes[18:], body["data"])) # Check that next page is empty # Check that next page is valid next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link, headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert not body["data"] # Invalid after_id res = await client.get("/quizzes?after_id=3", headers={"Authorization": token_user}) assert res.status == 404 res = await client.get("/quizzes?after_id=", headers={"Authorization": token_user}) assert res.status == 400
async def test_create_quiz(client, users, quizzes, token_user): fake_quiz = get_fake_quiz() fake_quiz.pop("creator_id") new_quiz = { **fake_quiz, "questions": get_fake_quiz_questions(has_id=False) } new_quiz.pop("id", None) # Cannot create an quiz without token res = await client.post("/quizzes", json=new_quiz) assert res.status == 401 # Create a quiz with valid args res = await client.post("/quizzes", json=new_quiz, headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) all_quizzes = await Quiz.query.gino.all() assert len(all_quizzes) == len(quizzes) + 1 assert profile_created_from_origin(new_quiz, all_quizzes[-1].to_dict(), ignore={"questions"})
async def test_get_questions(client, questions, quizzes, token_user): # Without token res = await client.get("/quizzes/{}/questions".format(quizzes[2]["id"])) assert res.status == 401 # Get one quiz with id res = await client.get( "/quizzes/{}/questions".format(quizzes[2]["id"]), headers={"Authorization": token_user}, ) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) for question_origin, question_retrieved in zip(questions[2], body["data"]): assert "correct_option" not in question_retrieved assert all(key in question_retrieved for key in ["text", "quiz_id", "id", "options"]) assert profile_created_from_origin(question_origin, question_retrieved, ignore={"correct_option"}) # quiz doesnt exist res = await client.get("/quizzes/{}/questions".format("9" * 32), headers={"Authorization": token_user}) assert res.status == 404 res = await client.get("/quizzes/3/questions", headers={"Authorization": token_user}) assert res.status == 404
async def test_update_one_user(client, users, token_user): new_changes = { "full_name": "this name surely doesnt exist", "password": "******", } # Without token res = await client.patch("/users/{}".format(users[0]["id"]), json=new_changes) assert res.status == 401 # An user cannot update another user res = await client.patch( "/users/{}".format(users[3]["id"]), json=new_changes, headers={"Authorization": token_user}, ) assert res.status == 403 # With id res = await client.patch( "/users/{}".format(users[0]["id"]), json=new_changes, headers={"Authorization": token_user}, ) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) updated_user = await get_one(User, internal_id=1) updated_user = updated_user.to_dict() ## Assert the new password has been updated assert profile_created_from_origin( { **body["data"], "password": hash_password(new_changes["password"]) }, updated_user, ignore=["updated_at"], ) # User doesnt exist res = await client.patch( "/users/{}".format("9" * 32), json=new_changes, headers={"Authorization": token_user}, ) assert res.status == 404 # Update to a weak password new_changes = {"password": "******"} res = await client.patch( "/users/{}".format(users[1]["id"]), json=new_changes, headers={"Authorization": token_user}, ) assert res.status == 400
async def test_login_user(client, users): for user in users[-1:-4]: res = await client.post("/login", json={ "email": user["email"], "password": user["password"] }) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body, dict) assert "access_token" in body and "refresh_token" in body assert "user" in body assert profile_created_from_origin(user, body["user"]) # Login with wrong password user = users[-1] res = await client.post("/login", json={ "email": user["email"], "password": "******" }) assert res.status == 401 # Login with weak password user = users[-1] res = await client.post("/login", json={ "email": user["email"], "password": "******" }) assert res.status == 400 # Login with missing password user = users[-1] res = await client.post("/login", json={"email": user["email"]}) assert res.status == 400 # Login with missing email user = users[-1] res = await client.post("/login", json={"password": user["password"]}) assert res.status == 400
async def test_get_one_user(client, users): res = await client.get("/users/{}".format(users[0]["id"])) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) assert profile_created_from_origin(users[0], body["data"]) # User doesnt exist res = await client.get("/users/{}".format("9" * 32)) assert res.status == 404 res = await client.get("/users/true") assert res.status == 404 # Invalid query res = await client.get("/users?id=") assert res.status == 400
async def test_get_users_with_after_id(client, users): # Use after_id in query parameter. res = await client.get("/users?after_id={}".format(users[2]["id"])) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for User is 15 # Check if all profiles match from id 4 to 19 assert all( profile_created_from_origin(origin, created) for origin, created in zip(users[3:20], body["data"])) # Invalid after_id res = await client.get("/users?after_id=2") assert res.status == 404 res = await client.get("/users?after_id=") assert res.status == 400
async def test_get_questions_after_create(client, questions, quizzes, token_user): fake_quiz = get_fake_quiz() fake_quiz.pop("creator_id") quiz_questions = get_fake_quiz_questions(has_id=False) new_quiz = {**fake_quiz, "questions": quiz_questions} new_quiz.pop("id", None) # Create a quiz with valid args res = await client.post("/quizzes", json=new_quiz, headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() quiz_id = body["data"]["id"] # Get the created questions res = await client.get("/quizzes/{}/questions".format(quiz_id), headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() created_questions = body["data"] for question_origin, question_retrieved in zip(quiz_questions, created_questions): assert "correct_option" not in question_retrieved assert all(key in question_retrieved for key in ["text", "quiz_id", "id", "options"]) assert profile_created_from_origin(question_origin, question_retrieved, ignore={"correct_option"}) # If created quiz has the correct questions' IDs res = await client.get("/quizzes/{}".format(quiz_id), headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert len(quiz_questions) == len(body["data"]["questions"]) assert all(quiz_question_id == created_question["id"] for quiz_question_id, created_question in zip( body["data"]["questions"], created_questions))
async def test_replace_user(client, users, token_user): new_user = get_fake_user() new_user.pop("id") # Missing token res = await client.put("/users/{}".format(users[0]["id"]), json=new_user) assert res.status == 401 # Valid request res = await client.put( "/users/{}".format(users[0]["id"]), json=new_user, headers={"Authorization": token_user}, ) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) updated_user = await get_one(User, internal_id=1) updated_user = updated_user.to_dict() assert profile_created_from_origin(new_user, updated_user)
async def test_get_one_quiz(client, quizzes, token_user): # Without token # Get one quiz with id res = await client.get("/quizzes/{}".format(quizzes[2]["id"])) assert res.status == 401 # Get one quiz with id res = await client.get("/quizzes/{}".format(quizzes[2]["id"]), headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], dict) assert profile_created_from_origin(quizzes[2], body["data"]) assert body["data"]["num_attempts"] == 0 # quiz doesnt exist res = await client.get("/quizzes/{}".format("9" * 32), headers={"Authorization": token_user}) assert res.status == 404 res = await client.get("/quizzes/3", headers={"Authorization": token_user}) assert res.status == 404
async def test_get_all_quizzes(client, quizzes, token_user): # Without token res = await client.get("/quizzes") assert res.status == 401 res = await client.get("/quizzes", headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for quiz is 15 assert all( profile_created_from_origin(origin, created) and created["num_attempts"] == 0 for origin, created in zip(quizzes, body["data"])) # GET request will have its body ignored. res = await client.get("/quizzes", json={"num_attempts": 0}, headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for quiz is 15 # Get one quiz by id with many=True res = await client.get("/quizzes?id={}".format(quizzes[2]["id"]), headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 1 assert profile_created_from_origin(quizzes[2], body["data"][0]) ## LIMIT ## # No quizzes res = await client.get("/quizzes?limit=0", headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert not body["data"] # 10 quizzes res = await client.get("/quizzes?limit=10", headers={"Authorization": token_user}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 10 assert all( profile_created_from_origin(origin, created) for origin, created in zip(quizzes[:10], body["data"])) # -1 quizzes res = await client.get("/quizzes?limit=-1", headers={"Authorization": token_user}) assert res.status == 400
async def test_pagination_created_attempted_quizzes(app, client, users, questions, quizzes, token_user): # Create a fresh user, as the created user already has some previously created quizzes new_user = get_fake_user() new_user.pop("id") res = await client.post("/users", json=new_user) assert res.status == 200 body = await res.json() new_user_id = body["data"]["id"] new_user_token = await get_access_token_for_user(body["data"], app) # Create a few quizzes created_quizzes = [] for _ in range(35): fake_quiz = get_fake_quiz() fake_quiz.pop("creator_id") new_quiz = { **fake_quiz, "questions": get_fake_quiz_questions(has_id=False) } new_quiz.pop("id", None) # Create a quiz with valid args res = await client.post("/quizzes", json=new_quiz, headers={"Authorization": new_user_token}) assert res.status == 200 body = await res.json() created_quizzes.append(body["data"]) # Check pagination res = await client.get("/users/{}/quizzes/created".format(new_user_id)) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 for created, retrieve in zip(created_quizzes[:15], body["data"]): assert profile_created_from_origin(retrieve, created, ignore={"questions", "updated_at"}) # Check second page next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 for created, retrieve in zip(created_quizzes[15:30], body["data"]): assert profile_created_from_origin(retrieve, created, ignore={"questions", "updated_at"}) # Check last page next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 5 for created, retrieve in zip(created_quizzes[30:], body["data"]): assert profile_created_from_origin(retrieve, created, ignore={"questions", "updated_at"}) ## ATTEMPTED # Attempt to do a few quizzes as well attempt_user_id = users[0]["id"] attempted_quizzes = created_quizzes[::-1] for quiz in created_quizzes: question_index = 5 selected_option = 3 res = await client.post( "/quizzes/{}/questions/{}/answers".format( quiz["id"], quiz["questions"][question_index]), json={"selected_option": selected_option}, headers={"Authorization": token_user}, ) assert res.status == 200 body = await res.json() # Check pagination res = await client.get( "/users/{}/quizzes/attempted".format(attempt_user_id)) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 assert "links" in body assert "next" in body["links"] for created, retrieve in zip(attempted_quizzes[:15], body["data"]): assert profile_created_from_origin( retrieve, { **created, "num_attempts": 1, "is_finished": False }, ignore={"questions", "updated_at"}, ) # Check second page next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 for created, retrieve in zip(attempted_quizzes[15:30], body["data"]): assert profile_created_from_origin( retrieve, { **created, "num_attempts": 1, "is_finished": False }, ignore={"questions", "updated_at"}, ) # Check last page next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 5 for created, retrieve in zip(attempted_quizzes[30:], body["data"]): assert profile_created_from_origin( retrieve, { **created, "num_attempts": 1, "is_finished": False }, ignore={"questions", "updated_at"}, )
async def test_get_own_created_and_attempted_quizzes(app, client, users, questions, quizzes): # Create a fresh user new_user = get_fake_user() new_user.pop("id") res = await client.post("/users", json=new_user) assert res.status == 200 body = await res.json() new_user_id = body["data"]["id"] new_user_token = await get_access_token_for_user(body["data"], app) # Create a few quizzes created_quizzes = [] for _ in range(20): fake_quiz = get_fake_quiz() fake_quiz.pop("creator_id") new_quiz = { **fake_quiz, "questions": get_fake_quiz_questions(has_id=False) } new_quiz.pop("id", None) # Cannot create an quiz without token res = await client.post("/quizzes", json=new_quiz) assert res.status == 401 # Create a quiz with valid args res = await client.post("/quizzes", json=new_quiz, headers={"Authorization": new_user_token}) assert res.status == 200 body = await res.json() created_quizzes.append(body["data"]) # Attempt to do a few quizzes as well attempted_quizzes = [] for quiz_index in range(1, 17): question_index = 3 selected_option_3 = 1 res = await client.post( "/quizzes/{}/questions/{}/answers".format( quizzes[quiz_index]["id"], questions[quiz_index][question_index]["id"]), json={"selected_option": selected_option_3}, headers={"Authorization": new_user_token}, ) assert res.status == 200 body = await res.json() attempted_quizzes.append(quizzes[quiz_index]) # Check if the attempted and created quizzes are correct res = await client.get("/users/{}/quizzes/created".format(new_user_id)) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Check if the created quizzes are correct for created, retrieve in zip(created_quizzes, body["data"]): assert profile_created_from_origin(retrieve, created, ignore={"questions", "updated_at"}) # Check if the attempted quizzes are correct res = await client.get("/users/{}/quizzes/attempted".format(new_user_id)) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 for expected_quiz, actual_quiz in zip(attempted_quizzes[::-1], body["data"]): assert profile_created_from_origin( { **expected_quiz, "is_finished": False }, actual_quiz, ignore={"questions", "updated_at"}, )
async def test_get_all_users(client, users): res = await client.get("/users") assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for User is 15 assert all( profile_created_from_origin(origin, created) for origin, created in zip(users, body["data"])) # GET request will have its body ignored. res = await client.get("/users", json={"id": 3}) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 15 # Default offset for User is 15 # Get one user by id res = await client.get("/users?id={}".format(users[2]["id"])) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 1 assert profile_created_from_origin(users[2], body["data"][0]) ## LIMIT ## # No users res = await client.get("/users?limit=0") assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert not body["data"] # 10 users res = await client.get("/users?limit=10") assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 10 assert all( profile_created_from_origin(origin, created) for origin, created in zip(users[:10], body["data"])) # Get the next 10 users next_page_link = body["links"]["next"] # Strip the host, as it is a testing host next_page_link = "/" + "/".join(next_page_link.split("/")[3:]) res = await client.get(next_page_link) assert res.status == 200 body = await res.json() assert "data" in body assert isinstance(body["data"], list) assert len(body["data"]) == 10 assert all( profile_created_from_origin(origin, created) for origin, created in zip(users[10:20], body["data"])) # -1 users res = await client.get("/users?limit=-1") assert res.status == 400
async def test_replace_user_with_invalid_args(client, users): res = await client.put("/users/{}".format(users[0]["id"]), json={}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"id": 4}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"full_name": ""}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"full_name": ""}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={ "full_name": "Josh", "password": "" }) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"email": ""}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"location": 2}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"created_at": 2}) assert res.status == 400 res = await client.put("/users/{}".format(users[0]["id"]), json={"updated_at": 2}) assert res.status == 400 # Invalid or weak password res = await client.put( "/users/{}".format(users[0]["id"]), json={ "full_name": "Josh", "password": "******" }, ) assert res.status == 400 res = await client.put( "/users/{}".format(users[0]["id"]), json={ "full_name": "Josh", "password": "******" }, ) assert res.status == 400 # Assert no new users are created all_users = await User.query.gino.all() assert len(all_users) == len(users) updated_user = await get_one(User, internal_id=1) updated_user = updated_user.to_dict() assert profile_created_from_origin(users[0], updated_user)