mirror of
https://github.com/azaion/loader.git
synced 2026-04-22 08:36:31 +00:00
Add E2E tests, fix bugs
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
def test_status_unauthenticated(base_url, api_client):
|
||||
# Act
|
||||
response = api_client.get(f"{base_url}/status")
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert response.json()["authenticated"] is False
|
||||
|
||||
|
||||
def test_download_unauthenticated(base_url, api_client):
|
||||
# Arrange
|
||||
url = f"{base_url}/load/testmodel"
|
||||
body = {"filename": "testmodel", "folder": "models"}
|
||||
|
||||
# Act
|
||||
response = api_client.post(url, json=body)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 500
|
||||
|
||||
|
||||
def test_login_invalid_credentials(base_url, api_client):
|
||||
# Arrange
|
||||
payload = {"email": "wrong@example.com", "password": "wrong"}
|
||||
|
||||
# Act
|
||||
response = api_client.post(f"{base_url}/login", json=payload)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_login_empty_body(base_url, api_client):
|
||||
# Act
|
||||
response = api_client.post(f"{base_url}/login", json={})
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_login_valid_credentials(base_url, api_client):
|
||||
# Arrange
|
||||
payload = {"email": "test@azaion.com", "password": "testpass"}
|
||||
|
||||
# Act
|
||||
response = api_client.post(f"{base_url}/login", json=payload)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "ok"
|
||||
|
||||
|
||||
def test_status_authenticated_after_login(base_url, logged_in_client):
|
||||
# Act
|
||||
response = logged_in_client.get(f"{base_url}/status")
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert response.json()["authenticated"] is True
|
||||
@@ -0,0 +1,7 @@
|
||||
def test_health_returns_200(base_url, api_client):
|
||||
# Act
|
||||
response = api_client.get(f"{base_url}/health")
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "healthy"
|
||||
@@ -0,0 +1,17 @@
|
||||
import time
|
||||
|
||||
|
||||
def test_health_latency_p95(base_url, api_client):
|
||||
# Arrange
|
||||
times = []
|
||||
# Act
|
||||
for _ in range(100):
|
||||
start = time.perf_counter()
|
||||
response = api_client.get(f"{base_url}/health")
|
||||
elapsed = time.perf_counter() - start
|
||||
times.append(elapsed)
|
||||
response.raise_for_status()
|
||||
times.sort()
|
||||
p95 = times[94]
|
||||
# Assert
|
||||
assert p95 <= 0.1
|
||||
@@ -0,0 +1,74 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_upload_resource(base_url, logged_in_client):
|
||||
# Arrange
|
||||
url = f"{base_url}/upload/testmodel"
|
||||
files = {"data": ("testmodel.bin", b"test content")}
|
||||
data = {"folder": "models"}
|
||||
|
||||
# Act
|
||||
response = logged_in_client.post(url, files=files, data=data)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "ok"
|
||||
|
||||
|
||||
def test_download_resource(base_url, logged_in_client):
|
||||
# Arrange
|
||||
url = f"{base_url}/load/testmodel"
|
||||
body = {"filename": "testmodel", "folder": "models"}
|
||||
|
||||
# Act
|
||||
response = logged_in_client.post(url, json=body)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
assert len(response.content) > 0
|
||||
|
||||
|
||||
def test_download_nonexistent(base_url, logged_in_client):
|
||||
# Arrange
|
||||
url = f"{base_url}/load/nonexistent"
|
||||
body = {"filename": "nonexistent", "folder": "nonexistent"}
|
||||
|
||||
# Act
|
||||
response = logged_in_client.post(url, json=body)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 500
|
||||
|
||||
|
||||
def test_upload_no_file(base_url, logged_in_client):
|
||||
# Arrange
|
||||
url = f"{base_url}/upload/testfile"
|
||||
|
||||
# Act
|
||||
response = logged_in_client.post(url, data={"folder": "models"})
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_upload_download_roundtrip(base_url, logged_in_client):
|
||||
# Arrange
|
||||
filename = "roundtrip"
|
||||
folder = "models"
|
||||
content = b"roundtrip-payload-data"
|
||||
upload_url = f"{base_url}/upload/{filename}"
|
||||
load_url = f"{base_url}/load/{filename}"
|
||||
files = {"data": (f"{filename}.bin", content)}
|
||||
data = {"folder": folder}
|
||||
|
||||
# Act
|
||||
upload_response = logged_in_client.post(upload_url, files=files, data=data)
|
||||
download_response = logged_in_client.post(
|
||||
load_url,
|
||||
json={"filename": filename, "folder": folder},
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert upload_response.status_code == 200
|
||||
assert download_response.status_code == 200
|
||||
assert download_response.content == content
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
COMPOSE_FILE = os.path.join(os.path.dirname(__file__), "..", "docker-compose.test.yml")
|
||||
|
||||
|
||||
def _compose_exec(cmd: str):
|
||||
subprocess.run(
|
||||
["docker", "compose", "-f", COMPOSE_FILE, "exec", "system-under-test",
|
||||
"bash", "-c", cmd],
|
||||
capture_output=True, timeout=15,
|
||||
)
|
||||
|
||||
|
||||
def _wait_for_settled(base_url, client, timeout=30):
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
resp = client.get(f"{base_url}/unlock/status")
|
||||
state = resp.json()["state"]
|
||||
if state in ("idle", "error", "ready"):
|
||||
return state
|
||||
time.sleep(0.5)
|
||||
return None
|
||||
|
||||
|
||||
def test_unlock_status_idle(base_url, api_client):
|
||||
# Act
|
||||
response = api_client.get(f"{base_url}/unlock/status")
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["state"] == "idle"
|
||||
assert data["error"] is None
|
||||
|
||||
|
||||
def test_unlock_missing_archive(base_url, api_client):
|
||||
# Arrange
|
||||
payload = {"email": "test@azaion.com", "password": "testpass"}
|
||||
|
||||
# Act
|
||||
response = api_client.post(f"{base_url}/unlock", json=payload)
|
||||
|
||||
# Assert
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_unlock_concurrent_returns_current_state(base_url, api_client):
|
||||
# Arrange
|
||||
_compose_exec("dd if=/dev/urandom of=/tmp/test.enc bs=1024 count=1 2>/dev/null")
|
||||
payload = {"email": "test@azaion.com", "password": "testpass"}
|
||||
|
||||
try:
|
||||
# Act
|
||||
first = api_client.post(f"{base_url}/unlock", json=payload)
|
||||
second = api_client.post(f"{base_url}/unlock", json=payload)
|
||||
|
||||
# Assert
|
||||
assert first.status_code == 200
|
||||
assert second.status_code == 200
|
||||
assert second.json()["state"] != "idle"
|
||||
finally:
|
||||
_compose_exec("rm -f /tmp/test.enc /tmp/test.tar")
|
||||
_wait_for_settled(base_url, api_client)
|
||||
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
COMPOSE_FILE = os.path.join(os.path.dirname(__file__), "..", "docker-compose.test.yml")
|
||||
|
||||
|
||||
def _compose(*args):
|
||||
subprocess.run(
|
||||
["docker", "compose", "-f", COMPOSE_FILE, *args],
|
||||
capture_output=True, timeout=30,
|
||||
)
|
||||
|
||||
|
||||
def test_download_when_cdn_unavailable(base_url, logged_in_client):
|
||||
# Arrange
|
||||
_compose("stop", "mock-cdn")
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
# Act
|
||||
try:
|
||||
response = logged_in_client.post(
|
||||
f"{base_url}/load/nocache",
|
||||
json={"filename": "nocache", "folder": "models"},
|
||||
timeout=15,
|
||||
)
|
||||
status = response.status_code
|
||||
except Exception:
|
||||
status = 0
|
||||
|
||||
# Assert
|
||||
assert status != 200
|
||||
finally:
|
||||
_compose("start", "mock-cdn")
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def test_unlock_with_corrupt_archive(base_url, api_client):
|
||||
# Arrange
|
||||
subprocess.run(
|
||||
["docker", "compose", "-f", COMPOSE_FILE, "exec", "system-under-test",
|
||||
"bash", "-c", "dd if=/dev/urandom of=/tmp/test.enc bs=1024 count=1 2>/dev/null"],
|
||||
capture_output=True, timeout=15,
|
||||
)
|
||||
payload = {"email": "test@azaion.com", "password": "testpass"}
|
||||
|
||||
try:
|
||||
# Act
|
||||
response = api_client.post(f"{base_url}/unlock", json=payload)
|
||||
assert response.status_code == 200
|
||||
|
||||
deadline = time.monotonic() + 30
|
||||
body = None
|
||||
while time.monotonic() < deadline:
|
||||
status = api_client.get(f"{base_url}/unlock/status")
|
||||
body = status.json()
|
||||
if body["state"] in ("error", "ready"):
|
||||
break
|
||||
time.sleep(0.5)
|
||||
|
||||
# Assert
|
||||
assert body is not None
|
||||
assert body["state"] == "error"
|
||||
assert body["error"] is not None
|
||||
finally:
|
||||
subprocess.run(
|
||||
["docker", "compose", "-f", COMPOSE_FILE, "exec", "system-under-test",
|
||||
"bash", "-c", "rm -f /tmp/test.enc /tmp/test.tar"],
|
||||
capture_output=True, timeout=15,
|
||||
)
|
||||
Reference in New Issue
Block a user