Add E2E tests, fix bugs

Made-with: Cursor
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-04-13 05:17:48 +03:00
parent 1f98b5e958
commit 8f7deb3fca
71 changed files with 4740 additions and 29 deletions
View File
+59
View File
@@ -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
+7
View File
@@ -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"
+17
View File
@@ -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
+74
View File
@@ -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
+66
View File
@@ -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)
+72
View File
@@ -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,
)