idd / test_api.py
esmaill1
feat: implement image processing core, FastAPI backend, and full-stack integration tests
f19ba0f
Raw
History Blame Contribute Delete
7.91 kB
"""
EL HELAL Studio β€” API Test Suite
Run with: python test_api.py
"""
import requests
import json
import io
import sys
from pathlib import Path
from PIL import Image
BASE_URL = "http://127.0.0.1:9000"
# ── Helpers ──────────────────────────────────────────────────────────────────
def status():
r = requests.get(f"{BASE_URL}/status")
r.raise_for_status()
return r.json()
def wait_for_ai(timeout=60):
"""Poll /status until ai_ready is True."""
import time
for _ in range(timeout):
s = status()
if s.get("ai_ready"):
return True
time.sleep(2)
raise TimeoutError("AI model did not become ready within timeout")
def create_test_image(width=400, height=600, color=(100, 150, 200)) -> io.BytesIO:
"""Create a synthetic JPEG for testing upload."""
img = Image.new("RGB", (width, height), color)
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=85)
buf.seek(0)
buf.name = "test_portrait.jpg"
return buf
def upload_image(file_obj) -> dict:
r = requests.post(f"{BASE_URL}/upload", files={"file": file_obj})
r.raise_for_status()
return r.json()
# ── Test Cases ────────────────────────────────────────────────────────────────
def test_status():
print("\n[TEST] GET /status")
data = status()
assert "ai_ready" in data, f"Expected 'ai_ready' key, got: {data}"
print(f" β†’ ai_ready: {data['ai_ready']}")
def test_settings_get():
print("\n[TEST] GET /settings")
r = requests.get(f"{BASE_URL}/settings")
r.raise_for_status()
data = r.json()
print(f" β†’ settings: {json.dumps(data, indent=4)}")
def test_settings_update():
print("\n[TEST] POST /settings")
payload = {"retouch": {"enabled": True, "sensitivity": 2.5, "tone_smoothing": 0.5}}
r = requests.post(f"{BASE_URL}/settings", json=payload)
r.raise_for_status()
data = r.json()
assert data.get("status") == "success"
print(f" β†’ status: {data['status']}")
def test_frames_list():
print("\n[TEST] GET /frames")
r = requests.get(f"{BASE_URL}/frames")
r.raise_for_status()
data = r.json()
assert "frames" in data
print(f" β†’ frames count: {len(data['frames'])}")
def test_upload():
print("\n[TEST] POST /upload")
img = create_test_image()
data = upload_image(img)
assert "id" in data
assert "thumb_url" in data
print(f" β†’ file_id: {data['id']}")
print(f" β†’ thumb_url: {data['thumb_url']}")
return data # Return full dict for caller
def test_process_image(file_id: str):
print(f"\n[TEST] POST /process/{file_id}")
payload = {
"name": "Test User",
"id_number": "12345",
"do_restore": False,
"fidelity": 0.5,
"do_rmbg": True,
"do_color": True,
"do_retouch": True,
"do_crop": True,
"add_studio_name": True,
"add_logo": True,
"add_date": True,
}
r = requests.post(f"{BASE_URL}/process/{file_id}", data=payload)
r.raise_for_status()
data = r.json()
assert "result_url" in data
print(f" β†’ result_url: {data['result_url']}")
print(f" β†’ preview_url: {data['preview_url']}")
def test_process_image_with_restore(file_id: str):
print(f"\n[TEST] POST /process/{file_id} (with restoration)")
payload = {
"name": "Restored User",
"id_number": "99999",
"do_restore": True,
"fidelity": 0.6,
"do_rmbg": True,
"do_color": True,
"do_retouch": False,
"do_crop": True,
"add_studio_name": True,
"add_logo": False,
"add_date": False,
}
r = requests.post(f"{BASE_URL}/process/{file_id}", data=payload)
r.raise_for_status()
data = r.json()
print(f" β†’ result_url: {data['result_url']}")
def test_process_manual_crop(file_id: str):
print(f"\n[TEST] POST /process/{file_id} (manual crop)")
payload = {
"name": "Manual Crop",
"id_number": "55555",
"x1": 100,
"y1": 50,
"x2": 300,
"y2": 450,
"do_crop": True,
"do_rmbg": True,
"do_color": False,
"do_retouch": False,
"add_studio_name": False,
"add_logo": False,
"add_date": False,
}
r = requests.post(f"{BASE_URL}/process/{file_id}", data=payload)
r.raise_for_status()
data = r.json()
print(f" β†’ result_url: {data['result_url']}")
def test_frames_upload():
print("\n[TEST] POST /frames (upload a test frame)")
img = Image.new("RGBA", (200, 200), (200, 100, 100, 255))
buf = io.BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
r = requests.post(
f"{BASE_URL}/frames",
files={"file": ("test_frame.png", buf, "image/png")},
)
r.raise_for_status()
data = r.json()
assert data.get("status") == "success"
frame = data["frame"]
print(f" β†’ uploaded: {frame['filename']}")
return frame["filename"]
def test_frames_delete(filename: str):
print(f"\n[TEST] DELETE /frames/{filename}")
r = requests.delete(f"{BASE_URL}/frames/{filename}")
r.raise_for_status()
data = r.json()
assert data.get("status") == "success"
print(f" β†’ deleted: {filename}")
def test_backup_export():
print("\n[TEST] POST /backup/export")
client_data = {"theme": "dark", "last_user": "tester"}
r = requests.post(f"{BASE_URL}/backup/export", json={"client_data": client_data})
r.raise_for_status()
assert r.headers["content-type"].startswith("application/zip")
print(f" β†’ Content-Length: {len(r.content)} bytes")
return r.content
def test_backup_import(zip_bytes: bytes):
print("\n[TEST] POST /backup/import")
files = {"file": ("backup.zip", io.BytesIO(zip_bytes), "application/zip")}
r = requests.post(f"{BASE_URL}/backup/import", files=files)
r.raise_for_status()
data = r.json()
print(f" β†’ status: {data['status']}")
def test_clear_all():
print("\n[TEST] POST /clear-all")
r = requests.post(f"{BASE_URL}/clear-all")
r.raise_for_status()
data = r.json()
assert data.get("status") == "success"
print(f" β†’ removed_count: {data.get('removed_count')}")
# ── Main ─────────────────────────────────────────────────────────────────────
def main():
print("=" * 60)
print("EL HELAL Studio β€” API Test Suite")
print("=" * 60)
# 1. Status check
test_status()
# 2. Wait for AI to be ready
print("\n[INFO] Waiting for AI models to load...")
try:
wait_for_ai(timeout=90)
print("[INFO] AI models are ready.")
except TimeoutError as e:
print(f"[WARN] {e}")
# 3. Settings
test_settings_get()
test_settings_update()
# 4. Frames
test_frames_list()
# 5. Upload + Process pipeline
upload_data = test_upload()
file_id = upload_data["id"]
test_process_image(file_id)
test_process_image_with_restore(file_id)
test_process_image_with_restore(file_id) # extra run for variance
# 6. Manual crop
upload2 = test_upload()
test_process_manual_crop(upload2["id"])
# 7. Frames upload/delete cycle
frame_filename = test_frames_upload()
test_frames_delete(frame_filename)
# 8. Backup cycle
zip_bytes = test_backup_export()
test_backup_import(zip_bytes)
# 9. Clear all
test_clear_all()
print("\n" + "=" * 60)
print("ALL TESTS PASSED")
print("=" * 60)
if __name__ == "__main__":
main()