Spaces:
Running
Running
Download tests/test_social.py from smodusermc/InfinityChat: direct link, hf CLI and curl.
- Browser
- Download file 7.51 kB
-
https://huggingface.co/spaces/smodusermc/InfinityChat/resolve/main/tests/test_social.py
- Command line
-
hf download hf://spaces/smodusermc/InfinityChat/tests/test_social.py
-
curl -L -o test_social.py https://huggingface.co/spaces/smodusermc/InfinityChat/resolve/main/tests/test_social.py
7.51 kB
| #!/usr/bin/env python3 | |
| """Focused end-to-end checks for the social media feature set. | |
| Runs against a live server on 127.0.0.1:8899. Start it with a fresh writable | |
| DATABASE_URL before running (see README/test harness examples). | |
| """ | |
| import asyncio | |
| import json | |
| import os | |
| import sys | |
| import uuid | |
| import httpx | |
| BASE = "http://127.0.0.1:8899" | |
| failures = [] | |
| def check(cond, label, extra=""): | |
| if cond: | |
| print(f" \u2713 {label}") | |
| else: | |
| failures.append(label + (f" \u2014 {extra}" if extra else "")) | |
| print(f" \u2717 {label} {extra}") | |
| async def signup(client, username): | |
| r = await client.post(f"{BASE}/api/auth/signup", params={ | |
| "username": username, | |
| "password": "password123", | |
| "display_name": username.title(), | |
| }) | |
| return r | |
| async def upload_file(client, token, name, data, mime): | |
| params = { | |
| "chunk_index": 0, | |
| "total_chunks": 1, | |
| "file_name": name, | |
| "file_type": mime, | |
| "file_size": len(data), | |
| "upload_id": uuid.uuid4().hex, | |
| } | |
| r = await client.post( | |
| f"{BASE}/api/upload/chunk", | |
| params=params, | |
| files={"file": (name, data, mime)}, | |
| headers={"X-Auth-Token": token}, | |
| ) | |
| if r.status_code != 200: | |
| return None, r | |
| return r.json()["file_path"], r | |
| async def main(): | |
| async with httpx.AsyncClient(timeout=20) as http: | |
| # Unique usernames so the test can be run repeatedly. | |
| u1 = "soc_" + uuid.uuid4().hex[:8] | |
| u2 = "soc_" + uuid.uuid4().hex[:8] | |
| r = await signup(http, u1) | |
| check(r.status_code == 200 and r.json().get("token"), f"signup {u1}", r.text[:160]) | |
| t1 = r.json()["token"] | |
| h1 = {"X-Auth-Token": t1} | |
| r = await signup(http, u2) | |
| check(r.status_code == 200 and r.json().get("token"), f"signup {u2}", r.text[:160]) | |
| t2 = r.json()["token"] | |
| h2 = {"X-Auth-Token": t2} | |
| # --- image upload through the shared chunked endpoint + post create --- | |
| # Minimal 1x1 PNG. | |
| png = ( | |
| b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" | |
| b"\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\xcf\xc0" | |
| b"\x00\x00\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | |
| b"\x00\x00\x00\x00IEND\xaeB`\x82" | |
| ) | |
| fp, up = await upload_file(http, t1, "pic.png", png, "image/png") | |
| check(up.status_code == 200 and fp, "chunked image upload returned a path", up.text[:160]) | |
| media = [{"file_path": fp, "file_name": "pic.png", "file_type": "image/png", "file_size": len(png)}] | |
| r = await http.post( | |
| f"{BASE}/api/social/posts", | |
| params={"body": "Hello social world", "media_json": json.dumps(media)}, | |
| headers=h1, | |
| ) | |
| check(r.status_code == 200 and len(r.json().get("post", {}).get("media", [])) == 1, | |
| "post with image created", r.text[:160]) | |
| post_id = r.json()["post"]["id"] | |
| # --- text attachments are allowed by the shared uploader but rejected on posts --- | |
| txt = b"just some notes" | |
| tfp, up = await upload_file(http, t1, "notes.txt", txt, "text/plain") | |
| check(up.status_code == 200 and tfp, "chunked text upload still works for chat", up.text[:160]) | |
| bad_media = [{"file_path": tfp, "file_name": "notes.txt", "file_type": "text/plain", "file_size": len(txt)}] | |
| r = await http.post( | |
| f"{BASE}/api/social/posts", | |
| params={"body": "should fail", "media_json": json.dumps(bad_media)}, | |
| headers=h1, | |
| ) | |
| check(r.status_code == 400 and "images and videos" in (r.json().get("detail") or ""), | |
| "text media rejected on social posts", r.text[:160]) | |
| # --- edit own post --- | |
| r = await http.patch(f"{BASE}/api/social/posts/{post_id}", params={"body": "Edited body"}, headers=h1) | |
| check(r.status_code == 200 and r.json()["post"]["body"] == "Edited body" | |
| and r.json()["post"].get("edited_at_ms"), | |
| "author can edit own post", r.text[:160]) | |
| # --- non-author cannot edit --- | |
| r = await http.patch(f"{BASE}/api/social/posts/{post_id}", params={"body": "nope"}, headers=h2) | |
| check(r.status_code == 403, "non-author cannot edit", r.text[:160]) | |
| # --- like / repost / bookmark toggles --- | |
| r = await http.post(f"{BASE}/api/social/posts/{post_id}/like", headers=h2) | |
| check(r.status_code == 200 and r.json()["liked"] and r.json()["like_count"] == 1, | |
| "second user can like", r.text[:160]) | |
| r = await http.post(f"{BASE}/api/social/posts/{post_id}/repost", headers=h2) | |
| check(r.status_code == 200 and r.json()["reposted"], "second user can repost", r.text[:160]) | |
| r = await http.get(f"{BASE}/api/social/feed", params={"feed": "home", "limit": 50}, headers=h2) | |
| repost = next((p for p in (r.json().get("posts") or []) | |
| if p["id"] == post_id and p.get("reposter_id")), None) | |
| check(repost is not None and repost.get("reposter", {}).get("username") == u2, | |
| "repost carries reposter info", r.text[:160]) | |
| r = await http.post(f"{BASE}/api/social/posts/{post_id}/bookmark", headers=h2) | |
| check(r.status_code == 200 and r.json()["bookmarked"], "second user can bookmark", r.text[:160]) | |
| # --- search shows follow state truthfully --- | |
| await http.post(f"{BASE}/api/social/users/{u1}/follow", headers=h2) | |
| r = await http.get(f"{BASE}/api/social/search", params={"q": u1}, headers=h2) | |
| hit = next((u for u in (r.json().get("users") or []) if u["username"] == u1), None) | |
| check(r.status_code == 200 and hit and hit["is_following"], | |
| "search reflects follow status", r.text[:160]) | |
| # --- delete own post --- | |
| r = await http.delete(f"{BASE}/api/social/posts/{post_id}", headers=h1) | |
| check(r.status_code == 200, "author can delete post", r.text[:160]) | |
| r = await http.get(f"{BASE}/api/social/posts/{post_id}", headers=h1) | |
| check(r.status_code == 404, "deleted post returns 404", r.text[:160]) | |
| # --- backup admin endpoints are admin-only --- | |
| admin_user = os.environ.get("ADMIN_USERNAME", "").strip() | |
| admin_pass = os.environ.get("ADMIN_PASSWORD", "") | |
| if admin_user and admin_pass: | |
| r = await http.post(f"{BASE}/api/admin/login", json={"username": admin_user, "password": admin_pass}) | |
| check(r.status_code == 200 and r.json().get("token"), "admin login succeeds", r.text[:160]) | |
| admin_headers = {"X-Admin-Token": r.json()["token"]} | |
| r = await http.get(f"{BASE}/api/admin/backups", headers=admin_headers) | |
| check(r.status_code == 200, "admin backup list reachable", r.text[:160]) | |
| r = await http.get(f"{BASE}/api/admin/backups", headers=h1) | |
| check(r.status_code == 401, "normal user cannot access admin backups", r.text[:160]) | |
| else: | |
| r = await http.get(f"{BASE}/api/admin/backups", headers=h1) | |
| check(r.status_code == 401, "backup admin requires dedicated admin auth", r.text[:160]) | |
| print(f"\nfinished. failures={len(failures)}") | |
| print("ALL SOCIAL CHECKS PASSED" if not failures else "SOCIAL CHECKS FAILED") | |
| return failures | |
| if __name__ == "__main__": | |
| fails = asyncio.run(main()) | |
| if fails: | |
| for f in fails: | |
| print(" -", f) | |
| sys.exit(1) | |
| sys.exit(0) |