Spaces:
Build error
Build error
| """ | |
| Route-level tests for the /admin/keys endpoints against the real, | |
| Postgres-backed UsageTracker -- these routes are normally exercised through | |
| the module-level `tracker` singleton (conftest.py replaces it globally with | |
| MockTracker for every other test), so `tracker` is patched on | |
| `app.core.usage_tracker` and `ADMIN_API_KEY` on `app.api.routes_admin` for | |
| the duration of this module. | |
| `tracker` is patched on the *defining* module, not on routes_admin, because | |
| routes_admin no longer holds its own binding: it reaches the singleton | |
| through `usage_tracker.tracker` so that init_tracker()'s rebinding is | |
| visible to it. That is also what makes one patch here sufficient where five | |
| would otherwise be needed. | |
| This is the main regression coverage for the api_keys-on-Postgres | |
| migration: it exercises the exact raw SQL routes_admin.py runs against | |
| `usage_tracker.tracker._get_pg_conn()`. | |
| """ | |
| import hashlib | |
| import hmac | |
| import os | |
| import pytest | |
| from app.core import usage_tracker | |
| from app.core.usage_tracker import UsageTracker | |
| import app.api.routes_admin as routes_admin | |
| TEST_ADMIN_KEY = "test-admin-key-for-routes-admin-tests" | |
| TEST_PEPPER = os.environ["ARF_KEY_PEPPER"] # set in conftest.py before app import | |
| def _key_id(raw_key: str) -> str: | |
| """Reproduce UsageTracker._lookup_hash without a tracker instance, so | |
| tests can locate the row created for a given raw key deterministically.""" | |
| return hmac.new(TEST_PEPPER.encode(), raw_key.encode(), hashlib.sha256).hexdigest() | |
| def real_tracker(monkeypatch): | |
| real = UsageTracker(db_path=":memory:") | |
| monkeypatch.setattr(usage_tracker, "tracker", real) | |
| monkeypatch.setattr(routes_admin, "ADMIN_API_KEY", TEST_ADMIN_KEY) | |
| yield real | |
| def test_create_list_update_deactivate_key(client): | |
| create_resp = client.post( | |
| "/api/v1/admin/keys", | |
| params={"admin_key": TEST_ADMIN_KEY}, | |
| json={"tier": "free", "org_name": "Test Org"}, | |
| ) | |
| assert create_resp.status_code == 200 | |
| body = create_resp.json() | |
| api_key = body["api_key"] | |
| assert body["tier"] == "free" | |
| key_id = _key_id(api_key) | |
| list_resp = client.get("/api/v1/admin/keys", params={"admin_key": TEST_ADMIN_KEY}) | |
| assert list_resp.status_code == 200 | |
| keys_by_id = {row["key_id"]: row for row in list_resp.json()["keys"]} | |
| assert key_id in keys_by_id | |
| assert keys_by_id[key_id]["tier"] == "free" | |
| assert keys_by_id[key_id]["is_active"] is True | |
| patch_resp = client.patch( | |
| f"/api/v1/admin/keys/{key_id}/tier", | |
| params={"admin_key": TEST_ADMIN_KEY}, | |
| json={"tier": "pro"}, | |
| ) | |
| assert patch_resp.status_code == 200 | |
| list_resp2 = client.get("/api/v1/admin/keys", params={"admin_key": TEST_ADMIN_KEY}) | |
| assert list_resp2.json()["keys"][0] # non-empty, sanity check | |
| keys_by_id2 = {row["key_id"]: row for row in list_resp2.json()["keys"]} | |
| assert keys_by_id2[key_id]["tier"] == "pro" | |
| delete_resp = client.delete(f"/api/v1/admin/keys/{key_id}", params={"admin_key": TEST_ADMIN_KEY}) | |
| assert delete_resp.status_code == 200 | |
| list_resp3 = client.get("/api/v1/admin/keys", params={"admin_key": TEST_ADMIN_KEY}) | |
| keys_by_id3 = {row["key_id"]: row for row in list_resp3.json()["keys"]} | |
| assert keys_by_id3[key_id]["is_active"] is False | |
| def test_update_nonexistent_key_returns_404(client): | |
| resp = client.patch( | |
| "/api/v1/admin/keys/does-not-exist/tier", | |
| params={"admin_key": TEST_ADMIN_KEY}, | |
| json={"tier": "pro"}, | |
| ) | |
| assert resp.status_code == 404 | |
| def test_rotate_key_deactivates_old_and_creates_new_on_same_tenant(client): | |
| create_resp = client.post( | |
| "/api/v1/admin/keys", | |
| params={"admin_key": TEST_ADMIN_KEY}, | |
| json={"tier": "pro", "org_name": "Rotate Test Org"}, | |
| ) | |
| assert create_resp.status_code == 200 | |
| old_body = create_resp.json() | |
| old_key_id = _key_id(old_body["api_key"]) | |
| tenant_id = old_body["tenant_id"] | |
| rotate_resp = client.post( | |
| f"/api/v1/admin/keys/{old_key_id}/rotate", params={"admin_key": TEST_ADMIN_KEY}) | |
| assert rotate_resp.status_code == 200 | |
| rotated = rotate_resp.json() | |
| assert rotated["tenant_id"] == tenant_id | |
| assert rotated["tier"] == "pro" | |
| assert rotated["deactivated_key_id"] == old_key_id | |
| new_key_id = _key_id(rotated["api_key"]) | |
| assert new_key_id != old_key_id | |
| list_resp = client.get("/api/v1/admin/keys", params={"admin_key": TEST_ADMIN_KEY}) | |
| keys_by_id = {row["key_id"]: row for row in list_resp.json()["keys"]} | |
| assert keys_by_id[old_key_id]["is_active"] is False | |
| assert keys_by_id[new_key_id]["is_active"] is True | |
| assert keys_by_id[new_key_id]["tier"] == "pro" | |
| def test_rotate_nonexistent_key_returns_404(client): | |
| resp = client.post( | |
| "/api/v1/admin/keys/does-not-exist/rotate", params={"admin_key": TEST_ADMIN_KEY}) | |
| assert resp.status_code == 404 | |