diff --git a/backend/tests/test_doctor.py b/backend/tests/test_doctor.py index 556177e78..97c0ba97d 100644 --- a/backend/tests/test_doctor.py +++ b/backend/tests/test_doctor.py @@ -187,6 +187,14 @@ class TestCheckModelsConfigured: result = doctor.check_models_configured(tmp_path / "config.yaml") assert result.status == "skip" + def test_commented_out_models_block(self, tmp_path): + # config.example.yaml ships a `models:` key whose entries are all + # commented out, so it parses as None rather than an empty list. + cfg = tmp_path / "config.yaml" + cfg.write_text("config_version: 5\nmodels:\n # - name: default\n") + result = doctor.check_models_configured(cfg) + assert result.status == "fail" + # --------------------------------------------------------------------------- # check_llm_api_key @@ -216,6 +224,14 @@ class TestCheckLLMApiKey: results = doctor.check_llm_api_key(tmp_path / "config.yaml") assert results == [] + def test_commented_out_models_block_returns_empty(self, tmp_path): + # Regression: iterating a null `models:` raised TypeError, which the + # broad handler rendered as "('NoneType' object is not iterable)". + cfg = tmp_path / "config.yaml" + cfg.write_text("config_version: 5\nmodels:\n # - name: default\n") + results = doctor.check_llm_api_key(cfg) + assert results == [] + # --------------------------------------------------------------------------- # check_llm_auth @@ -237,6 +253,12 @@ class TestCheckLLMAuth: results = doctor.check_llm_auth(cfg) assert any(result.status == "ok" and "Claude auth available" in result.label for result in results) + def test_commented_out_models_block_returns_empty(self, tmp_path): + cfg = tmp_path / "config.yaml" + cfg.write_text("config_version: 5\nmodels:\n # - name: default\n") + assert doctor.check_llm_auth(cfg) == [] + assert doctor.check_llm_package(cfg) == [] + # --------------------------------------------------------------------------- # check_web_search diff --git a/scripts/doctor.py b/scripts/doctor.py index 79c2728b3..b2830fd3b 100644 --- a/scripts/doctor.py +++ b/scripts/doctor.py @@ -280,7 +280,7 @@ def check_models_configured(config_path: Path) -> CheckResult: return CheckResult("models configured", "skip") try: data = _load_yaml_file(config_path) - models = data.get("models", []) + models = data.get("models") or [] if models: return CheckResult("models configured", "ok", f"{len(models)} model(s)") return CheckResult( @@ -326,7 +326,7 @@ def check_llm_api_key(config_path: Path) -> list[CheckResult]: with open(config_path, encoding="utf-8") as f: data = yaml.safe_load(f) or {} - for model in data.get("models", []): + for model in data.get("models") or []: # Collect all values that look like $ENV_VAR references def _collect_env_refs(obj: object) -> list[str]: refs: list[str] = [] @@ -373,7 +373,7 @@ def check_llm_package(config_path: Path) -> list[CheckResult]: data = yaml.safe_load(f) or {} seen_packages: set[str] = set() - for model in data.get("models", []): + for model in data.get("models") or []: use = model.get("use", "") if ":" in use: package_path = use.split(":")[0] @@ -408,7 +408,7 @@ def check_llm_auth(config_path: Path) -> list[CheckResult]: results: list[CheckResult] = [] try: data = _load_yaml_file(config_path) - for model in data.get("models", []): + for model in data.get("models") or []: use = model.get("use", "") model_name = model.get("name", "default")