diff --git a/src/specify_cli/workflows/catalog.py b/src/specify_cli/workflows/catalog.py index 61f490631c..695f82e939 100644 --- a/src/specify_cli/workflows/catalog.py +++ b/src/specify_cli/workflows/catalog.py @@ -1394,12 +1394,14 @@ def add_catalog(self, url: str, name: str | None = None) -> None: data: dict[str, Any] = {"catalogs": []} if config_path.exists(): try: - raw = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {} + raw = yaml.safe_load(config_path.read_text(encoding="utf-8")) except (yaml.YAMLError, OSError, UnicodeDecodeError) as exc: raise StepValidationError( f"Catalog config file is unreadable or malformed: {exc}" ) from exc - if not isinstance(raw, dict): + if raw is None: + raw = {} + elif not isinstance(raw, dict): raise StepValidationError( "Catalog config file is corrupted (expected a mapping)." ) diff --git a/tests/test_workflows.py b/tests/test_workflows.py index 95baf22d3c..c0e6ff412d 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -8829,6 +8829,23 @@ def test_add_catalog_empty_yaml_file(self, project_dir): assert len(data["catalogs"]) == 1 assert data["catalogs"][0]["url"] == "https://example.com/steps.json" + @pytest.mark.parametrize("bad", [[], False, 0, ""]) + def test_add_catalog_rejects_falsy_non_mapping_config( + self, project_dir, bad + ): + from specify_cli.workflows.catalog import StepCatalog, StepValidationError + + config_path = project_dir / ".specify" / "step-catalogs.yml" + original = yaml.safe_dump(bad) + config_path.write_text(original, encoding="utf-8") + + with pytest.raises(StepValidationError, match="expected a mapping"): + StepCatalog(project_dir).add_catalog( + "https://example.com/steps.json", "my-steps" + ) + + assert config_path.read_text(encoding="utf-8") == original + def test_add_catalog_duplicate_rejected(self, project_dir): from specify_cli.workflows.catalog import StepCatalog, StepValidationError