"""HTTP client unit tests (no live network).""" from __future__ import annotations import importlib import os import sys # Make the package importable when pytest runs from repo root sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) def _reload_client(monkeypatch, key="abc", url="http://test.invalid"): monkeypatch.setenv("AG_BIDS_API_URL", url) monkeypatch.setenv("AG_BIDS_API_KEY", key) from ag_bids_mcp import client importlib.reload(client) return client def test_missing_api_key_raises(monkeypatch): monkeypatch.setenv("AG_BIDS_API_URL", "http://test.invalid") monkeypatch.delenv("AG_BIDS_API_KEY", raising=False) from ag_bids_mcp import client importlib.reload(client) import pytest with pytest.raises(client.AgBidsError): client.latest() def test_get_sends_api_key_header(monkeypatch): client = _reload_client(monkeypatch, key="topsecret") captured = {} class FakeResp: status_code = 200 text = "" def json(self): return {"count": 0, "rows": []} def fake_get(url, params=None, timeout=None, headers=None): captured["url"] = url captured["params"] = dict(params or {}) captured["headers"] = dict(headers or {}) return FakeResp() monkeypatch.setattr(client.httpx, "get", fake_get) out = client.latest(commodity="corn") assert captured["headers"]["X-API-Key"] == "topsecret" assert captured["url"].endswith("/api/data/latest") assert captured["params"] == {"commodity": "corn"} assert out == {"count": 0, "rows": []} def test_get_drops_none_params(monkeypatch): client = _reload_client(monkeypatch) captured = {} class FakeResp: status_code = 200 text = "" def json(self): return {"count": 0, "rows": []} def fake_get(url, params=None, timeout=None, headers=None): captured["params"] = dict(params or {}) return FakeResp() monkeypatch.setattr(client.httpx, "get", fake_get) client.latest(commodity="corn", source=None, delivery="", kind=None) # None and "" should both be dropped assert captured["params"] == {"commodity": "corn"} def test_get_raises_on_non_200(monkeypatch): client = _reload_client(monkeypatch) class FakeResp: status_code = 401 text = "no key" def json(self): return {} monkeypatch.setattr(client.httpx, "get", lambda *a, **k: FakeResp()) import pytest with pytest.raises(client.AgBidsError): client.best("corn") def test_get_raises_on_network_error(monkeypatch): client = _reload_client(monkeypatch) def boom(*a, **k): raise client.httpx.ConnectError("network is unreachable") monkeypatch.setattr(client.httpx, "get", boom) import pytest with pytest.raises(client.AgBidsError): client.sources() def test_each_endpoint_hits_expected_path(monkeypatch): client = _reload_client(monkeypatch) calls = [] class FakeResp: status_code = 200 text = "" def json(self): return {} def fake_get(url, params=None, timeout=None, headers=None): calls.append((url, dict(params or {}))) return FakeResp() monkeypatch.setattr(client.httpx, "get", fake_get) client.latest() client.history("corn", days=7) client.best("soy") client.inputs(product="lime") client.sources() client.deliveries("corn") client.todays_summary() paths = [u.replace("http://test.invalid", "") for u, _ in calls] assert paths == [ "/api/data/latest", "/api/data/history", "/api/data/best", "/api/data/inputs", "/api/data/sources", "/api/data/deliveries", "/api/brief/snapshot", ] # history call sent commodity + days assert calls[1][1] == {"commodity": "corn", "days": 7} # best call sent only commodity assert calls[2][1] == {"commodity": "soy"} # todays_summary uses kind=morning assert calls[6][1] == {"kind": "morning"}