Build [v1.4.1] - Security hardening, PWA and UI refinements
This commit is contained in:
92
backend/tests/api_bench.py
Normal file
92
backend/tests/api_bench.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
import sys
|
||||
|
||||
BASE_URL = "http://localhost:8000"
|
||||
|
||||
def log_test(name, status, details=""):
|
||||
icon = "✅" if status == "PASS" else "❌"
|
||||
print(f"{icon} [{name}] - {details}")
|
||||
|
||||
def run_api_suite():
|
||||
print(f"\n🚀 Starting API Testing Suite (Postman-style logic)\n" + "-"*50)
|
||||
|
||||
# 1. AUTHENTICATION TEST
|
||||
try:
|
||||
login_res = requests.post(f"{BASE_URL}/users/login", json={
|
||||
"username": "Admin",
|
||||
"password": "admin"
|
||||
})
|
||||
if login_res.status_code == 200:
|
||||
token = login_res.json()["access_token"]
|
||||
log_test("Auth: Admin Login", "PASS", f"Status: {login_res.status_code}")
|
||||
else:
|
||||
log_test("Auth: Admin Login", "FAIL", f"Status: {login_res.status_code}")
|
||||
return
|
||||
except Exception as e:
|
||||
log_test("Auth: Admin Login", "FAIL", str(e))
|
||||
return
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# 2. STATUS CODES & CRUD
|
||||
try:
|
||||
item_res = requests.get(f"{BASE_URL}/items/", headers=headers)
|
||||
if item_res.status_code == 200:
|
||||
log_test("Items: List All", "PASS", f"Returned {len(item_res.json())} items")
|
||||
else:
|
||||
log_test("Items: List All", "FAIL", f"Status: {item_res.status_code}")
|
||||
except Exception as e:
|
||||
log_test("Items: List All", "FAIL", str(e))
|
||||
|
||||
# 3. RATE LIMITING TEST (Security Policy)
|
||||
print("\n⏳ Testing Rate Limiting (Anti Brute-Force)...")
|
||||
limit_hit = False
|
||||
for i in range(12): # More than the 5/min limit
|
||||
res = requests.post(f"{BASE_URL}/users/login", json={"username": "fake", "password": "fake"})
|
||||
if res.status_code == 429:
|
||||
limit_hit = True
|
||||
log_test("Security: Rate Limiter", "PASS", f"Blocked at attempt {i+1} (429 Too Many Requests)")
|
||||
break
|
||||
if not limit_hit:
|
||||
log_test("Security: Rate Limiter", "FAIL", "Limiter did not trigger after 12 quick requests")
|
||||
|
||||
# 4. RBAC PROTECTION
|
||||
# Create a test item to delete
|
||||
test_item = requests.post(f"{BASE_URL}/items/", headers=headers, json={
|
||||
"barcode": "API-TEST-999",
|
||||
"name": "API Test Item",
|
||||
"category": "Testing",
|
||||
"quantity": 10,
|
||||
"min_quantity": 1
|
||||
})
|
||||
|
||||
if test_item.status_code == 201:
|
||||
item_id = test_item.json()["id"]
|
||||
log_test("Items: Create test resource", "PASS", f"ID: {item_id}")
|
||||
|
||||
# Now try to delete it (as Admin - should pass)
|
||||
del_res = requests.delete(f"{BASE_URL}/items/{item_id}", headers=headers)
|
||||
if del_res.status_code == 200:
|
||||
log_test("RBAC: Admin Delete", "PASS", "Resource purged successfully")
|
||||
else:
|
||||
log_test("RBAC: Admin Delete", "FAIL", f"Status: {del_res.status_code}")
|
||||
else:
|
||||
# Check if it already exists from previous failed run
|
||||
if test_item.status_code == 400:
|
||||
log_test("Items: Create test resource", "PASS", "Resource already exists")
|
||||
else:
|
||||
log_test("Items: Create test resource", "FAIL", f"Status: {test_item.status_code}")
|
||||
|
||||
# 5. ERROR STATES
|
||||
unauth_res = requests.get(f"{BASE_URL}/items/stats")
|
||||
if unauth_res.status_code == 401:
|
||||
log_test("Security: Unauth Block", "PASS", "Blocked 401 Unauthorized")
|
||||
else:
|
||||
log_test("Security: Unauth Block", "FAIL", f"Server allowed access! Status: {unauth_res.status_code}")
|
||||
|
||||
print("\n" + "-"*50 + "\n🏁 API Test Suite Finished.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_api_suite()
|
||||
Reference in New Issue
Block a user