# 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"]