@@ -1751,6 +1751,78 @@ def test_get_catalog_configs(self, project_dir):
17511751 assert configs [0 ]["name" ] == "default"
17521752 assert isinstance (configs [0 ]["install_allowed" ], bool )
17531753
1754+ def test_load_catalog_config_non_dict_yaml_raises (self , project_dir ):
1755+ """A YAML catalog config that is a list (not a mapping) must raise WorkflowValidationError."""
1756+ from specify_cli .workflows .catalog import WorkflowCatalog , WorkflowValidationError
1757+
1758+ config_path = project_dir / ".specify" / "workflow-catalogs.yml"
1759+ config_path .write_text ("- item1\n - item2\n " , encoding = "utf-8" )
1760+
1761+ catalog = WorkflowCatalog (project_dir )
1762+ with pytest .raises (WorkflowValidationError , match = "expected a mapping" ):
1763+ catalog .get_active_catalogs ()
1764+
1765+ def test_add_catalog_malformed_yaml_raises (self , project_dir ):
1766+ """A malformed YAML config file must raise WorkflowValidationError when adding a catalog."""
1767+ from specify_cli .workflows .catalog import WorkflowCatalog , WorkflowValidationError
1768+
1769+ config_path = project_dir / ".specify" / "workflow-catalogs.yml"
1770+ config_path .write_text (": invalid: yaml: {\n " , encoding = "utf-8" )
1771+
1772+ catalog = WorkflowCatalog (project_dir )
1773+ with pytest .raises (WorkflowValidationError , match = "unreadable or malformed" ):
1774+ catalog .add_catalog ("https://example.com/new.json" )
1775+
1776+ def test_remove_catalog_malformed_yaml_raises (self , project_dir ):
1777+ """A malformed YAML config file must raise WorkflowValidationError when removing a catalog."""
1778+ from specify_cli .workflows .catalog import WorkflowCatalog , WorkflowValidationError
1779+
1780+ catalog = WorkflowCatalog (project_dir )
1781+ catalog .add_catalog ("https://example.com/c1.json" , "first" )
1782+
1783+ config_path = project_dir / ".specify" / "workflow-catalogs.yml"
1784+ config_path .write_text (": bad: yaml: {\n " , encoding = "utf-8" )
1785+
1786+ with pytest .raises (WorkflowValidationError , match = "unreadable or malformed" ):
1787+ catalog .remove_catalog (0 )
1788+
1789+ def test_add_catalog_wraps_write_oserror (self , project_dir , monkeypatch ):
1790+ """An OSError on write must be wrapped as WorkflowValidationError."""
1791+ from specify_cli .workflows .catalog import WorkflowCatalog , WorkflowValidationError
1792+ import builtins
1793+
1794+ catalog = WorkflowCatalog (project_dir )
1795+ config_path = project_dir / ".specify" / "workflow-catalogs.yml"
1796+ real_open = builtins .open
1797+
1798+ def _raising_open (file , mode = "r" , * args , ** kwargs ):
1799+ if Path (file ) == config_path and "w" in mode :
1800+ raise OSError ("simulated write failure" )
1801+ return real_open (file , mode , * args , ** kwargs )
1802+
1803+ monkeypatch .setattr (builtins , "open" , _raising_open )
1804+ with pytest .raises (WorkflowValidationError , match = "Failed to write catalog config" ):
1805+ catalog .add_catalog ("https://example.com/new-catalog.json" , "my-catalog" )
1806+
1807+ def test_remove_catalog_wraps_write_oserror (self , project_dir , monkeypatch ):
1808+ """An OSError on write must be wrapped as WorkflowValidationError."""
1809+ from specify_cli .workflows .catalog import WorkflowCatalog , WorkflowValidationError
1810+ import builtins
1811+
1812+ catalog = WorkflowCatalog (project_dir )
1813+ catalog .add_catalog ("https://example.com/c1.json" , "first" )
1814+ config_path = project_dir / ".specify" / "workflow-catalogs.yml"
1815+ real_open = builtins .open
1816+
1817+ def _raising_open (file , mode = "r" , * args , ** kwargs ):
1818+ if Path (file ) == config_path and "w" in mode :
1819+ raise OSError ("simulated write failure" )
1820+ return real_open (file , mode , * args , ** kwargs )
1821+
1822+ monkeypatch .setattr (builtins , "open" , _raising_open )
1823+ with pytest .raises (WorkflowValidationError , match = "Failed to write catalog config" ):
1824+ catalog .remove_catalog (0 )
1825+
17541826
17551827# ===== Integration Test =====
17561828
0 commit comments