|
# retoor <retoor@molodetz.nl>
|
|
|
|
import requests
|
|
|
|
from tests.api.quizzes._helpers import (
|
|
JSON,
|
|
build_published,
|
|
create_quiz,
|
|
signup,
|
|
)
|
|
from tests.conftest import BASE_URL
|
|
|
|
|
|
def test_the_hub_is_public(app_server, seeded_db):
|
|
response = requests.get(f"{BASE_URL}/quizzes", headers=JSON)
|
|
assert response.status_code == 200
|
|
assert "quizzes" in response.json()
|
|
|
|
|
|
def test_the_hub_renders_html_for_a_browser(app_server, seeded_db):
|
|
response = requests.get(f"{BASE_URL}/quizzes")
|
|
assert response.status_code == 200
|
|
assert "feed-layout" in response.text
|
|
assert "sidebar-card" in response.text
|
|
assert "feed-right" in response.text
|
|
|
|
|
|
def test_the_hub_exposes_the_scoreboard_and_counts(app_server, seeded_db):
|
|
payload = requests.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
|
assert "scoreboard" in payload
|
|
assert "counts" in payload
|
|
assert "pagination" in payload
|
|
|
|
|
|
def test_a_guest_cannot_create(app_server, seeded_db):
|
|
payload = requests.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
|
assert payload["viewer_can_create"] is False
|
|
|
|
|
|
def test_a_member_can_create(app_server, seeded_db):
|
|
session, _ = signup()
|
|
payload = session.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
|
assert payload["viewer_can_create"] is True
|
|
|
|
|
|
def test_creating_a_quiz_returns_the_builder_url(app_server, seeded_db):
|
|
session, _ = signup()
|
|
response = session.post(
|
|
f"{BASE_URL}/quizzes/create",
|
|
data={"title": "My first quiz", "description": "d"},
|
|
headers=JSON,
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()["data"]
|
|
assert data["slug"]
|
|
assert data["url"].endswith("/edit")
|
|
|
|
|
|
def test_creating_a_quiz_requires_auth(app_server, seeded_db):
|
|
response = requests.post(
|
|
f"{BASE_URL}/quizzes/create", data={"title": "Nope"}, headers=JSON
|
|
)
|
|
assert response.status_code in (401, 303)
|
|
|
|
|
|
def test_a_short_title_is_rejected(app_server, seeded_db):
|
|
session, _ = signup()
|
|
response = session.post(f"{BASE_URL}/quizzes/create", data={"title": "no"}, headers=JSON)
|
|
assert response.status_code == 422
|
|
|
|
|
|
def test_a_new_quiz_starts_as_a_draft(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = create_quiz(session)
|
|
payload = session.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).json()
|
|
assert payload["quiz"]["status"] == "draft"
|
|
assert payload["quiz"]["viewer_can_edit"] is True
|
|
|
|
|
|
def test_a_draft_is_hidden_from_a_guest(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = create_quiz(session)
|
|
assert requests.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).status_code == 404
|
|
|
|
|
|
def test_a_draft_is_hidden_from_another_member(app_server, seeded_db):
|
|
owner, _ = signup()
|
|
slug = create_quiz(owner)
|
|
other, _ = signup()
|
|
assert other.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).status_code == 404
|
|
|
|
|
|
def test_the_owner_sees_their_draft(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = create_quiz(session)
|
|
assert session.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).status_code == 200
|
|
|
|
|
|
def test_an_admin_sees_another_members_draft(app_server, seeded_db):
|
|
owner, _ = signup()
|
|
slug = create_quiz(owner)
|
|
admin = requests.Session()
|
|
admin.post(
|
|
f"{BASE_URL}/auth/login",
|
|
data={"email": seeded_db["alice"]["email"], "password": seeded_db["alice"]["password"]},
|
|
allow_redirects=True,
|
|
)
|
|
assert admin.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).status_code == 200
|
|
|
|
|
|
def test_a_published_quiz_is_public(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Public quiz")
|
|
response = requests.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON)
|
|
assert response.status_code == 200
|
|
assert response.json()["quiz"]["status"] == "published"
|
|
|
|
|
|
def test_editing_a_draft_changes_its_settings(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = create_quiz(session)
|
|
session.post(
|
|
f"{BASE_URL}/quizzes/edit/{slug}",
|
|
data={"title": "Renamed quiz", "description": "changed", "pass_percent": "80"},
|
|
headers=JSON,
|
|
)
|
|
payload = session.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).json()
|
|
assert payload["quiz"]["title"] == "Renamed quiz"
|
|
assert payload["quiz"]["pass_percent"] == 80
|
|
|
|
|
|
def test_another_member_cannot_edit(app_server, seeded_db):
|
|
owner, _ = signup()
|
|
slug = build_published(owner, title="Not yours")
|
|
other, _ = signup()
|
|
response = other.post(
|
|
f"{BASE_URL}/quizzes/edit/{slug}", data={"title": "Hijacked"}, headers=JSON
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
def test_the_owner_deletes_their_quiz(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Doomed quiz")
|
|
assert session.post(f"{BASE_URL}/quizzes/delete/{slug}", headers=JSON).status_code == 200
|
|
assert requests.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).status_code == 404
|
|
|
|
|
|
def test_an_admin_deletes_another_members_quiz(app_server, seeded_db):
|
|
owner, _ = signup()
|
|
slug = build_published(owner, title="Moderated quiz")
|
|
admin = requests.Session()
|
|
admin.post(
|
|
f"{BASE_URL}/auth/login",
|
|
data={"email": seeded_db["alice"]["email"], "password": seeded_db["alice"]["password"]},
|
|
allow_redirects=True,
|
|
)
|
|
assert admin.post(f"{BASE_URL}/quizzes/delete/{slug}", headers=JSON).status_code == 200
|
|
|
|
|
|
def test_deleting_cascades_to_the_questions(app_server, seeded_db):
|
|
from devplacepy.database import get_table, refresh_snapshot
|
|
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Cascade quiz")
|
|
refresh_snapshot()
|
|
quiz = get_table("quizzes").find_one(slug=slug)
|
|
session.post(f"{BASE_URL}/quizzes/delete/{slug}", headers=JSON)
|
|
refresh_snapshot()
|
|
for table in ("quiz_questions", "quiz_options"):
|
|
assert get_table(table).count(quiz_uid=quiz["uid"], deleted_at=None) == 0
|
|
|
|
|
|
def test_the_delete_cascade_shares_one_stamp(app_server, seeded_db):
|
|
from devplacepy.database import get_table, refresh_snapshot
|
|
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Stamped quiz")
|
|
attempt_owner, _ = signup()
|
|
attempt_owner.post(f"{BASE_URL}/quizzes/{slug}/attempts", headers=JSON)
|
|
refresh_snapshot()
|
|
quiz = get_table("quizzes").find_one(slug=slug)
|
|
session.post(f"{BASE_URL}/quizzes/delete/{slug}", headers=JSON)
|
|
refresh_snapshot()
|
|
stamps = set()
|
|
for table in ("quiz_questions", "quiz_options", "quiz_attempts", "quiz_answers"):
|
|
for row in get_table(table).find(quiz_uid=quiz["uid"]):
|
|
stamps.add(row["deleted_at"])
|
|
assert len(stamps) == 1
|
|
assert next(iter(stamps))
|
|
|
|
|
|
def test_the_todo_filter_excludes_a_completed_quiz(app_server, seeded_db):
|
|
from tests.api.quizzes._helpers import (
|
|
answer_correctly,
|
|
attempt_state,
|
|
start_attempt,
|
|
)
|
|
|
|
owner, _ = signup()
|
|
slug = build_published(owner, title="Todo filter quiz")
|
|
player, _ = signup()
|
|
before = player.get(f"{BASE_URL}/quizzes?filter=todo", headers=JSON).json()
|
|
assert any(item["slug"] == slug for item in before["quizzes"])
|
|
attempt_uid = start_attempt(player, slug)
|
|
state = attempt_state(player, slug, attempt_uid)
|
|
for question in state["attempt"]["questions"]:
|
|
answer_correctly(player, slug, attempt_uid, question)
|
|
player.post(f"{BASE_URL}/quizzes/{slug}/attempts/{attempt_uid}/finish", headers=JSON)
|
|
after = player.get(f"{BASE_URL}/quizzes?filter=todo", headers=JSON).json()
|
|
assert not any(item["slug"] == slug for item in after["quizzes"])
|
|
done = player.get(f"{BASE_URL}/quizzes?filter=done", headers=JSON).json()
|
|
assert any(item["slug"] == slug for item in done["quizzes"])
|
|
|
|
|
|
def test_the_mine_filter_lists_only_the_viewers_quizzes(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Mine filter quiz")
|
|
payload = session.get(f"{BASE_URL}/quizzes?filter=mine", headers=JSON).json()
|
|
assert all(item["author"]["username"] for item in payload["quizzes"])
|
|
assert any(item["slug"] == slug for item in payload["quizzes"])
|
|
|
|
|
|
def test_the_drafts_filter_lists_only_drafts(app_server, seeded_db):
|
|
session, _ = signup()
|
|
draft_slug = create_quiz(session, "Draft filter quiz")
|
|
published_slug = build_published(session, title="Published filter quiz")
|
|
payload = session.get(f"{BASE_URL}/quizzes?filter=drafts", headers=JSON).json()
|
|
slugs = [item["slug"] for item in payload["quizzes"]]
|
|
assert draft_slug in slugs
|
|
assert published_slug not in slugs
|
|
|
|
|
|
def test_a_guest_gets_no_personal_filters(app_server, seeded_db):
|
|
payload = requests.get(f"{BASE_URL}/quizzes?filter=todo", headers=JSON).json()
|
|
assert payload["quizzes"] == []
|
|
|
|
|
|
def test_search_matches_the_title(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Highly searchable haddock quiz")
|
|
payload = requests.get(f"{BASE_URL}/quizzes?search=haddock", headers=JSON).json()
|
|
assert any(item["slug"] == slug for item in payload["quizzes"])
|
|
|
|
|
|
def test_search_matches_the_author_username(app_server, seeded_db):
|
|
session, name = signup("searchable")
|
|
build_published(session, title="Author search quiz")
|
|
payload = requests.get(f"{BASE_URL}/quizzes?search={name}", headers=JSON).json()
|
|
assert any(item["author"]["username"] == name for item in payload["quizzes"])
|
|
|
|
|
|
def test_an_unknown_filter_falls_back_to_all(app_server, seeded_db):
|
|
payload = requests.get(f"{BASE_URL}/quizzes?filter=nonsense", headers=JSON).json()
|
|
assert payload["filter"] == "all"
|
|
|
|
|
|
def test_the_listing_carries_the_viewer_state(app_server, seeded_db):
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Viewer state quiz")
|
|
other, _ = signup()
|
|
payload = other.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
|
row = next(item for item in payload["quizzes"] if item["slug"] == slug)
|
|
assert row["viewer_state"] == "todo"
|
|
assert row["question_count"] == 2
|
|
assert row["total_points"] == 3
|
|
|
|
|
|
def test_a_quiz_accepts_a_comment_and_counts_it(app_server, seeded_db):
|
|
from devplacepy.database import get_table, refresh_snapshot
|
|
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Commented quiz")
|
|
refresh_snapshot()
|
|
quiz = get_table("quizzes").find_one(slug=slug)
|
|
session.post(
|
|
f"{BASE_URL}/comments/create",
|
|
data={"content": "Nice quiz", "target_uid": quiz["uid"], "target_type": "quiz"},
|
|
headers=JSON,
|
|
)
|
|
payload = requests.get(f"{BASE_URL}/quizzes", headers=JSON).json()
|
|
row = next(item for item in payload["quizzes"] if item["slug"] == slug)
|
|
assert row["comment_count"] == 1
|
|
|
|
|
|
def test_a_quiz_accepts_a_star_vote(app_server, seeded_db):
|
|
from devplacepy.database import get_table, refresh_snapshot
|
|
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Voted quiz")
|
|
refresh_snapshot()
|
|
quiz = get_table("quizzes").find_one(slug=slug)
|
|
voter, _ = signup()
|
|
voter.post(f"{BASE_URL}/votes/quiz/{quiz['uid']}", data={"value": "1"}, headers=JSON)
|
|
payload = requests.get(f"{BASE_URL}/quizzes/{slug}", headers=JSON).json()
|
|
assert payload["star_count"] == 1
|
|
|
|
|
|
def test_the_bare_uid_resolves_like_the_slug(app_server, seeded_db):
|
|
from devplacepy.database import get_table, refresh_snapshot
|
|
|
|
session, _ = signup()
|
|
slug = build_published(session, title="Uid lookup quiz")
|
|
refresh_snapshot()
|
|
quiz = get_table("quizzes").find_one(slug=slug)
|
|
response = requests.get(f"{BASE_URL}/quizzes/{quiz['uid']}", headers=JSON)
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_an_unknown_quiz_is_a_404(app_server, seeded_db):
|
|
assert requests.get(f"{BASE_URL}/quizzes/nope-nope", headers=JSON).status_code == 404
|
|
|
|
|
|
def test_the_new_quiz_page_requires_auth(app_server, seeded_db):
|
|
response = requests.get(f"{BASE_URL}/quizzes/new", headers=JSON, allow_redirects=False)
|
|
assert response.status_code in (401, 303)
|
|
|
|
|
|
def test_the_new_quiz_page_renders_for_a_member(app_server, seeded_db):
|
|
session, _ = signup()
|
|
response = session.get(f"{BASE_URL}/quizzes/new")
|
|
assert response.status_code == 200
|
|
assert "quiz-form" in response.text
|
|
|
|
|
|
def test_the_hub_nav_entry_is_visible_to_a_guest(app_server, seeded_db):
|
|
response = requests.get(f"{BASE_URL}/quizzes")
|
|
assert 'href="/quizzes"' in response.text
|
|
assert "Quizzes" in response.text
|