"""End-to-end test of roles, tenancy and levels with Flask's test client against a throwaway database.   python tools/smoke_test.py"""
import os, sys, tempfile
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT); os.chdir(ROOT)
os.environ["DB_PATH"] = os.path.join(tempfile.mkdtemp(), "test.db")
os.environ.update(ADMIN_USERNAME="admin", ADMIN_PASSWORD="admin-pass-1", KID_USERNAME="yaseen", KID_PASSWORD="kid123", KID_DISPLAY_NAME="Yaseen",
                  PARENT_USERNAME="parent", PARENT_PASSWORD="parent123", PARENT_DISPLAY_NAME="Parent", BASE_PATH="")
import app as A
A.init_app()
app = A.app


def client(user, pw):
    c = app.test_client()
    r = c.post("/api/login", json=dict(username=user, password=pw)).get_json()
    assert "error" not in r, (user, r)
    return c, r


ok = lambda r: (r.status_code == 200 and r.get_json().get("ok")) or (_ for _ in ()).throw(AssertionError(r.get_json()))
anon = app.test_client()
assert anon.get("/").status_code == 302 and anon.get("/api/me").status_code == 401
assert "error" in anon.post("/api/login", json=dict(username="yaseen", password="nope")).get_json()
adm, r = client("admin", "admin-pass-1"); assert r["home"].endswith("/admin")
par1, r = client("parent", "parent123")
kid1, r = client("yaseen", "kid123")
# role walls
assert kid1.get("/api/kids").status_code == 403 and kid1.get("/admin").status_code == 302
assert par1.get("/api/admin/parents").status_code == 403 and adm.get("/admin").status_code == 200 and par1.get("/").status_code == 200
# the kid studies level 2
me = kid1.get("/api/me").get_json(); assert me["course_level"] == 2, me
cur = kid1.get("/api/curriculum").get_json(); assert cur and all(u["level"] == 2 for u in cur)
act = cur[0]["chapters"][0]["sections"][0]["activities"][0]
assert kid1.get(f"/api/activity/{act['id']}").status_code == 200
assert kid1.post(f"/api/activity/{act['id']}/submit", json=dict(score=100, points_earned=9999, answers={})).get_json()["points_earned"] <= act["points"] + 10
# admin: a second family limited to 1 kid
r = adm.post("/api/admin/parents", json=dict(username="mona", display_name="Mona", max_kids=1)).get_json(); assert r["ok"]
p2_id, p2_pw = r["parent"]["id"], r["password"]
par2, _ = client("mona", p2_pw)
r = par2.post("/api/kids", json=dict(username="sara", display_name="Sara")).get_json(); assert r["ok"] and r["kid"]["course_level"] == 2, r
sara_id, sara_pw = r["kid"]["id"], r["password"]
assert "error" in par2.post("/api/kids", json=dict(username="omar", display_name="Omar")).get_json(), "limit"
ok(adm.post(f"/api/admin/parents/{p2_id}", json=dict(max_kids=2)))
r = adm.post("/api/kids", json=dict(parent_id=p2_id, username="omar", display_name="Omar", password="omar-pass", course_level=2)).get_json(); assert r["ok"], r
# levels: 1..12 listed, only level 2 can be assigned for now
lv = par2.get("/api/kids").get_json()["levels"]; assert [l["id"] for l in lv] == list(range(1, 13)) and [l["available"] for l in lv].count(True) == 1
assert "error" in par2.post(f"/api/kids/{sara_id}", json=dict(course_level=3)).get_json()
assert "error" in adm.post("/api/kids", json=dict(parent_id=p2_id, username="x9", display_name="X", course_level=13)).get_json()
# tenancy
yaseen_id = par1.get("/api/kids").get_json()["kids"][0]["id"]
assert par1.post(f"/api/kids/{sara_id}", json=dict(display_name="X")).status_code == 403
assert par1.post(f"/api/kid/{sara_id}/reset", json={}).status_code == 403
assert par1.post("/api/parent/reset", json=dict(kid_id=sara_id)).status_code == 403
assert all(k["id"] != sara_id for k in par1.get("/api/parent/dashboard").get_json()["kids"])
assert [k["id"] for k in par1.get("/api/parent/dashboard").get_json()["kids"]] == [yaseen_id]
assert [k["username"] for k in adm.get(f"/api/parent/dashboard?parent={p2_id}").get_json()["kids"]] == ["omar", "sara"]
assert par1.get("/report?kid=1").status_code == 200 and kid1.get("/report").status_code == 302
# the parent sees, resets; passwords; suspension
d = par1.get("/api/parent/dashboard").get_json()["kids"][0]; assert d["total_attempts"] == 1, d
ok(par1.post(f"/api/kid/{yaseen_id}/reset", json={}))
assert par1.get("/api/parent/dashboard").get_json()["kids"][0]["total_attempts"] == 0
r = par1.post(f"/api/kids/{yaseen_id}/password", json={}).get_json(); assert r["ok"]
assert "error" in app.test_client().post("/api/login", json=dict(username="yaseen", password="kid123")).get_json()
client("yaseen", r["password"])
sara, _ = client("sara", sara_pw)
ok(adm.post(f"/api/admin/parents/{p2_id}", json=dict(active=False)))
assert sara.get("/api/me").status_code == 401, "suspended parent blocks its kids"
ok(adm.post(f"/api/admin/parents/{p2_id}", json=dict(active=True)))
# a restart keeps progress (activity ids are stable)
sara, _ = client("sara", sara_pw)
a2 = sara.get("/api/curriculum").get_json()[1]["chapters"][0]["sections"][0]["activities"][0]
sara.post(f"/api/activity/{a2['id']}/submit", json=dict(score=80, points_earned=10, answers={}))
A.init_app()
assert adm.get(f"/api/parent/dashboard?parent={p2_id}").get_json()["kids"][1]["total_attempts"] == 1, "progress survives a restart"
# deleting a family removes its kids
assert adm.post(f"/api/admin/parents/{p2_id}/delete", json={}).get_json()["deleted"] == 3
s = adm.get("/api/admin/summary").get_json(); assert s["parents"] == 1 and s["kids"] == 1, s
print("ALL SMOKE TESTS PASSED")
