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