"""v3 M2 — OpenAI Codex provider: read rate-limit % from the Codex session rollouts. Codex writes a token_count event per turn whose payload.rate_limits mirrors Claude's model (primary=5h, secondary=weekly). The provider surfaces the freshest such snapshot; a window whose reset time has passed is reported as a fresh 0%. """ import asyncio import json import os import time from daemon.providers.openai_codex import OpenAICodexProvider def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) def _write_rollout(home, name, rate_limits_list, mtime=None): """Write a rollout JSONL with one token_count event per rate_limits dict.""" d = home / "sessions" / "2026" / "07" / "10" d.mkdir(parents=True, exist_ok=True) p = d / f"rollout-{name}.jsonl" lines = [] for i, rl in enumerate(rate_limits_list): lines.append(json.dumps({ "timestamp": f"2026-07-10T00:00:0{i}Z", "type": "event_msg", "payload": {"type": "token_count", "info": {"total_token_usage": {"total_tokens": 1}}, "rate_limits": rl}, })) p.write_text("\n".join(lines), encoding="utf-8") if mtime is not None: os.utime(p, (mtime, mtime)) return p def _rl(pri_used, pri_reset, sec_used, sec_reset, plan="plus", reached=None): return {"limit_id": "codex", "primary": {"used_percent": pri_used, "window_minutes": 300, "resets_at": pri_reset}, "secondary": {"used_percent": sec_used, "window_minutes": 10080, "resets_at": sec_reset}, "plan_type": plan, "rate_limit_reached_type": reached} def test_maps_windows_with_future_reset(tmp_path): now = time.time() _write_rollout(tmp_path, "a", [_rl(42.0, now + 3600, 10.0, now + 7200)]) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.ok is True assert st.s == 42.0 and 58 <= st.sr <= 60 assert st.w == 10.0 and 118 <= st.wr <= 120 assert st.st == "Plus" def test_past_reset_is_fresh_zero(tmp_path): now = time.time() # primary window already elapsed => 0% fresh, unknown reset; secondary still open _write_rollout(tmp_path, "a", [_rl(99.0, now - 100, 55.0, now + 600)]) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.s == 0.0 and st.sr == -1 assert st.w == 55.0 and 8 <= st.wr <= 10 def test_rate_limit_reached_marks_limited(tmp_path): now = time.time() _write_rollout(tmp_path, "a", [_rl(100.0, now + 60, 80.0, now + 600, reached="primary")]) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.st == "limited" and st.ok is True def test_last_snapshot_in_file_wins(tmp_path): now = time.time() _write_rollout(tmp_path, "a", [_rl(10.0, now + 600, 1.0, now + 600), _rl(73.0, now + 600, 2.0, now + 600)]) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.s == 73.0 # the later event, not the first def test_newest_file_wins(tmp_path): now = time.time() _write_rollout(tmp_path, "old", [_rl(11.0, now + 600, 0.0, now + 600)], mtime=now - 500) _write_rollout(tmp_path, "new", [_rl(88.0, now + 600, 0.0, now + 600)], mtime=now) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.s == 88.0 def test_skips_file_without_rate_limits(tmp_path): now = time.time() # newest file has a token_count with null rate_limits; older file has the data _write_rollout(tmp_path, "older", [_rl(64.0, now + 600, 0.0, now + 600)], mtime=now - 500) p = _write_rollout(tmp_path, "newer", [], mtime=now) p.write_text(json.dumps({ "timestamp": "2026-07-10T01:00:00Z", "type": "event_msg", "payload": {"type": "token_count", "info": {}, "rate_limits": None}, }), encoding="utf-8") os.utime(p, (now, now)) st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.ok is True and st.s == 64.0 def test_no_sessions_dir_reports_no_data(tmp_path): st = _run(OpenAICodexProvider(codex_home=str(tmp_path)).poll()) assert st.ok is False and st.st == "no data"