Spaces:
Build error
Build error
File size: 8,301 Bytes
8299ce4 8e87953 450072f 8e87953 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 5bbe84d 5a3a712 8e87953 0f7833b 8e87953 8299ce4 450072f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | import json
import os
from datetime import datetime
import psycopg2
import pytest
import tempfile
import time
from app.core.usage_tracker import UsageTracker, Tier, UsageRecord
@pytest.fixture
def tracker():
with tempfile.NamedTemporaryFile(suffix=".db") as tmp:
yield UsageTracker(db_path=tmp.name)
def test_get_or_create_api_key(tracker):
# Updated: pass tenant_id as keyword argument (new signature)
assert tracker.get_or_create_api_key("test_key", tenant_id="test") is True
assert tracker.get_tier("test_key") == Tier.FREE
# Second call should return True without error
assert tracker.get_or_create_api_key("test_key", tenant_id="test") is True
def test_update_api_key_tier(tracker):
tracker.get_or_create_api_key("test_key", tenant_id="test")
assert tracker.update_api_key_tier("test_key", Tier.PRO) is True
assert tracker.get_tier("test_key") == Tier.PRO
# Non-existent key
assert tracker.update_api_key_tier("nonexistent", Tier.PRO) is False
def test_get_remaining_quota_free(tracker):
tracker.get_or_create_api_key("free_key", tenant_id="test")
# Initially 1000 remaining
remaining = tracker.get_remaining_quota("free_key", Tier.FREE)
assert remaining == 1000
# Simulate usage using the atomic method
record = UsageRecord(
api_key="free_key",
tier=Tier.FREE,
timestamp=time.time(),
endpoint="/test"
)
tracker.increment_usage_sync(record)
remaining = tracker.get_remaining_quota("free_key", Tier.FREE)
assert remaining == 999
def test_get_remaining_quota_enterprise(tracker):
tracker.get_or_create_api_key("ent_key", tenant_id="test")
remaining = tracker.get_remaining_quota("ent_key", Tier.ENTERPRISE)
assert remaining is None
def test_increment_usage_sync(tracker):
tracker.get_or_create_api_key("test_key", tenant_id="test")
record = UsageRecord(
api_key="test_key",
tier=Tier.FREE,
timestamp=time.time(),
endpoint="/test",
)
result = tracker.increment_usage_sync(record)
assert result is True
# Check quota decreased
remaining = tracker.get_remaining_quota("test_key", Tier.FREE)
assert remaining == 999
def test_get_audit_logs(tracker):
tracker.get_or_create_api_key("test_key", tenant_id="test")
record = UsageRecord(
api_key="test_key",
tier=Tier.FREE,
timestamp=time.time(),
endpoint="/test",
request_body={"foo": "bar"},
response={"status": "ok"},
)
tracker.increment_usage_sync(record)
logs = tracker.get_audit_logs("test_key", limit=10)
assert len(logs) == 1
assert logs[0]["endpoint"] == "/test"
def _pg_monthly_count(api_key: str, month: str) -> int:
"""Direct Postgres read, bypassing UsageTracker entirely -- this is
exactly the query arf-gateway's Go code runs against the same table."""
conn = psycopg2.connect(os.environ["DATABASE_URL"])
try:
with conn.cursor() as cur:
cur.execute(
"SELECT COALESCE(count, 0) FROM monthly_counts WHERE api_key = %s AND year_month = %s",
(api_key, month),
)
row = cur.fetchone()
return row[0] if row else 0
finally:
conn.close()
def test_increment_usage_sync_mirrors_to_postgres_monthly_counts(tracker):
"""Regresses arf-gateway-001: arf-gateway's quota check reads
monthly_counts from Postgres, not from this service's local SQLite
file. Every successfully-counted call must be visible there."""
tracker.get_or_create_api_key("pg-mirror-key", tenant_id="test")
month = tracker._get_month_key()
# No truncate fixture exists for this Postgres-resident table (CI's
# Postgres service is ephemeral per run, but a local repeat run against
# a persistent database could have leftover rows) -- assert the delta,
# not an absolute count.
baseline = _pg_monthly_count("pg-mirror-key", month)
record = UsageRecord(
api_key="pg-mirror-key", tier=Tier.FREE, timestamp=time.time(), endpoint="/test",
)
tracker.increment_usage_sync(record)
assert _pg_monthly_count("pg-mirror-key", month) == baseline + 1
tracker.increment_usage_sync(record)
assert _pg_monthly_count("pg-mirror-key", month) == baseline + 2
def test_increment_usage_sync_succeeds_even_if_postgres_mirror_fails(tracker, monkeypatch):
"""The local quota decision (SQLite/Redis) must not fail just because
the best-effort mirror write to Postgres did -- see
_record_pg_monthly_count's docstring. A degraded mirror write should
not turn into a degraded evaluate/healing endpoint for the caller."""
def _boom(*args, **kwargs):
raise psycopg2.OperationalError("simulated Postgres outage")
# get_or_create_api_key also goes through _get_pg_conn (it persists to
# the real api_keys table), so it must run before the patch below -- the
# outage this test simulates is specific to the monthly_counts mirror
# write, not to Postgres as a whole.
tracker.get_or_create_api_key("mirror-fail-key", tenant_id="test")
# Patch what _record_pg_monthly_count calls internally, not the method
# itself -- replacing the whole method would bypass its own try/except
# and prove nothing about that error-handling actually working.
monkeypatch.setattr(tracker, "_get_pg_conn", _boom)
record = UsageRecord(
api_key="mirror-fail-key", tier=Tier.FREE, timestamp=time.time(), endpoint="/test",
)
# Must not raise, and the local quota decision must still succeed.
result = tracker.increment_usage_sync(record)
assert result is True
assert tracker.get_remaining_quota("mirror-fail-key", Tier.FREE) == 999
def test_insert_audit_log_writes_response_row(tracker):
"""routes_governance.py schedules current_tracker._insert_audit_log as
a background task (background_tasks.add_task) to record the response
body once it's known, at app/api/routes_governance.py:407 and :738 --
always with tier=None, since quota was already consumed by an earlier
consume_quota_and_log call for the same request. The real UsageTracker
had no such method (only tests/conftest.py's MockTracker did), so every
real call raised AttributeError inside the background task (arf-api-002)."""
record = UsageRecord(
api_key="audit-log-key",
tier=None,
timestamp=time.time(),
endpoint="/api/v1/intents/evaluate/response",
request_body=None,
response={"recommended_action": "approve"},
processing_ms=12.5,
)
tracker._insert_audit_log(record)
logs = tracker.get_audit_logs("audit-log-key", limit=10)
assert len(logs) == 1
assert logs[0]["endpoint"] == "/api/v1/intents/evaluate/response"
assert logs[0]["tier"] == "unknown"
assert json.loads(logs[0]["response"]) == {"recommended_action": "approve"}
def test_consume_quota_and_log_handles_non_json_native_request_body(tracker):
"""request_body/response are whatever a Pydantic model's plain
.model_dump() returns, e.g. ReliabilityEvent.timestamp in
app/api/routes_governance.py's HealingDecisionRequest -- a raw
datetime, not the ISO string model_dump(mode="json") would produce.
json.dumps has no default encoder for datetime, so any real request
carrying one raised TypeError here, outside any try/except in the
/healing/evaluate handler, on every call (surfaced while verifying the
tier=None fix for that same endpoint: fixing tier alone still crashed,
one layer deeper, on this). default=str makes the insert tolerant of
datetime and any other type json.dumps doesn't natively handle."""
tracker.get_or_create_api_key("datetime-body-key", tenant_id="test")
record = UsageRecord(
api_key="datetime-body-key",
tier=Tier.FREE,
timestamp=time.time(),
endpoint="/api/v1/healing/evaluate",
request_body={"event": {"component": "svc", "timestamp": datetime.now()}},
)
result = tracker.increment_usage_sync(record)
assert result is True
logs = tracker.get_audit_logs("datetime-body-key", limit=10)
assert len(logs) == 1
assert "timestamp" in json.loads(logs[0]["request_body"])["event"]
|