Quiiz system
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import requests
|
||||
|
||||
from tests.api.quizzes._helpers import (
|
||||
JSON,
|
||||
NUMERIC,
|
||||
answer_correctly,
|
||||
attempt_state,
|
||||
build_published,
|
||||
signup,
|
||||
start_attempt,
|
||||
)
|
||||
from tests.conftest import BASE_URL
|
||||
|
||||
|
||||
def _play(session, slug, correct=True):
|
||||
attempt_uid = start_attempt(session, slug)
|
||||
state = attempt_state(session, slug, attempt_uid)
|
||||
for question in state["attempt"]["questions"]:
|
||||
if correct:
|
||||
answer_correctly(session, slug, attempt_uid, question)
|
||||
else:
|
||||
session.post(
|
||||
f"{BASE_URL}/quizzes/{slug}/attempts/{attempt_uid}/answer",
|
||||
data={"question_uid": question["uid"], "answer_text": "-1"},
|
||||
headers=JSON,
|
||||
)
|
||||
session.post(f"{BASE_URL}/quizzes/{slug}/attempts/{attempt_uid}/finish", headers=JSON)
|
||||
return attempt_uid
|
||||
|
||||
|
||||
def _entry(username, limit=100):
|
||||
payload = requests.get(f"{BASE_URL}/quizzes/scoreboard?limit={limit}", headers=JSON).json()
|
||||
for entry in payload["scoreboard"]:
|
||||
if entry["user"]["username"] == username:
|
||||
return entry
|
||||
return None
|
||||
|
||||
|
||||
def test_the_scoreboard_is_public(app_server, seeded_db):
|
||||
response = requests.get(f"{BASE_URL}/quizzes/scoreboard", headers=JSON)
|
||||
assert response.status_code == 200
|
||||
assert "scoreboard" in response.json()
|
||||
|
||||
|
||||
def test_the_scoreboard_reports_its_limit(app_server, seeded_db):
|
||||
payload = requests.get(f"{BASE_URL}/quizzes/scoreboard?limit=5", headers=JSON).json()
|
||||
assert payload["limit"] == 5
|
||||
assert len(payload["scoreboard"]) <= 5
|
||||
|
||||
|
||||
def test_the_limit_is_bounded(app_server, seeded_db):
|
||||
payload = requests.get(f"{BASE_URL}/quizzes/scoreboard?limit=9999", headers=JSON).json()
|
||||
assert payload["limit"] == 100
|
||||
|
||||
|
||||
def test_completing_a_quiz_puts_a_member_on_the_board(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Board entry quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
entry = _entry(player_name)
|
||||
assert entry is not None
|
||||
assert entry["total_points"] == 1.0
|
||||
assert entry["quizzes_completed"] == 1
|
||||
|
||||
|
||||
def test_the_board_projects_users_through_the_safe_schema(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Safe user quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
entry = _entry(player_name)
|
||||
assert "email" not in entry["user"]
|
||||
assert "api_key" not in entry["user"]
|
||||
assert "password_hash" not in entry["user"]
|
||||
|
||||
|
||||
def test_the_board_cannot_be_farmed_by_replaying(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Farming quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
before = _entry(player_name)["total_points"]
|
||||
for _ in range(4):
|
||||
_play(player, slug)
|
||||
assert _entry(player_name)["total_points"] == before
|
||||
|
||||
|
||||
def test_a_worse_replay_never_lowers_the_score(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Worse replay quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug, correct=True)
|
||||
_play(player, slug, correct=False)
|
||||
assert _entry(player_name)["total_points"] == 1.0
|
||||
|
||||
|
||||
def test_a_better_replay_raises_the_score(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Better replay quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug, correct=False)
|
||||
assert _entry(player_name)["total_points"] == 0.0
|
||||
_play(player, slug, correct=True)
|
||||
assert _entry(player_name)["total_points"] == 1.0
|
||||
|
||||
|
||||
def test_an_author_scores_on_their_own_quiz(app_server, seeded_db):
|
||||
owner, owner_name = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Own quiz scoring")
|
||||
_play(owner, slug)
|
||||
entry = _entry(owner_name)
|
||||
assert entry is not None
|
||||
assert entry["total_points"] == 1.0
|
||||
assert entry["quizzes_completed"] == 1
|
||||
|
||||
|
||||
def test_two_quizzes_add_up(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
first = build_published(owner, questions=(NUMERIC,), title="Sum quiz one")
|
||||
second = build_published(owner, questions=(NUMERIC,), title="Sum quiz two")
|
||||
player, player_name = signup()
|
||||
_play(player, first)
|
||||
_play(player, second)
|
||||
entry = _entry(player_name)
|
||||
assert entry["total_points"] == 2.0
|
||||
assert entry["quizzes_completed"] == 2
|
||||
|
||||
|
||||
def test_an_unfinished_attempt_scores_nothing(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Unfinished quiz")
|
||||
player, player_name = signup()
|
||||
start_attempt(player, slug)
|
||||
assert _entry(player_name) is None
|
||||
|
||||
|
||||
def test_the_viewer_standing_is_returned_for_a_member(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Standing quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
payload = player.get(f"{BASE_URL}/quizzes/scoreboard", headers=JSON).json()
|
||||
assert payload["viewer_standing"]["user"]["username"] == player_name
|
||||
|
||||
|
||||
def test_the_viewer_standing_is_absent_for_a_guest(app_server, seeded_db):
|
||||
payload = requests.get(f"{BASE_URL}/quizzes/scoreboard", headers=JSON).json()
|
||||
assert payload["viewer_standing"] is None
|
||||
|
||||
|
||||
def test_the_hub_carries_the_same_board(app_server, seeded_db):
|
||||
from devplacepy.config import QUIZ_SCOREBOARD_LIMIT
|
||||
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Hub board quiz")
|
||||
player, _ = signup()
|
||||
_play(player, slug)
|
||||
hub = requests.get(f"{BASE_URL}/quizzes", headers=JSON).json()["scoreboard"]
|
||||
board = requests.get(
|
||||
f"{BASE_URL}/quizzes/scoreboard?limit={QUIZ_SCOREBOARD_LIMIT}", headers=JSON
|
||||
).json()["scoreboard"]
|
||||
assert len(hub) <= QUIZ_SCOREBOARD_LIMIT
|
||||
assert [entry["user"]["username"] for entry in hub] == [
|
||||
entry["user"]["username"] for entry in board
|
||||
]
|
||||
|
||||
|
||||
def test_the_hub_reports_the_viewer_progress(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Progress quiz")
|
||||
player, _ = signup()
|
||||
_play(player, slug)
|
||||
payload = player.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
||||
assert payload["viewer_progress"]["completed"] >= 1
|
||||
|
||||
|
||||
def test_ranks_are_gapless(app_server, seeded_db):
|
||||
payload = requests.get(f"{BASE_URL}/quizzes/scoreboard?limit=100", headers=JSON).json()
|
||||
ranks = [entry["rank"] for entry in payload["scoreboard"]]
|
||||
assert ranks == list(range(1, len(ranks) + 1))
|
||||
|
||||
|
||||
def test_a_perfect_run_is_counted(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Perfect quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
assert _entry(player_name)["perfect_count"] == 1
|
||||
|
||||
|
||||
def test_completing_your_own_quiz_updates_the_progress_card(app_server, seeded_db):
|
||||
author, author_name = signup()
|
||||
slug = build_published(author, questions=(NUMERIC,), title="Own quiz progress")
|
||||
_play(author, slug)
|
||||
payload = author.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
||||
progress = payload["viewer_progress"]
|
||||
entry = _entry(author_name)
|
||||
assert entry is not None
|
||||
assert progress["completed"] == entry["quizzes_completed"] == 1
|
||||
assert progress["avg_percent"] == entry["avg_percent"] == 100.0
|
||||
assert progress["total_points"] == entry["total_points"] == 1.0
|
||||
assert progress["perfect_count"] == entry["perfect_count"] == 1
|
||||
assert payload["viewer_standing"]["user"]["username"] == author_name
|
||||
|
||||
|
||||
def test_the_author_reaches_the_board_after_their_own_run(app_server, seeded_db):
|
||||
author, author_name = signup()
|
||||
slug = build_published(author, questions=(NUMERIC,), title="Own quiz on the board")
|
||||
assert _entry(author_name) is None
|
||||
_play(author, slug)
|
||||
assert _entry(author_name) is not None
|
||||
standing = author.get(f"{BASE_URL}/quizzes/scoreboard", headers=JSON).json()[
|
||||
"viewer_standing"
|
||||
]
|
||||
assert standing["user"]["username"] == author_name
|
||||
assert standing["quizzes_completed"] == 1
|
||||
|
||||
|
||||
def test_the_progress_card_matches_the_board_for_a_ranked_player(app_server, seeded_db):
|
||||
owner, _ = signup()
|
||||
slug = build_published(owner, questions=(NUMERIC,), title="Ranked progress quiz")
|
||||
player, player_name = signup()
|
||||
_play(player, slug)
|
||||
payload = player.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
||||
progress = payload["viewer_progress"]
|
||||
entry = _entry(player_name)
|
||||
assert progress["completed"] == entry["quizzes_completed"]
|
||||
assert progress["avg_percent"] == entry["avg_percent"]
|
||||
assert progress["total_points"] == entry["total_points"]
|
||||
Reference in New Issue
Block a user