"""회원 가입·승인(소유자만, 반 지정)·사례/문제 회원 전용.""" from fastapi.testclient import TestClient def _term_with_example(client): d = client.get("/api/atlas").json() for t in d["terms"]: if t["e"]: r = client.get(f"/api/terms/{t['i']}").json() if r.get("example_locked"): return t["i"] raise AssertionError("사례가 있는 용어가 없음") def test_member_flow(client): from server.app import app # 비회원: 사례 잠김, 문제 불가 tid = _term_with_example(client) assert client.get(f"/api/members/questions?term={tid}").status_code == 401 # 가입 코드 없이는 가입 불가 assert client.post("/api/members/signup", json={"code": "NOPE", "username": "stud1", "password": "secret1", "name": "김학생"}).status_code == 400 with TestClient(app) as own: own.post("/api/admin/login", json={"username": "professor", "password": "owner-pass"}) assert own.post("/api/admin/signup-codes", json={"max_uses": 2}).status_code == 400 # 반 없는 코드 불가 cid = own.post("/api/admin/classes", json={"name": "1반"}).json()["class_id"] code = own.post("/api/admin/signup-codes", json={"class_id": cid, "max_uses": 2}).json()["code"] # 코드로 가입하면 바로 회원 (승인 대기 없음) r = client.post("/api/members/signup", json={"code": code.lower(), "username": "stud1", "password": "secret1", "name": "김학생"}) assert r.json()["status"] == "approved" assert client.post("/api/members/signup", json={"code": code, "username": "stud1", "password": "secret1", "name": "x"}).status_code == 409 client.post("/api/members/login", json={"username": "stud1", "password": "secret1"}) # 교수자 비밀번호가 아니면 관리 화면에 들어갈 수 없음 with TestClient(app) as rev: assert rev.post("/api/admin/login", json={"username": "professor", "password": "test-pass"}).status_code == 401 assert rev.get("/api/admin/members").status_code == 401 # 승인 후: 사례·문제 me = client.get("/api/members/me").json() assert me["status"] == "approved" and me["class_name"] == "1반" and me["can_view"] assert client.get(f"/api/terms/{tid}").json()["example"] qs = client.get(f"/api/members/questions?term={tid}").json()["questions"] assert qs and all("answer" not in q for q in qs) # 정답은 내려보내지 않음 q = qs[0] res = client.post("/api/members/answer", json={"token": q["token"], "choice": 0}).json() assert "correct" in res and "answer" in res quiz = client.get("/api/members/quiz?galaxy=g6&n=5").json()["questions"] assert len(quiz) == 5 assert client.get("/api/members/me").json()["attempts"] == 1 # 위조 토큰 거부 assert client.post("/api/members/answer", json={"token": q["token"] + "x", "choice": 0}).status_code == 400 with TestClient(app) as own: own.post("/api/admin/login", json={"username": "professor", "password": "owner-pass"}) cls = own.get("/api/admin/classes").json()["items"][0] assert cls["members"] == 1 and cls["attempts"] == 1 assert own.delete(f"/api/admin/classes/{cls['class_id']}").status_code == 409 def test_manual_question(admin): _, tid = admin.get("/api/atlas").json(), None tid = admin.get("/api/atlas").json()["terms"][0]["i"] r = admin.post("/api/admin/questions", json={"term_id": tid, "qtype": "ox", "prompt": "맞을까요?", "answer": 0, "review_status": "confirmed"}) qid = r.json()["question_id"] qs = admin.get(f"/api/members/questions?term={tid}").json()["questions"] # 관리자는 볼 수 있음 mq = next(q for q in qs if q["origin"] == "manual") assert mq["choices"] == ["O", "X"] assert admin.post("/api/members/answer", json={"token": mq["token"], "choice": 0}).json()["correct"] assert admin.post("/api/admin/questions", json={"qtype": "mcq", "prompt": "x?", "choices": ["a"], "answer": 0}).status_code == 400 assert admin.delete(f"/api/admin/questions/{qid}").json()["ok"] def test_signup_code_rules(owner): c = owner cid = c.post("/api/admin/classes", json={"name": "B반"}).json()["class_id"] r = c.post("/api/admin/signup-codes", json={"code": "class-b", "class_id": cid, "max_uses": 1}).json() assert r["code"] == "CLASS-B" from fastapi.testclient import TestClient from server.app import app with TestClient(app) as s: r = s.post("/api/members/signup", json={"code": "class-b", "username": "autostud", "password": "secret1", "name": "자동"}) assert r.json()["status"] == "approved" assert s.post("/api/members/signup", json={"code": "CLASS-B", "username": "late", "password": "secret1", "name": "x"}).status_code == 400 # 횟수 초과 items = c.get("/api/admin/members?status=approved").json()["items"] u = next(x for x in items if x["username"] == "autostud") assert u["class_name"] == "B반" and u["signup_code"] == "CLASS-B" d = c.get(f"/api/admin/members/{u['user_id']}").json() assert "password_hash" not in d and "by_galaxy" in d code_id = c.get("/api/admin/signup-codes").json()["items"][0]["code_id"] c.put(f"/api/admin/signup-codes/{code_id}", json={"class_id": cid, "active": False})