import requests import json import uuid from datetime import datetime from typing import Dict, Any, Optional, List # Configuration BASE_URL: str = "https://devrant.com/api" RESULTS_FILE: str = "api_test_results.json" APP: int = 3 PLAT: int = 3 GUID: str = str(uuid.uuid4()) SID: str = str(int(datetime.now().timestamp())) SEID: str = str(uuid.uuid4()) # Real credentials for login (using provided username/email and password) LOGIN_USERNAME: str = "power-to@the-puff.com" LOGIN_PASSWORD: str = "powerpuffgirl" # Variables to store auth after login AUTH_TOKEN_ID: Optional[str] = None AUTH_TOKEN_KEY: Optional[str] = None AUTH_USER_ID: Optional[str] = None # Mock/fallback values (overridden after login or fetch) TEST_EMAIL: str = "test@example.com" TEST_USERNAME: str = "testuser" + str(int(datetime.now().timestamp())) # Make unique for registration TEST_PASSWORD: str = "Test1234!" TEST_RANT_ID: str = "1" # Will be overridden with real one TEST_COMMENT_ID: str = "1" # Will be overridden with real one TEST_NEWS_ID: str = "1" # Assuming this might work; adjust if needed # Initialize results results: List[Dict[str, Any]] = [] def save_results() -> None: """Save the accumulated test results to JSON file.""" with open(RESULTS_FILE, 'w') as f: json.dump(results, f, indent=2) def test_endpoint(method: str, url: str, params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, files: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]: """ Execute an API request and record the result. Payload: - method: HTTP method (GET, POST, DELETE, etc.) - url: Full API URL - params: Query parameters (dict) - data: POST/PUT body (dict) - files: Multipart files (dict) - headers: Custom headers (dict) Response: - Returns a dict with url, method, status_code, response (JSON or error), headers, request_body, timestamp """ try: response = requests.request(method, url, params=params, data=data, files=files, headers=headers) result: Dict[str, Any] = { "url": response.url, "method": method, "status_code": response.status_code, "response": response.json() if response.content else {}, "headers": dict(response.headers), "request_body": data or params or {}, "timestamp": datetime.now().isoformat() } results.append(result) return result except Exception as e: result: Dict[str, Any] = { "url": url, "method": method, "status_code": None, "response": {"error": str(e)}, "headers": {}, "request_body": data or params or {}, "timestamp": datetime.now().isoformat() } results.append(result) return result # Helper to patch auth into params/data def patch_auth(base_dict: Dict[str, Any]) -> Dict[str, Any]: """ Add authentication to request dict if available. Payload: base_dict (params or data) Response: Updated dict with app, user_id, token_id, token_key if auth present """ auth_dict: Dict[str, Any] = {"app": APP} if AUTH_USER_ID and AUTH_TOKEN_ID and AUTH_TOKEN_KEY: auth_dict.update({ "user_id": AUTH_USER_ID, "token_id": AUTH_TOKEN_ID, "token_key": AUTH_TOKEN_KEY }) base_dict.update(auth_dict) return base_dict # Login function to get real auth tokens def login_user() -> bool: """ Perform login to obtain real auth tokens. Payload: POST to /users/auth-token with username, password, app Response: success (bool), sets global AUTH_* variables on success """ params: Dict[str, Any] = { "username": LOGIN_USERNAME, "password": LOGIN_PASSWORD, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } result = test_endpoint("POST", f"{BASE_URL}/users/auth-token", data=patch_auth(params)) if result["status_code"] == 200 and result.get("response", {}).get("success"): auth_token = result["response"].get("auth_token", {}) global AUTH_USER_ID, AUTH_TOKEN_ID, AUTH_TOKEN_KEY AUTH_USER_ID = str(auth_token.get("user_id", "")) AUTH_TOKEN_ID = str(auth_token.get("id", "")) AUTH_TOKEN_KEY = auth_token.get("key", "") return True return False # Fetch a real rant_id from feed def fetch_real_rant_id() -> Optional[str]: """ Fetch rants feed to get a real rant_id. Payload: GET to /devrant/rants with auth Response: First rant_id if success, else None """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } result = test_endpoint("GET", f"{BASE_URL}/devrant/rants", params=patch_auth(params)) if result["status_code"] == 200 and result.get("response", {}).get("success"): rants = result["response"].get("rants", []) if rants: return str(rants[0]["id"]) return None # Post a test rant and return its id def post_test_rant() -> Optional[str]: """ Post a test rant to get a real rant_id for further tests. Payload: POST to /devrant/rants with rant content, tags, auth Response: rant_id if success, else None """ data: Dict[str, Any] = { "rant": "Test rant for API testing (ignore)", "tags": "test,api", "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } result = test_endpoint("POST", f"{BASE_URL}/devrant/rants", data=patch_auth(data)) if result["status_code"] == 200 and result.get("response", {}).get("success"): return str(result["response"].get("rant_id", "")) return None # Post a test comment and return its id def post_test_comment(rant_id: str) -> Optional[str]: """ Post a test comment to get a real comment_id. Payload: POST to /devrant/rants/{rant_id}/comments with comment, auth Response: comment_id if success, else None """ data: Dict[str, Any] = { "comment": "Test comment for API testing (ignore)", "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } result = test_endpoint("POST", f"{BASE_URL}/devrant/rants/{rant_id}/comments", data=patch_auth(data)) if result["status_code"] == 200 and result.get("response", {}).get("success"): return str(result["response"].get("comment_id", "")) return None # Test cases with docstrings def test_register_user() -> None: """ Test user registration (valid and invalid). Payload (valid): POST /users with email, username, password, type=1, plat, guid, sid, seid, app Expected: success=true if unique, else error Payload (invalid): Missing email Expected: error on email field """ params: Dict[str, Any] = { "type": 1, "email": TEST_EMAIL, "username": TEST_USERNAME, "password": TEST_PASSWORD, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users", data=patch_auth(params.copy())) invalid_params = params.copy() del invalid_params["email"] test_endpoint("POST", f"{BASE_URL}/users", data=patch_auth(invalid_params)) def test_login_user() -> None: """ Test user login (valid and invalid). Already done in login_user(), but record here. Payload (valid): POST /users/auth-token with username, password, plat, guid, sid, seid, app Expected: success=true, auth_token Payload (invalid): Wrong password Expected: error=Invalid login credentials """ # Valid is handled in login_user(); here test invalid params: Dict[str, Any] = { "username": TEST_USERNAME, "password": "WrongPass", "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users/auth-token", data=patch_auth(params)) def test_edit_profile() -> None: """ Test editing user profile. Payload: POST /users/me/edit-profile with profile fields, plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "profile_about": "Test bio", "profile_skills": "Python, JS", "profile_location": "Test City", "profile_website": "http://example.com", "profile_github": "testuser", "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users/me/edit-profile", data=patch_auth(params)) def test_forgot_password() -> None: """ Test forgot password. Payload: POST /users/forgot-password with username, plat, guid, sid, seid, app Expected: success=true (even without auth) """ params: Dict[str, Any] = { "username": LOGIN_USERNAME, # Use real one "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users/forgot-password", data=patch_auth(params)) def test_resend_confirm() -> None: """ Test resend confirmation email. Payload: POST /users/me/resend-confirm with plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users/me/resend-confirm", data=patch_auth(params)) def test_delete_account() -> None: """ Test delete account (caution: irreversible). Payload: DELETE /users/me with plat, guid, sid, seid, auth Expected: success=true """ # Comment out to avoid accidental deletion # params: Dict[str, Any] = { # "plat": PLAT, # "guid": GUID, # "sid": SID, # "seid": SEID # } # test_endpoint("DELETE", f"{BASE_URL}/users/me", params=patch_auth(params)) pass def test_mark_news_read() -> None: """ Test mark news as read. Payload: POST /users/me/mark-news-read with news_id, plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "news_id": TEST_NEWS_ID, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/users/me/mark-news-read", data=patch_auth(params)) def test_get_rant() -> None: """ Test get single rant. Payload: GET /devrant/rants/{rant_id} with last_comment_id, links, plat, guid, sid, seid, auth Expected: success=true, rant details """ params: Dict[str, Any] = { "last_comment_id": "999999999999", "links": 0, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("GET", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}", params=patch_auth(params)) def test_post_rant() -> None: """ Test post new rant. (Already done in post_test_rant for id) Payload: POST /devrant/rants with rant, tags, auth Expected: success=true, rant_id """ # Handled in setup pass def test_edit_rant() -> None: """ Test edit rant. Payload: POST /devrant/rants/{rant_id} with updated rant, tags, auth Expected: success=true """ data: Dict[str, Any] = { "rant": "Updated test rant", "tags": "test,python,update" } test_endpoint("POST", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}", data=patch_auth(data)) def test_delete_rant() -> None: """ Test delete rant. Payload: DELETE /devrant/rants/{rant_id} with plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("DELETE", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}", params=patch_auth(params)) def test_vote_rant() -> None: """ Test vote on rant (upvote and downvote with reason). Payload: POST /devrant/rants/{rant_id}/vote with vote (1/-1), reason (optional), plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "vote": 1, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}/vote", data=patch_auth(params)) params["vote"] = -1 params["reason"] = "1" test_endpoint("POST", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}/vote", data=patch_auth(params)) def test_favorite_rant() -> None: """ Test favorite/unfavorite rant. Payload: POST /devrant/rants/{rant_id}/favorite or /unfavorite with plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}/favorite", data=patch_auth(params)) test_endpoint("POST", f"{BASE_URL}/devrant/rants/{TEST_RANT_ID}/unfavorite", data=patch_auth(params)) def test_get_rant_feed() -> None: """ Test get rant feed. Payload: GET /devrant/rants with plat, guid, sid, seid, auth Expected: success=true, list of rants """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("GET", f"{BASE_URL}/devrant/rants", params=patch_auth(params)) def test_get_comment() -> None: """ Test get single comment. Payload: GET /comments/{comment_id} with links, plat, guid, sid, seid, auth Expected: success=true, comment details """ params: Dict[str, Any] = { "links": 0, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("GET", f"{BASE_URL}/comments/{TEST_COMMENT_ID}", params=patch_auth(params)) def test_post_comment() -> None: """ Test post comment. (Handled in post_test_comment for id) Payload: POST /devrant/rants/{rant_id}/comments with comment, auth Expected: success=true, comment_id """ # Handled in setup pass def test_edit_comment() -> None: """ Test edit comment. Payload: POST /comments/{comment_id} with updated comment, auth Expected: success=true """ data: Dict[str, Any] = { "comment": "Updated test comment" } test_endpoint("POST", f"{BASE_URL}/comments/{TEST_COMMENT_ID}", data=patch_auth(data)) def test_delete_comment() -> None: """ Test delete comment. Payload: DELETE /comments/{comment_id} with plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("DELETE", f"{BASE_URL}/comments/{TEST_COMMENT_ID}", params=patch_auth(params)) def test_vote_comment() -> None: """ Test vote on comment. Payload: POST /comments/{comment_id}/vote with vote (1), plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "vote": 1, "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("POST", f"{BASE_URL}/comments/{TEST_COMMENT_ID}/vote", data=patch_auth(params)) def test_get_notif_feed() -> None: """ Test get notification feed. Payload: GET /users/me/notif-feed with ext_prof, last_time, plat, guid, sid, seid, auth Expected: success=true, notifications """ params: Dict[str, Any] = { "ext_prof": 1, "last_time": str(int(datetime.now().timestamp())), "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("GET", f"{BASE_URL}/users/me/notif-feed", params=patch_auth(params)) def test_clear_notifications() -> None: """ Test clear notifications. Payload: DELETE /users/me/notif-feed with plat, guid, sid, seid, auth Expected: success=true """ params: Dict[str, Any] = { "plat": PLAT, "guid": GUID, "sid": SID, "seid": SEID } test_endpoint("DELETE", f"{BASE_URL}/users/me/notif-feed", params=patch_auth(params)) def test_beta_list_signup() -> None: """ Test beta list signup (external API). Payload: GET https://www.hexicallabs.com/api/beta-list with email, platform, app Expected: Whatever the API returns (may not be JSON) """ params: Dict[str, Any] = { "email": TEST_EMAIL, "platform": "test_platform" } test_endpoint("GET", "https://www.hexicallabs.com/api/beta-list", params=patch_auth(params)) def main() -> None: # Setup: Login and fetch real IDs if login_user(): global TEST_RANT_ID TEST_RANT_ID = post_test_rant() or fetch_real_rant_id() or "1" global TEST_COMMENT_ID TEST_COMMENT_ID = post_test_comment(TEST_RANT_ID) or "1" test_register_user() test_login_user() test_edit_profile() test_forgot_password() test_resend_confirm() test_mark_news_read() test_get_rant() test_post_rant() test_edit_rant() test_vote_rant() test_favorite_rant() test_get_rant_feed() test_get_comment() test_post_comment() test_edit_comment() test_delete_comment() test_vote_comment() test_get_notif_feed() test_clear_notifications() test_beta_list_signup() test_delete_rant() test_delete_account() save_results() if __name__ == "__main__": main()