|
# 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)
|