# retoor <retoor@molodetz.nl>
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)