# retoor import requests from tests.api.quizzes._helpers import ( CHOICE, FREE_TEXT, JSON, NUMERIC, TRUE_FALSE, add_question, create_quiz, signup, ) from tests.conftest import BASE_URL FILL_BLANK = { "kind": "fill_blank", "prompt": "Complete the pragma names", "points": 2, "options": "First blank\nSecond blank", "match_values": "journal_mode\nsynchronous", } ORDERING = { "kind": "ordering", "prompt": "Order the steps", "points": 2, "options": "Open\nRead\nClose", } MATCHING = { "kind": "matching", "prompt": "Match the pragma to its value", "points": 2, "options": "journal_mode\nsynchronous", "match_values": "WAL\nNORMAL", } def _builder(session, slug): return session.get(f"{BASE_URL}/quizzes/{slug}/edit", headers=JSON).json() def test_the_builder_requires_auth(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = requests.get(f"{BASE_URL}/quizzes/{slug}/edit", headers=JSON, allow_redirects=False) assert response.status_code in (401, 303, 404) def test_the_builder_lists_the_question_kinds(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) payload = _builder(session, slug) assert len(payload["kinds"]) == 8 assert {kind["key"] for kind in payload["kinds"]} >= {"single_choice", "free_text"} def test_another_member_cannot_open_the_builder(app_server, seeded_db): owner, _ = signup() slug = create_quiz(owner) other, _ = signup() assert other.get(f"{BASE_URL}/quizzes/{slug}/edit", headers=JSON).status_code == 404 def test_adding_a_single_choice_stores_its_options(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) assert add_question(session, slug, CHOICE).status_code == 200 question = _builder(session, slug)["questions"][0] assert question["kind"] == "single_choice" assert [option["label"] for option in question["options"]] == ["DELETE", "WAL", "MEMORY"] assert [option["is_correct"] for option in question["options"]] == [False, True, False] def test_adding_a_true_false_stores_the_boolean(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, TRUE_FALSE) assert _builder(session, slug)["questions"][0]["correct_boolean"] is True def test_adding_a_numeric_stores_the_value_and_tolerance(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, {**NUMERIC, "numeric_tolerance": "0.5"}) question = _builder(session, slug)["questions"][0] assert question["numeric_value"] == 1024.0 assert question["numeric_tolerance"] == 0.5 def test_adding_a_free_text_stores_the_criteria(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, FREE_TEXT) question = _builder(session, slug)["questions"][0] assert question["expected_answer"].startswith("It keeps") assert question["grading_criteria"].startswith("Accept") def test_adding_a_fill_blank_stores_the_accepted_answers(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, FILL_BLANK) question = _builder(session, slug)["questions"][0] assert [option["match_value"] for option in question["options"]] == [ "journal_mode", "synchronous", ] def test_adding_an_ordering_stores_the_author_order(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, ORDERING) question = _builder(session, slug)["questions"][0] assert [option["label"] for option in question["options"]] == ["Open", "Read", "Close"] def test_adding_a_matching_stores_the_pairs(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, MATCHING) question = _builder(session, slug)["questions"][0] assert [option["match_value"] for option in question["options"]] == ["WAL", "NORMAL"] def test_a_choice_needs_at_least_two_options(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = add_question( session, slug, {**CHOICE, "options": "OnlyOne", "correct_indexes": "0"} ) assert response.status_code == 422 def test_a_single_choice_needs_exactly_one_correct_option(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = add_question(session, slug, {**CHOICE, "correct_indexes": "0,1"}) assert response.status_code == 422 def test_a_free_text_needs_a_reference_or_criteria(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = add_question( session, slug, {"kind": "free_text", "prompt": "Explain", "points": 1} ) assert response.status_code == 422 def test_an_unknown_kind_is_rejected(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = add_question(session, slug, {"kind": "telepathy", "prompt": "Guess"}) assert response.status_code == 422 def test_points_are_bounded(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) assert add_question(session, slug, {**NUMERIC, "points": "0"}).status_code == 422 assert add_question(session, slug, {**NUMERIC, "points": "500"}).status_code == 422 def test_questions_are_appended_in_order(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) add_question(session, slug, NUMERIC) add_question(session, slug, TRUE_FALSE) positions = [question["position"] for question in _builder(session, slug)["questions"]] assert positions == [0, 1, 2] def test_adding_a_question_recomputes_the_totals(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) add_question(session, slug, NUMERIC) quiz = _builder(session, slug)["quiz"] assert quiz["question_count"] == 2 assert quiz["total_points"] == 3 def test_editing_a_question_replaces_its_options(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) question_uid = _builder(session, slug)["questions"][0]["uid"] session.post( f"{BASE_URL}/quizzes/{slug}/questions/{question_uid}", data={**CHOICE, "prompt": "Rewritten", "options": "A\nB", "correct_indexes": "0"}, headers=JSON, ) question = _builder(session, slug)["questions"][0] assert question["prompt"] == "Rewritten" assert [option["label"] for option in question["options"]] == ["A", "B"] def test_editing_a_question_recomputes_the_totals(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) question_uid = _builder(session, slug)["questions"][0]["uid"] session.post( f"{BASE_URL}/quizzes/{slug}/questions/{question_uid}", data={**CHOICE, "points": "9"}, headers=JSON, ) assert _builder(session, slug)["quiz"]["total_points"] == 9 def test_editing_an_unknown_question_is_a_400(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = session.post( f"{BASE_URL}/quizzes/{slug}/questions/nope", data=CHOICE, headers=JSON ) assert response.status_code == 400 def test_deleting_a_question_renumbers_the_rest(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) add_question(session, slug, NUMERIC) first_uid = _builder(session, slug)["questions"][0]["uid"] session.post(f"{BASE_URL}/quizzes/{slug}/questions/{first_uid}/delete", headers=JSON) questions = _builder(session, slug)["questions"] assert len(questions) == 1 assert questions[0]["position"] == 0 def test_deleting_an_unknown_question_is_a_400(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = session.post(f"{BASE_URL}/quizzes/{slug}/questions/nope/delete", headers=JSON) assert response.status_code == 400 def test_reordering_applies_the_new_order(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) add_question(session, slug, NUMERIC) uids = [question["uid"] for question in _builder(session, slug)["questions"]] session.post( f"{BASE_URL}/quizzes/{slug}/questions/reorder", data={"order": ",".join(reversed(uids))}, headers=JSON, ) assert [q["uid"] for q in _builder(session, slug)["questions"]] == list(reversed(uids)) def test_reordering_needs_every_question(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) add_question(session, slug, NUMERIC) uids = [question["uid"] for question in _builder(session, slug)["questions"]] response = session.post( f"{BASE_URL}/quizzes/{slug}/questions/reorder", data={"order": uids[0]}, headers=JSON ) assert response.status_code == 400 def test_reordering_needs_a_non_empty_order(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) add_question(session, slug, CHOICE) response = session.post( f"{BASE_URL}/quizzes/{slug}/questions/reorder", data={"order": ""}, headers=JSON ) assert response.status_code == 422 def test_another_member_cannot_add_a_question(app_server, seeded_db): owner, _ = signup() slug = create_quiz(owner) other, _ = signup() assert add_question(other, slug, CHOICE).status_code == 404 def test_a_guest_cannot_add_a_question(app_server, seeded_db): session, _ = signup() slug = create_quiz(session) response = requests.post( f"{BASE_URL}/quizzes/{slug}/questions", data=CHOICE, headers=JSON ) assert response.status_code in (401, 303)