diff --git a/src/specify_cli/presets/_manifest.py b/src/specify_cli/presets/_manifest.py index b5a7ff03c3..6cd7d0d54e 100644 --- a/src/specify_cli/presets/_manifest.py +++ b/src/specify_cli/presets/_manifest.py @@ -97,7 +97,7 @@ def _validate(self): # Validate preset metadata pack = self.data["preset"] # Check presence AND type: the format/version checks below feed these - # values straight to ``re.match`` and ``packaging.Version``, both of + # values straight to ``re.fullmatch`` and ``packaging.Version``, both of # which raise a bare TypeError on a non-string. YAML makes that an easy # authoring slip -- unquoted ``version: 1.0`` parses as a float and # ``id: 2`` as an int -- and TypeError is not a PresetValidationError, @@ -117,7 +117,7 @@ def _validate(self): ) # Validate pack ID format - if not re.match(r'^[a-z0-9-]+$', pack["id"]): + if not re.fullmatch(r'[a-z0-9-]+', pack["id"]): raise PresetValidationError( f"Invalid preset ID '{pack['id']}': " "must be lowercase alphanumeric with hyphens only" diff --git a/tests/specify_cli/presets/test_manifest.py b/tests/specify_cli/presets/test_manifest.py index 27ac723daa..7fa2cc4992 100644 --- a/tests/specify_cli/presets/test_manifest.py +++ b/tests/specify_cli/presets/test_manifest.py @@ -199,15 +199,29 @@ def test_missing_pack_id(self, temp_dir, valid_pack_data): with pytest.raises(PresetValidationError, match="Missing preset.id"): PresetManifest(manifest_path) - def test_invalid_pack_id_format(self, temp_dir, valid_pack_data): + @pytest.mark.parametrize( + "pack_id", + ["Invalid_ID", "", "test-pack\n", "test-pack\r\n", "test\npack", + " test-pack", "test-pack ", "../test-pack", "test/pack"], + ) + def test_invalid_pack_id_format(self, temp_dir, valid_pack_data, pack_id): """Test invalid pack ID format.""" - valid_pack_data["preset"]["id"] = "Invalid_ID" + valid_pack_data["preset"]["id"] = pack_id manifest_path = temp_dir / "preset.yml" with open(manifest_path, 'w') as f: yaml.dump(valid_pack_data, f) with pytest.raises(PresetValidationError, match="Invalid preset ID"): PresetManifest(manifest_path) + @pytest.mark.parametrize("pack_id", ["test-pack", "preset123", "123", "a"]) + def test_valid_pack_id_format(self, temp_dir, valid_pack_data, pack_id): + """Accept IDs containing only lowercase letters, digits, and hyphens.""" + valid_pack_data["preset"]["id"] = pack_id + manifest_path = temp_dir / "preset.yml" + manifest_path.write_text(yaml.safe_dump(valid_pack_data), encoding="utf-8") + + assert PresetManifest(manifest_path).id == pack_id + def test_invalid_version(self, temp_dir, valid_pack_data): """Test invalid semantic version.""" valid_pack_data["preset"]["version"] = "not-a-version"