Files
jsahn 4e943700fc feat: add common utilities and feedback handling scripts
- Introduced common.js for shared functions including API calls, error reporting, and UI helpers.
- Implemented feedback.js for the feedback reporting page, including form handling and displaying feedback status.
- Added layout.js for managing galaxy and section layouts in the visualization.
- Created login.js for user authentication with session management.
- Developed search.js for term searching based on user input and synonyms.
- Implemented signup.js for user registration with validation and immediate login.
2026-10-06 23:14:45 +09:00

90 lines
5.2 KiB
Python

"""회원 가입·승인(소유자만, 반 지정)·사례/문제 회원 전용."""
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})