tienhiep-api / backend /test_sects_flow.py
Cong123779
deploy: update backend production to new Space
d9bfc2d
Raw
History Blame Contribute Delete
13 kB
import requests, time, sys, os
BASE_URL = "http://localhost:5051"
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(ROOT_DIR)
from backend.database.db_manager import get_user_db_conn
H = {"X-Bypass-Rate-Limit": "tienhiep_bypass_secret_9988"}
def step(msg): print(f"\n{'='*60}\n👉 {msg}\n{'='*60}")
def ok(msg): print(f" ✅ {msg}")
def info(msg): print(f" 📌 {msg}")
def verify_db(username):
conn = get_user_db_conn()
conn.execute("UPDATE users SET email_verified = 1 WHERE username = ?", (username,))
conn.commit(); conn.close()
class C:
def __init__(self, u, p):
self.u, self.p, self.token, self.h, self.uid = u, p, None, dict(H), None
def register(self):
r = requests.post(f"{BASE_URL}/api/auth/register", json={"username":self.u,"password":self.p,"email":f"{self.u}@test.com"}, headers=self.h)
if r.status_code == 200 or "đã tồn tại" in r.text: verify_db(self.u)
def login(self):
r = requests.post(f"{BASE_URL}/api/auth/login", json={"username":self.u,"password":self.p}, headers=self.h)
assert r.status_code == 200, f"Login fail {self.u}: {r.text}"
d = r.json(); self.token = d.get("access_token") or d.get("token")
self.h = {"Authorization":f"Bearer {self.token}", **H}; self.uid = d.get("user",{}).get("id")
ok(f"Login {self.u} (ID:{self.uid})")
def post(self, ep, data=None): return requests.post(f"{BASE_URL}{ep}", json=data, headers=self.h)
def get(self, ep, params=None): return requests.get(f"{BASE_URL}{ep}", params=params, headers=self.h)
def cleanup():
# Use API-based cleanup - leave all sects via HTTP
for n in ["tst_leader","tst_vice","tst_elder","tst_inner1","tst_inner2","tst_outer"]:
try:
# Login
r = requests.post(f"{BASE_URL}/api/auth/login", json={"username":n,"password":"pw123"}, headers=H)
if r.status_code != 200: continue
tok = r.json().get("access_token") or r.json().get("token")
ah = {"Authorization":f"Bearer {tok}", **H}
# Check if in sect
r = requests.get(f"{BASE_URL}/api/sects/my-sect", headers=ah)
if r.status_code == 200 and r.json().get("in_sect"):
requests.post(f"{BASE_URL}/api/sects/leave", headers=ah)
except: pass
def run():
step("1. TẠO 6 TÀI KHOẢN")
names = ["tst_leader","tst_vice","tst_elder","tst_inner1","tst_inner2","tst_outer"]
us = {n: C(n,"pw123") for n in names}
for c in us.values(): c.register(); c.login()
step("2. DỌN DẸP DB CŨ")
cleanup()
step("3. SÁNG LẬP TÔNG MÔN")
sn = f"TestTong{int(time.time())}"
r = us["tst_leader"].post("/api/sects/create", {"name":sn,"slogan":"Kiếm khí tung hoành","badge":"gold"})
assert r.status_code == 200, f"Create fail: {r.text}"
sid = r.json()["sect_id"]; ok(f"Tông '{sn}' ID={sid}")
step("4. TÌM KIẾM TÔNG MÔN")
r = us["tst_leader"].get("/api/sects/search", {"q":"TestTong"})
assert r.status_code == 200 and r.json()["total"] >= 1
ok(f"Tìm thấy {r.json()['total']} tông môn, trang {r.json()['page']}/{r.json()['total_pages']}")
step("5. XEM TÔNG MÔN THEO ID")
r = us["tst_leader"].get(f"/api/sects/{sid}")
assert r.status_code == 200 and r.json()["sect"]["name"] == sn
ok(f"Xem tông ID={sid}: {r.json()['member_count']} thành viên")
step("6. GIA NHẬP 5 ĐỆ TỬ")
for n in names[1:]:
r = us[n].post("/api/sects/join", {"sect_id":sid})
assert r.status_code == 200, f"{n} join fail: {r.text}"
ok("5 đệ tử gửi đơn bái kiến")
r = us["tst_leader"].get("/api/sects/requests/list")
reqs = r.json()["requests"]; ok(f"{len(reqs)} yêu cầu chờ duyệt")
for rq in reqs:
r = us["tst_leader"].post("/api/sects/requests/respond", {"request_id":rq["id"],"action":"approve"})
assert r.status_code == 200
ok("Duyệt toàn bộ")
step("7. HỆ THỐNG 5 CHỨC DANH")
promotions = [
("tst_vice", "vice_leader", "Phó Tông chủ"),
("tst_elder", "elder", "Trưởng lão"),
("tst_inner1", "inner_disciple", "Nội môn đệ tử"),
("tst_inner2", "inner_disciple", "Nội môn đệ tử"),
]
for uname, role, label in promotions:
r = us["tst_leader"].post("/api/sects/promote/rank", {"user_id":us[uname].uid,"role":role})
assert r.status_code == 200, f"Promote {uname} fail: {r.text}"
ok(f"{uname}{label}")
step("8. KIỂM TRA QUYỀN PHÂN CẤP")
# Vice leader thăng elder cho outer
r = us["tst_vice"].post("/api/sects/promote/rank", {"user_id":us["tst_outer"].uid,"role":"elder"})
assert r.status_code == 200; ok("Phó Tông chủ thăng Trưởng lão cho tst_outer ✓")
# Hạ lại về member
r = us["tst_vice"].post("/api/sects/promote/rank", {"user_id":us["tst_outer"].uid,"role":"member"})
assert r.status_code == 200; ok("Phó Tông chủ hạ tst_outer về Ngoại môn đệ tử ✓")
# Elder KHÔNG THỂ thăng vice_leader
r = us["tst_elder"].post("/api/sects/promote/rank", {"user_id":us["tst_outer"].uid,"role":"vice_leader"})
assert r.status_code == 403; ok("Trưởng lão bị chặn thăng Phó Tông chủ ✓ (403)")
step("9. XEM THÀNH VIÊN & LỌC THEO CHỨC DANH")
r = us["tst_leader"].get("/api/sects/members")
assert r.status_code == 200
for m in r.json()["members"]: info(f"{m['username']} = {m['role_label']} (cống hiến: {m['contribution']})")
r = us["tst_leader"].get("/api/sects/members", {"role":"inner_disciple"})
ok(f"Lọc Nội môn: {len(r.json()['members'])} người")
step("10. CHAT CHUNG TỔNG TÔNG MÔN")
msgs = [
("tst_leader", "Toàn tông nghe lệnh, ngày mai xuất chinh!"),
("tst_vice", "Phó Tông chủ đã sắp xếp đội hình chiến đấu."),
("tst_elder", "Trưởng lão báo cáo: đan dược đã đủ cung ứng."),
("tst_inner1", "Nội môn đệ tử xin tuân lệnh!"),
("tst_outer", "Ngoại môn đệ tử sẵn sàng!"),
]
for u, msg in msgs:
r = us[u].post("/api/sects/chat/send", {"message":msg,"chat_type":"general"})
assert r.status_code == 200
r = us["tst_outer"].get("/api/sects/chat/history", {"chat_type":"general"})
ok(f"Chat chung: {len(r.json()['messages'])} tin nhắn")
for m in r.json()["messages"]: info(f"[{m['sender_name']}]: {m['message']}")
step("11. CHAT RIÊNG 1-1 TRONG TÔNG MÔN")
pairs = [
("tst_leader","tst_vice","Phó Tông chủ, ngày mai phân công thế nào?"),
("tst_vice","tst_leader","Dạ, đệ tử đã chuẩn bị 3 đội tiên phong."),
("tst_inner1","tst_inner2","Đồng môn ơi, cùng luyện công tối nay không?"),
("tst_inner2","tst_inner1","Được chứ, 8 giờ tại Luyện Công Đường nhé!"),
("tst_elder","tst_outer","Ngoại môn đệ tử, tu vi gần đây tiến bộ ra sao?"),
("tst_outer","tst_elder","Dạ bẩm Trưởng lão, đệ tử đang ở Luyện Khí tầng 3."),
]
for s, t, msg in pairs:
r = us[s].post("/api/sects/chat/send", {"message":msg,"chat_type":"direct","target_id":us[t].uid})
assert r.status_code == 200
# Verify
r = us["tst_leader"].get("/api/sects/chat/history", {"chat_type":"direct","target_id":us["tst_vice"].uid})
ok(f"Chat riêng Leader↔Vice: {len(r.json()['messages'])} tin")
r = us["tst_inner1"].get("/api/sects/chat/history", {"chat_type":"direct","target_id":us["tst_inner2"].uid})
ok(f"Chat riêng Inner1↔Inner2: {len(r.json()['messages'])} tin")
r = us["tst_elder"].get("/api/sects/chat/history", {"chat_type":"direct","target_id":us["tst_outer"].uid})
ok(f"Chat riêng Elder↔Outer: {len(r.json()['messages'])} tin")
step("12. TẠO NHÓM CHAT NHỎ #1 (Ban lãnh đạo)")
r = us["tst_leader"].post("/api/sects/chat/groups/create", {
"name":"Ban lãnh đạo","members":[us["tst_leader"].uid, us["tst_vice"].uid, us["tst_elder"].uid]
})
assert r.status_code == 200; g1 = r.json()["group_id"]; ok(f"Nhóm 'Ban lãnh đạo' ID={g1}")
step("13. TẠO NHÓM CHAT NHỎ #2 (Đội tu luyện)")
r = us["tst_inner1"].post("/api/sects/chat/groups/create", {
"name":"Đội tu luyện","members":[us["tst_inner1"].uid, us["tst_inner2"].uid, us["tst_outer"].uid]
})
assert r.status_code == 200; g2 = r.json()["group_id"]; ok(f"Nhóm 'Đội tu luyện' ID={g2}")
step("14. CHAT TRONG NHÓM NHỎ")
for u, msg in [("tst_leader","Hội nghị mật: chiến lược đánh Ma Giáo"),("tst_vice","Đề xuất tấn công từ hướng Đông"),("tst_elder","Đồng ý, trưởng lão sẽ dẫn đội hỗ trợ")]:
r = us[u].post("/api/sects/chat/send", {"message":msg,"chat_type":"group","group_id":g1})
assert r.status_code == 200
for u, msg in [("tst_inner1","Tối nay luyện kiếm pháp mới!"),("tst_inner2","OK sư huynh!"),("tst_outer","Đệ tử cũng xin tham gia!")]:
r = us[u].post("/api/sects/chat/send", {"message":msg,"chat_type":"group","group_id":g2})
assert r.status_code == 200
r = us["tst_leader"].get("/api/sects/chat/history", {"chat_type":"group","group_id":g1})
ok(f"Nhóm BLĐ: {len(r.json()['messages'])} tin")
r = us["tst_inner1"].get("/api/sects/chat/history", {"chat_type":"group","group_id":g2})
ok(f"Nhóm tu luyện: {len(r.json()['messages'])} tin")
step("15. BẢO MẬT: NGƯỜI NGOÀI KHÔNG CHAT ĐƯỢC NHÓM")
r = us["tst_outer"].post("/api/sects/chat/send", {"message":"hack","chat_type":"group","group_id":g1})
assert r.status_code == 403; ok(f"Outer bị chặn chat nhóm BLĐ (403) ✓")
r = us["tst_outer"].get("/api/sects/chat/history", {"chat_type":"group","group_id":g1})
assert r.status_code == 403; ok(f"Outer bị chặn xem lịch sử BLĐ (403) ✓")
step("16. THÊM/XÓA THÀNH VIÊN NHÓM CHAT")
r = us["tst_leader"].post(f"/api/sects/chat/groups/{g1}/members/add", {"user_ids":[us["tst_inner1"].uid]})
assert r.status_code == 200; ok(f"Thêm Inner1 vào BLĐ, members={r.json()['members']}")
r = us["tst_leader"].post(f"/api/sects/chat/groups/{g1}/members/remove", {"user_id":us["tst_inner1"].uid})
assert r.status_code == 200; ok(f"Xóa Inner1 khỏi BLĐ, members={r.json()['members']}")
step("17. XEM THÔNG TIN NHÓM CHAT")
r = us["tst_leader"].get(f"/api/sects/chat/groups/{g1}")
assert r.status_code == 200
ginfo = r.json(); ok(f"Nhóm: {ginfo['group']['name']}, {ginfo['member_count']} thành viên")
for m in ginfo["members"]: info(f" {m['username']} ({m['role_label']})")
step("18. DANH SÁCH TẤT CẢ NHÓM CHAT CỦA TÔI")
r = us["tst_leader"].get("/api/sects/chat/groups")
assert r.status_code == 200; ok(f"Leader thấy {len(r.json()['groups'])} nhóm")
r = us["tst_inner1"].get("/api/sects/chat/groups")
ok(f"Inner1 thấy {len(r.json()['groups'])} nhóm")
step("19. KẾT BẠN & CHAT BẠN BÈ")
conn = get_user_db_conn()
conn.execute("DELETE FROM friendships WHERE user_id IN (?,?) OR friend_id IN (?,?)",
(us["tst_inner1"].uid, us["tst_outer"].uid, us["tst_inner1"].uid, us["tst_outer"].uid))
conn.commit(); conn.close()
r = us["tst_inner1"].post("/api/friends/request", {"friend_username":"tst_outer"})
assert r.status_code == 200; ok("Inner1 gửi kết bạn Outer")
r = us["tst_outer"].post("/api/friends/respond", {"sender_id":us["tst_inner1"].uid,"action":"accept"})
assert r.status_code == 200; ok("Outer chấp nhận")
r = us["tst_inner1"].get("/api/friends/list")
ok(f"Bạn bè Inner1: {[f['username'] for f in r.json()['friends']]}")
r = us["tst_inner1"].post("/api/messages/send", {"receiver_id":us["tst_outer"].uid,"message":"Chào bạn, tối nay đi phụ bản không?"})
assert r.status_code == 200
r = us["tst_outer"].post("/api/messages/send", {"receiver_id":us["tst_inner1"].uid,"message":"Đi chứ! 9h tối nhé!"})
assert r.status_code == 200
r = us["tst_inner1"].get(f"/api/messages/chat/{us['tst_outer'].uid}")
ok(f"Chat bạn bè: {len(r.json()['messages'])} tin nhắn")
for m in r.json()["messages"]:
sn = "tst_inner1" if m["sender_id"]==us["tst_inner1"].uid else "tst_outer"
info(f"[{sn}]: {m['message']}")
step("20. CỐNG HIẾN & XEM TÔNG MÔN QUA ID")
us["tst_inner1"].post("/api/sects/contribute", {"amount":300})
us["tst_outer"].post("/api/sects/contribute", {"amount":200})
r = us["tst_leader"].get(f"/api/sects/{sid}")
d = r.json()
ok(f"Tông '{d['sect']['name']}' cấp {d['sect']['level']}, cống hiến tổng: {d['sect']['contribution']}")
ok(f"Phân bố chức danh: {d['role_counts']}")
step("🎉 TOÀN BỘ 20 BƯỚC TEST THÀNH CÔNG!")
if __name__ == "__main__": run()