diff --git a/swh/model/model.py b/swh/model/model.py --- a/swh/model/model.py +++ b/swh/model/model.py @@ -772,6 +772,11 @@ perms = attr.ib(type=int, validator=type_validator(), converter=int, repr=oct) """Usually one of the values of `swh.model.from_disk.DentryPerms`.""" + @name.validator + def check_name(self, attribute, value): + if b"/" in value: + raise ValueError("{value!r} is not a valid directory entry name.") + @attr.s(frozen=True, slots=True) class Directory(HashableObject, BaseModel): @@ -784,6 +789,16 @@ git_object = git_objects.directory_git_object(self) return hashlib.new("sha1", git_object).digest() + @entries.validator + def check_entries(self, attribute, value): + seen = set() + for entry in value: + if entry.name in seen: + raise ValueError( + "{self.swhid()} has duplicated entry name: {entry.name!r}" + ) + seen.add(entry.name) + @classmethod def from_dict(cls, d): d = d.copy() diff --git a/swh/model/tests/test_model.py b/swh/model/tests/test_model.py --- a/swh/model/tests/test_model.py +++ b/swh/model/tests/test_model.py @@ -24,6 +24,7 @@ BaseModel, Content, Directory, + DirectoryEntry, MetadataAuthority, MetadataAuthorityType, MetadataFetcher, @@ -737,6 +738,30 @@ ) +# Directory + + +def test_directory_entry_name_validation(): + with pytest.raises(ValueError, match="valid directory entry name."): + DirectoryEntry(name=b"foo/", type="dir", target=b"\x00" * 20, perms=0), + + +def test_directory_duplicate_entry_name(): + entries = ( + DirectoryEntry(name=b"foo", type="file", target=b"\x00" * 20, perms=0), + DirectoryEntry(name=b"foo", type="dir", target=b"\x01" * 20, perms=1), + ) + with pytest.raises(ValueError, match="duplicated entry name"): + Directory(entries=entries) + + entries = ( + DirectoryEntry(name=b"foo", type="file", target=b"\x00" * 20, perms=0), + DirectoryEntry(name=b"foo", type="file", target=b"\x00" * 20, perms=0), + ) + with pytest.raises(ValueError, match="duplicated entry name"): + Directory(entries=entries) + + # Revision