test_account_usage.py
1 from datetime import datetime, timezone 2 3 from agent.account_usage import ( 4 AccountUsageSnapshot, 5 AccountUsageWindow, 6 fetch_account_usage, 7 render_account_usage_lines, 8 ) 9 10 11 class _Response: 12 def __init__(self, payload, status_code=200): 13 self._payload = payload 14 self.status_code = status_code 15 16 def raise_for_status(self): 17 if self.status_code >= 400: 18 raise RuntimeError(f"HTTP {self.status_code}") 19 20 def json(self): 21 return self._payload 22 23 24 class _Client: 25 def __init__(self, payload): 26 self._payload = payload 27 28 def __enter__(self): 29 return self 30 31 def __exit__(self, exc_type, exc, tb): 32 return False 33 34 def get(self, url, headers=None): 35 return _Response(self._payload) 36 37 38 class _RoutingClient: 39 def __init__(self, payloads): 40 self._payloads = payloads 41 42 def __enter__(self): 43 return self 44 45 def __exit__(self, exc_type, exc, tb): 46 return False 47 48 def get(self, url, headers=None): 49 return _Response(self._payloads[url]) 50 51 52 def test_fetch_account_usage_codex(monkeypatch): 53 monkeypatch.setattr( 54 "agent.account_usage.resolve_codex_runtime_credentials", 55 lambda refresh_if_expiring=True: { 56 "provider": "openai-codex", 57 "base_url": "https://chatgpt.com/backend-api/codex", 58 "api_key": "access-token", 59 }, 60 ) 61 monkeypatch.setattr( 62 "agent.account_usage._read_codex_tokens", 63 lambda: {"tokens": {"account_id": "acct_123"}}, 64 ) 65 monkeypatch.setattr( 66 "agent.account_usage.httpx.Client", 67 lambda timeout=15.0: _Client( 68 { 69 "plan_type": "pro", 70 "rate_limit": { 71 "primary_window": { 72 "used_percent": 15, 73 "reset_at": 1_900_000_000, 74 "limit_window_seconds": 18000, 75 }, 76 "secondary_window": { 77 "used_percent": 40, 78 "reset_at": 1_900_500_000, 79 "limit_window_seconds": 604800, 80 }, 81 }, 82 "credits": {"has_credits": True, "balance": 12.5}, 83 } 84 ), 85 ) 86 87 snapshot = fetch_account_usage("openai-codex") 88 89 assert snapshot is not None 90 assert snapshot.plan == "Pro" 91 assert len(snapshot.windows) == 2 92 assert snapshot.windows[0].label == "Session" 93 assert snapshot.windows[0].used_percent == 15.0 94 assert snapshot.windows[0].reset_at == datetime.fromtimestamp(1_900_000_000, tz=timezone.utc) 95 assert "Credits balance: $12.50" in snapshot.details 96 97 98 def test_render_account_usage_lines_includes_reset_and_provider(): 99 snapshot = AccountUsageSnapshot( 100 provider="openai-codex", 101 source="usage_api", 102 fetched_at=datetime.now(timezone.utc), 103 plan="Pro", 104 windows=( 105 AccountUsageWindow( 106 label="Session", 107 used_percent=25, 108 reset_at=datetime.now(timezone.utc), 109 ), 110 ), 111 details=("Credits balance: $9.99",), 112 ) 113 lines = render_account_usage_lines(snapshot) 114 115 assert lines[0] == "📈 Account limits" 116 assert "openai-codex (Pro)" in lines[1] 117 assert "Session: 75% remaining (25% used)" in lines[2] 118 assert "Credits balance: $9.99" in lines[3] 119 120 121 def test_fetch_account_usage_openrouter_uses_limit_remaining_and_ignores_deprecated_rate_limit(monkeypatch): 122 monkeypatch.setattr( 123 "agent.account_usage.resolve_runtime_provider", 124 lambda requested, explicit_base_url=None, explicit_api_key=None: { 125 "provider": "openrouter", 126 "base_url": "https://openrouter.ai/api/v1", 127 "api_key": "sk-test", 128 }, 129 ) 130 monkeypatch.setattr( 131 "agent.account_usage.httpx.Client", 132 lambda timeout=10.0: _RoutingClient( 133 { 134 "https://openrouter.ai/api/v1/credits": { 135 "data": {"total_credits": 300.0, "total_usage": 10.92} 136 }, 137 "https://openrouter.ai/api/v1/key": { 138 "data": { 139 "limit": 100.0, 140 "limit_remaining": 70.0, 141 "limit_reset": "monthly", 142 "usage": 12.5, 143 "usage_daily": 0.5, 144 "usage_weekly": 2.0, 145 "usage_monthly": 8.0, 146 "rate_limit": {"requests": -1, "interval": "10s"}, 147 } 148 }, 149 } 150 ), 151 ) 152 153 snapshot = fetch_account_usage("openrouter") 154 155 assert snapshot is not None 156 assert snapshot.windows == ( 157 AccountUsageWindow( 158 label="API key quota", 159 used_percent=30.0, 160 detail="$70.00 of $100.00 remaining • resets monthly", 161 ), 162 ) 163 assert "Credits balance: $289.08" in snapshot.details 164 assert "API key usage: $12.50 total • $0.50 today • $2.00 this week • $8.00 this month" in snapshot.details 165 assert all("-1 requests / 10s" not in line for line in render_account_usage_lines(snapshot)) 166 167 168 def test_fetch_account_usage_openrouter_omits_quota_window_when_key_has_no_limit(monkeypatch): 169 monkeypatch.setattr( 170 "agent.account_usage.resolve_runtime_provider", 171 lambda requested, explicit_base_url=None, explicit_api_key=None: { 172 "provider": "openrouter", 173 "base_url": "https://openrouter.ai/api/v1", 174 "api_key": "sk-test", 175 }, 176 ) 177 monkeypatch.setattr( 178 "agent.account_usage.httpx.Client", 179 lambda timeout=10.0: _RoutingClient( 180 { 181 "https://openrouter.ai/api/v1/credits": { 182 "data": {"total_credits": 100.0, "total_usage": 25.5} 183 }, 184 "https://openrouter.ai/api/v1/key": { 185 "data": { 186 "limit": None, 187 "limit_remaining": None, 188 "usage": 25.5, 189 "usage_daily": 1.25, 190 "usage_weekly": 4.5, 191 "usage_monthly": 18.0, 192 } 193 }, 194 } 195 ), 196 ) 197 198 snapshot = fetch_account_usage("openrouter") 199 200 assert snapshot is not None 201 assert snapshot.windows == () 202 assert "Credits balance: $74.50" in snapshot.details 203 assert "API key usage: $25.50 total • $1.25 today • $4.50 this week • $18.00 this month" in snapshot.details