|
| 1 | +"""`python -m zhub doctor` — install/environment check output. |
| 2 | +
|
| 3 | +The brain-credential section is derived from the adapter REGISTRY rather |
| 4 | +than a hardcoded key list, so adding a new adapter can never silently drop |
| 5 | +its env var from the doctor report. These tests pin that contract. |
| 6 | +""" |
| 7 | + |
| 8 | +import pytest |
| 9 | + |
| 10 | +from zhub import cli_doctor |
| 11 | +from zhub.brains import REGISTRY |
| 12 | + |
| 13 | + |
| 14 | +@pytest.fixture |
| 15 | +def doctor_output(monkeypatch, capsys): |
| 16 | + """Run `doctor` with network probing stubbed out, return captured stdout.""" |
| 17 | + # Keep REGISTRY real (that's what we're testing) but avoid the live |
| 18 | + # detection probes so the test stays offline and fast. |
| 19 | + monkeypatch.setattr("zhub.brains.list_available", lambda: []) |
| 20 | + |
| 21 | + def run(env=None): |
| 22 | + for cls in REGISTRY: |
| 23 | + for key in cls.env_keys: |
| 24 | + monkeypatch.delenv(key, raising=False) |
| 25 | + for k, v in (env or {}).items(): |
| 26 | + monkeypatch.setenv(k, v) |
| 27 | + cli_doctor.run([]) |
| 28 | + return capsys.readouterr().out |
| 29 | + |
| 30 | + return run |
| 31 | + |
| 32 | + |
| 33 | +def test_lists_every_adapter_env_key(doctor_output): |
| 34 | + out = doctor_output() |
| 35 | + for cls in REGISTRY: |
| 36 | + for key in cls.env_keys: |
| 37 | + assert key in out, f"{key} ({cls.name}) missing from doctor report" |
| 38 | + |
| 39 | + |
| 40 | +def test_includes_previously_dropped_keys(doctor_output): |
| 41 | + # Regression guard: these four belonged to adapters added after the |
| 42 | + # original hardcoded 4-key list and were silently absent from the report. |
| 43 | + out = doctor_output() |
| 44 | + for key in ("ANTHROPIC_API_KEY", "TOGETHER_API_KEY", |
| 45 | + "MISTRAL_API_KEY", "COHERE_API_KEY"): |
| 46 | + assert key in out |
| 47 | + |
| 48 | + |
| 49 | +def test_set_vs_unset_marking(doctor_output): |
| 50 | + out = doctor_output({"ANTHROPIC_API_KEY": "sk-test"}) |
| 51 | + lines = {ln.split()[1]: ln for ln in out.splitlines() |
| 52 | + if "API_KEY" in ln or "OLLAMA_HOST" in ln} |
| 53 | + assert "✓" in lines["ANTHROPIC_API_KEY"] |
| 54 | + assert "set" in lines["ANTHROPIC_API_KEY"] |
| 55 | + assert "✗" in lines["GROQ_API_KEY"] |
| 56 | + assert "not set" in lines["GROQ_API_KEY"] |
| 57 | + |
| 58 | + |
| 59 | +def test_each_env_key_reported_once(doctor_output): |
| 60 | + out = doctor_output() |
| 61 | + for cls in REGISTRY: |
| 62 | + for key in cls.env_keys: |
| 63 | + # one line per key in the brain-availability section |
| 64 | + assert out.count(f" {key}") == 1, f"{key} reported more than once" |
0 commit comments