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