mathly / mathly2 /tests /test_database_waits.py
3v324v23's picture
Add specifications for exercise tests, grades, learning path, lesson quizzes, readiness meter, real tests, rewards, simulation tests, skill mastery, smart board, student home, and test countdown
a7ffb22
Raw History Blame Contribute Delete
3.13 kB
"""Few database round trips (fewer-database-calls 1.1): with every store call made to wait, a screen and an answer must still be quick,
because independent reads run at the same time and repeated reads happen once."""
import time
import uuid
import pytest
from fastapi.testclient import TestClient
from mathly2.bank import load_bank
from mathly2.config import Settings
from mathly2.main import create_app
from mathly2.store import InMemoryStore
DELAY = 0.1
KID = "kid@school.test"
H = {"Authorization": f"Bearer dev:{KID}"}
class SlowStore(InMemoryStore):
"""Every public call takes DELAY seconds, like a remote database."""
slow = False
def __getattribute__(self, name):
attr = super().__getattribute__(name)
if callable(attr) and not name.startswith("_") and name != "calls" and super().__getattribute__("slow"):
def waiting(*a, **k):
time.sleep(DELAY)
return attr(*a, **k)
return waiting
return attr
@pytest.fixture(scope="module")
def world():
bank = load_bank()
store = SlowStore()
lessons = [l["id"] for l in bank.lessons][:6]
klass = store.create_class("setup", "A", lessons=lessons, teacher_email="t@school.test")
store.add_member(klass["id"], KID)
from datetime import timedelta
from mathly2 import clock
store.create_test(klass["id"], clock.school_day() + timedelta(days=9), lessons[:2], "t@school.test", created_at=clock.now() - timedelta(days=20))
client = TestClient(create_app(Settings(), bank=bank, store=store))
skill = next(s["id"] for s in bank.skills if s["unit"] in lessons and bank.question_keys(s["id"]))
client.get("/api/me", headers=H) # warm the roles cache, as a signed-in student's later requests are
client.get(f"/api/skills/{skill}", headers=H) # the question is chosen and remembered on the first open
store.slow = True
yield client, bank, skill, store
store.slow = False
def timed(client, method, path, **kw):
start = time.perf_counter()
response = client.request(method, path, headers=H, **kw)
assert response.status_code == 200, response.text
return time.perf_counter() - start
@pytest.mark.parametrize("path", ["/api/home", "/api/path", "/api/grades", "/api/collection"])
def test_a_screen_waits_for_the_database_at_most_three_times_in_a_row(world, path):
client, _, _, _ = world
assert timed(client, "GET", path) < 0.55
def test_opening_a_skill_waits_at_most_three_times_in_a_row(world):
client, _, skill, _ = world
assert timed(client, "GET", f"/api/skills/{skill}") < 0.55
def test_an_answer_waits_at_most_four_times_in_a_row(world):
client, bank, skill, store = world
store.slow = False
key = client.get(f"/api/skills/{skill}", headers=H).json()["question"]["key"]
store.slow = True
assert timed(client, "POST", f"/api/skills/{skill}/answer", json={"question_key": key, "choice": 0}) < 0.75
def test_me_waits_at_most_two_or_three_times(world):
client, _, _, _ = world
assert timed(client, "GET", "/api/me") < 0.55