from datetime import datetime, timezone import pytest from app.config import Settings from app.providers.codex import CodexProvider, parse_codex_result def payload(): return {'result': {'rateLimits': {'limitName': 'Plan', 'primary': { 'usedPercent': 27, 'windowDurationMins': 300, 'resetsAt': 1735689600}, 'secondary': { 'usedPercent': 54, 'windowDurationMins': 10080, 'resetsAt': 1735689600}, 'credits': {'hasCredits': True, 'unlimited': False, 'balance': '12.5'}}}} def test_codex_primary_secondary_and_credits(): limits = parse_codex_result(payload(), 'UTC', datetime(2025, 1, 1, tzinfo=timezone.utc)) assert [limit.name for limit in limits] == ['5 Hour Limit', 'Weekly Limit', 'Additional Credits'] assert limits[0].remaining_percent == 73 assert limits[1].used_percent == 54 assert limits[2].remaining == 12.5 def test_rate_limit_map_is_preferred_without_fallback_duplicate(): data = payload() data['result']['rateLimitsByLimitId'] = {'mapped': data['result']['rateLimits']} data['result']['rateLimits'] = {'primary': {'usedPercent': 1, 'windowDurationMins': 300}} limits = parse_codex_result(data, 'UTC') assert len(limits) == 3 assert all(limit.id.startswith('mapped-') for limit in limits) def test_empty_rate_limit_map_falls_back_to_snapshot(): data = payload() data['result']['rateLimitsByLimitId'] = {} limits = parse_codex_result(data, 'UTC') assert [limit.name for limit in limits] == ['5 Hour Limit', 'Weekly Limit', 'Additional Credits'] assert all(limit.id.startswith('rateLimits-') for limit in limits) def test_malformed_percentage_is_rejected(): data = payload() data['result']['rateLimits']['primary']['usedPercent'] = 140 with pytest.raises(ValueError): parse_codex_result(data, 'UTC') @pytest.mark.asyncio async def test_missing_codex_executable_is_generic(): provider = CodexProvider(Settings(codex_command='definitely-not-a-codex-executable')) result = await provider.get_usage() assert result.status == 'error' assert result.error == 'Unable to retrieve usage information'