""" yearly_targets 写权限收紧 — 冒烟测试脚本 幂等,可重复跑。 确保后端已启动: uvicorn app.main:app --reload 12 步覆盖: 1. zhipianren POST → 201 2. zebian POST → 201 3. biandao POST → 403 4. 未登录 POST → 401 5. zhipianren PATCH → 200 6. zebian PATCH → 200 7. biandao PATCH → 403 8. zhipianren DELETE → 200 9. zebian DELETE → 403 10. biandao DELETE → 403 11. biandao GET 列表 → 200 12. zhipianren GET 列表 → 200 """ import sys from pathlib import Path backend_dir = Path(__file__).resolve().parent.parent sys.path.insert(0, str(backend_dir)) import httpx BASE = "http://localhost:8000" HEADERS = {"Content-Type": "application/json"} def login(username: str, password: str) -> str: """登录并返回 session cookie。""" r = httpx.post(f"{BASE}/api/auth/login", json={"username": username, "password": password}) r.raise_for_status() return r.cookies.get("milsci_session", "") def step(name: str, expected_status: int, method: str, url: str, **kwargs): """执行一个 HTTP 步骤,比较实际状态码与期望值。""" cookies = kwargs.pop("cookies", {}) if method == "GET": r = httpx.get(url, cookies=cookies, timeout=10) elif method == "POST": r = httpx.post(url, cookies=cookies, headers=HEADERS, timeout=10, **kwargs) elif method == "PATCH": r = httpx.patch(url, cookies=cookies, headers=HEADERS, timeout=10, **kwargs) elif method == "DELETE": r = httpx.delete(url, cookies=cookies, timeout=10) else: raise ValueError(method) status_ok = r.status_code == expected_status mark = "[OK]" if status_ok else f"[FAIL expected {expected_status} got {r.status_code}]" print(f" {mark} [{method}] {name}") if not status_ok: print(f" Response: {r.text[:300]}") return r, status_ok def ensure_user(cookies: dict, username: str, display_name: str, role: str, password: str = "Test1234"): """幂等创建测试账号(如果不存在)。""" # 先尝试登录(账号可能已存在) try: r = httpx.get(f"{BASE}/api/users/me", cookies=cookies, timeout=5) if r.status_code == 200: me = r.json() if me.get("username") == username: print(f" [SKIP] user {username} already exists, reusing") return except Exception: pass # 尝试创建 r = httpx.post( f"{BASE}/api/users", json={"username": username, "display_name": display_name, "password": password, "role": role}, cookies=cookies, headers=HEADERS, timeout=10, ) if r.status_code == 201: print(f" [OK] created {username} ({role})") elif r.status_code == 409: print(f" [SKIP] {username} already exists") else: print(f" [WARN] create {username} returned {r.status_code}: {r.text[:100]}") def cleanup_test_target(cookies: dict): """清理测试用年度目标(2027),幂等。""" try: r = httpx.get(f"{BASE}/api/yearly_targets", cookies=cookies, timeout=10) if r.status_code == 200: for t in r.json(): if t.get("year") == 2027: httpx.delete(f"{BASE}/api/yearly_targets/{t['id']}", cookies=cookies, timeout=10) print(f" [CLEAN] removed existing 2027 target ID={t['id']}") except Exception: pass def main(): print("=" * 60) print(" yearly_targets 写权限收紧 — 冒烟测试") print("=" * 60) # Step 0: 用 zhipianren 登录,准备测试账号 print("\n[Step 0] Login as zhipianren and ensure test accounts exist") try: admin_cookie = login("simonkoson", "liutong65") except Exception as e: print(f" [FAIL] cannot login as simonkoson: {e}") return admin_cookies = {"milsci_session": admin_cookie} print(" [OK] logged in as simonkoson (zhipianren)") # 幂等创建 zebian / biandao 测试账号 ensure_user(admin_cookies, "test_zebian_smoke", "TestZebra Smoke", "zebian") ensure_user(admin_cookies, "test_biandao_smoke", "TestBiandao Smoke", "biandao") # 登录 zebian 和 biandao 获取 cookie print("\n[Step 0b] Login as zebian and biandao") zebian_cookie = login("test_zebian_smoke", "Test1234") zebian_cookies = {"milsci_session": zebian_cookie} print(" [OK] logged in as test_zebian_smoke (zebian)") biandao_cookie = login("test_biandao_smoke", "Test1234") biandao_cookies = {"milsci_session": biandao_cookie} print(" [OK] logged in as test_biandao_smoke (biandao)") # 清理可能残留的 2027 目标 cleanup_test_target(admin_cookies) results = [] # ===================================================================== # Step 1: zhipianren POST → 201 # ===================================================================== print("\n[Step 1] POST new yearly target — zhipianren → 201") r, ok = step( "POST /api/yearly_targets (zhipianren)", 201, "POST", f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, ) target_id = r.json().get("id") if ok else None results.append(("Step 1: zhipianren POST 201", ok)) # ===================================================================== # Step 2: zebian POST → 201 # ===================================================================== print("\n[Step 2] POST new yearly target — zebian → 201") # 清理 2027(刚被 zhipianren 创建了) cleanup_test_target(admin_cookies) r, ok = step( "POST /api/yearly_targets (zebian)", 201, "POST", f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=zebian_cookies, ) target_id2 = r.json().get("id") if ok else None results.append(("Step 2: zebian POST 201", ok)) # ===================================================================== # Step 3: biandao POST → 403 # ===================================================================== print("\n[Step 3] POST new yearly target — biandao → 403") cleanup_test_target(admin_cookies) r, ok = step( "POST /api/yearly_targets (biandao)", 403, "POST", f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=biandao_cookies, ) results.append(("Step 3: biandao POST 403", ok)) # ===================================================================== # Step 4: 未登录 POST → 401 # ===================================================================== print("\n[Step 4] POST new yearly target — not logged in → 401") r, ok = step( "POST /api/yearly_targets (no auth)", 401, "POST", f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies={}, ) results.append(("Step 4: no auth POST 401", ok)) # ===================================================================== # Step 5: zhipianren PATCH → 200 # ===================================================================== print("\n[Step 5] PATCH yearly target — zhipianren → 200") # 先用 admin 建一个可 PATCH 的目标 cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) patch_id = r.json().get("id") if r.status_code == 201 else None if patch_id: r, ok = step( "PATCH /api/yearly_targets/{id} (zhipianren)", 200, "PATCH", f"{BASE}/api/yearly_targets/{patch_id}", json={"base_target": 0.67}, cookies=admin_cookies, ) else: ok = False print(" [FAIL] could not create target for PATCH test") results.append(("Step 5: zhipianren PATCH 200", ok)) # ===================================================================== # Step 6: zebian PATCH → 200 # ===================================================================== print("\n[Step 6] PATCH yearly target — zebian → 200") cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) patch_id2 = r.json().get("id") if r.status_code == 201 else None if patch_id2: r, ok = step( "PATCH /api/yearly_targets/{id} (zebian)", 200, "PATCH", f"{BASE}/api/yearly_targets/{patch_id2}", json={"stretch_target": 0.92}, cookies=zebian_cookies, ) else: ok = False print(" [FAIL] could not create target for PATCH test") results.append(("Step 6: zebian PATCH 200", ok)) # ===================================================================== # Step 7: biandao PATCH → 403 # ===================================================================== print("\n[Step 7] PATCH yearly target — biandao → 403") cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) patch_id3 = r.json().get("id") if r.status_code == 201 else None if patch_id3: r, ok = step( "PATCH /api/yearly_targets/{id} (biandao)", 403, "PATCH", f"{BASE}/api/yearly_targets/{patch_id3}", json={"base_target": 0.68}, cookies=biandao_cookies, ) else: ok = False print(" [FAIL] could not create target for PATCH test") results.append(("Step 7: biandao PATCH 403", ok)) # ===================================================================== # Step 8: zhipianren DELETE → 200 # ===================================================================== print("\n[Step 8] DELETE yearly target — zhipianren → 200") cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) del_id = r.json().get("id") if r.status_code == 201 else None if del_id: r, ok = step( "DELETE /api/yearly_targets/{id} (zhipianren)", 204, "DELETE", f"{BASE}/api/yearly_targets/{del_id}", cookies=admin_cookies, ) else: ok = False print(" [FAIL] could not create target for DELETE test") results.append(("Step 8: zhipianren DELETE 204", ok)) # ===================================================================== # Step 9: zebian DELETE → 403 # ===================================================================== print("\n[Step 9] DELETE yearly target — zebian → 403") cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) del_id2 = r.json().get("id") if r.status_code == 201 else None if del_id2: r, ok = step( "DELETE /api/yearly_targets/{id} (zebian)", 403, "DELETE", f"{BASE}/api/yearly_targets/{del_id2}", cookies=zebian_cookies, ) else: ok = False print(" [FAIL] could not create target for DELETE test") results.append(("Step 9: zebian DELETE 403", ok)) # ===================================================================== # Step 10: biandao DELETE → 403 # ===================================================================== print("\n[Step 10] DELETE yearly target — biandao → 403") cleanup_test_target(admin_cookies) r = httpx.post( f"{BASE}/api/yearly_targets", json={"year": 2027, "base_target": 0.66, "stretch_target": 0.91}, cookies=admin_cookies, headers=HEADERS, timeout=10, ) del_id3 = r.json().get("id") if r.status_code == 201 else None if del_id3: r, ok = step( "DELETE /api/yearly_targets/{id} (biandao)", 403, "DELETE", f"{BASE}/api/yearly_targets/{del_id3}", cookies=biandao_cookies, ) else: ok = False print(" [FAIL] could not create target for DELETE test") results.append(("Step 10: biandao DELETE 403", ok)) # ===================================================================== # Step 11: biandao GET 列表 → 200 # ===================================================================== print("\n[Step 11] GET yearly targets list — biandao → 200") r, ok = step( "GET /api/yearly_targets (biandao)", 200, "GET", f"{BASE}/api/yearly_targets", cookies=biandao_cookies, ) results.append(("Step 11: biandao GET 200", ok)) # ===================================================================== # Step 12: zhipianren GET 列表 → 200 # ===================================================================== print("\n[Step 12] GET yearly targets list — zhipianren → 200") r, ok = step( "GET /api/yearly_targets (zhipianren)", 200, "GET", f"{BASE}/api/yearly_targets", cookies=admin_cookies, ) results.append(("Step 12: zhipianren GET 200", ok)) # ===================================================================== # Summary # ===================================================================== print("\n" + "=" * 60) print(" 结果汇总") print("=" * 60) passed = sum(1 for _, ok in results if ok) total = len(results) for name, ok in results: mark = "[OK]" if ok else "[FAIL]" print(f" {mark} {name}") print(f"\n PASS: {passed}/{total}") if passed == total: print(" ALL PASS") else: print(" SOME FAILURES") print("\n测试账号已保留(供后续手动清理):") print(" - test_zebian_smoke (zebian)") print(" - test_biandao_smoke (biandao)") print("\n" + "=" * 60) if __name__ == "__main__": main()