Files
FarmaFinder/pip-platform/tests/integration/test_api_key_auth.py
T
Antoni Nuñez Romeu 8615e213de
Run Tests on Branches / Backend Tests (push) Successful in 3m28s
Run Tests on Branches / Frontend Tests (push) Successful in 3m25s
Run Tests on Branches / PIP Tests (push) Failing after 9s
PIP PLATFORM ended
2026-07-01 17:19:03 +02:00

147 lines
5.4 KiB
Python

"""E2E tests for API Key authentication flow.
Tests dual auth support (JWT Bearer + X-API-Key header),
API Key CRUD, scope enforcement, and role-based access control.
"""
import asyncio
import os
import httpx
BASE_URL = os.environ.get("PIP_BASE_URL", "http://localhost:8000")
ADMIN_CREDS = {"username": os.environ.get("PIP_ADMIN_USER", "synctest"), "password": os.environ.get("PIP_ADMIN_PASS", "Test123!")}
async def get_admin_token(client: httpx.AsyncClient) -> str:
r = await client.post("/api/v1/auth/token", json=ADMIN_CREDS)
assert r.status_code == 200, f"Login failed: {r.status_code} {r.text[:200]}"
return r.json()["access_token"]
def bearer_headers(token: str) -> dict:
return {"Authorization": f"Bearer {token}"}
async def test_api_key_crud():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
token = await get_admin_token(c)
h = bearer_headers(token)
clients = await c.get("/api/v1/api-clients", headers=h)
assert clients.status_code == 200
client_id = next((ci["id"] for ci in clients.json().get("items", []) if ci["name"] == "Test Client"), None)
if not client_id:
resp = await c.post("/api/v1/api-clients", headers=h, json={
"name": "Test Client",
"description": "E2E test client",
"scopes": ["pharmacies:read", "medications:read", "inventory:read"],
})
assert resp.status_code == 201
client_id = resp.json()["id"]
key_resp = await c.post(f"/api/v1/api-clients/{client_id}/keys", headers=h, json={"name": "E2E CRUD Key"})
assert key_resp.status_code == 201, f"Key create failed: {key_resp.status_code} {key_resp.text[:200]}"
raw_key = key_resp.json()["key"]
key_prefix = key_resp.json()["key_prefix"]
assert len(raw_key) > 20
assert len(key_prefix) == 8
keys = await c.get(f"/api/v1/api-clients/{client_id}/keys", headers=h)
assert keys.status_code == 200
assert len(keys.json()) >= 1
print(" PASS: test_api_key_crud")
async def test_api_key_read_access():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
token = await get_admin_token(c)
h = bearer_headers(token)
clients = await c.get("/api/v1/api-clients", headers=h)
client_id = next((ci["id"] for ci in clients.json().get("items", []) if ci["name"] == "Test Client"), None)
kr = await c.post(f"/api/v1/api-clients/{client_id}/keys", headers=h, json={"name": "Read Access Key"})
raw_key = kr.json()["key"]
api_h = {"X-API-Key": raw_key}
r = await c.get("/api/v1/pharmacies", headers=api_h)
assert r.status_code == 200
assert r.json()["total"] >= 0
r2 = await c.get("/api/v1/medications", headers=api_h)
assert r2.status_code == 200
if r.json()["total"] > 0:
pharm_id = r.json()["items"][0]["id"]
r3 = await c.get(f"/api/v1/pharmacies/{pharm_id}/inventory", headers=api_h)
assert r3.status_code == 200
print(" PASS: test_api_key_read_access")
async def test_api_key_write_blocked():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
token = await get_admin_token(c)
h = bearer_headers(token)
clients = await c.get("/api/v1/api-clients", headers=h)
client_id = next((ci["id"] for ci in clients.json().get("items", []) if ci["name"] == "Test Client"), None)
kr = await c.post(f"/api/v1/api-clients/{client_id}/keys", headers=h, json={"name": "Write Block Key"})
raw_key = kr.json()["key"]
api_h = {"X-API-Key": raw_key}
r = await c.post("/api/v1/pharmacies", headers=api_h, json={"code": "BLOCKED", "name": "Should Fail"})
assert r.status_code == 403, f"Expected 403, got {r.status_code}"
print(" PASS: test_api_key_write_blocked")
async def test_invalid_api_key():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
r = await c.get("/api/v1/pharmacies", headers={"X-API-Key": "invalidkey12345"})
assert r.status_code == 401
print(" PASS: test_invalid_api_key")
async def test_no_auth_rejected():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
r = await c.get("/api/v1/pharmacies")
assert r.status_code == 401
print(" PASS: test_no_auth_rejected")
async def test_dual_auth_bearer_priority():
async with httpx.AsyncClient(base_url=BASE_URL) as c:
token = await get_admin_token(c)
h = bearer_headers(token)
clients = await c.get("/api/v1/api-clients", headers=h)
client_id = next((ci["id"] for ci in clients.json().get("items", []) if ci["name"] == "Test Client"), None)
kr = await c.post(f"/api/v1/api-clients/{client_id}/keys", headers=h, json={"name": "Dual Auth Key"})
raw_key = kr.json()["key"]
dual_h = {"Authorization": f"Bearer {token}", "X-API-Key": raw_key}
r = await c.get("/api/v1/pharmacies", headers=dual_h)
assert r.status_code == 200
print(" PASS: test_dual_auth_bearer_priority")
async def run_all():
print("=== API Key Auth E2E Tests ===")
await test_api_key_crud()
await test_api_key_read_access()
await test_api_key_write_blocked()
await test_invalid_api_key()
await test_no_auth_rejected()
await test_dual_auth_bearer_priority()
print("=== All API Key tests passed ===")
if __name__ == "__main__":
asyncio.run(run_all())