Repository: zimeon/ocfl-py

Issue:
Correctly handle validation with multiple entries for a given digest in `manifest`
See error case https://github.com/OCFL/fixtures/pull/78

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/39025051/badge)](https://coveralls.io/builds/39025051)

Coverage increased (+0.01%) to 94.524% when pulling **8b07e7c415611a0c7687c63d59568fa62d070097 on issue-52** into **b955d49be56e58ab0a012617f929ff6ec5eda100 on develop**.


Patch:
diff --git a/ocfl/data/validation-errors.json b/ocfl/data/validation-errors.json
index 56eed29..64f9dbf 100644
--- a/ocfl/data/validation-errors.json
+++ b/ocfl/data/validation-errors.json
@@ -513,7 +513,13 @@
        "en": "OCFL Object %s inventory manifest content path %s must not begin or end with /."
     }
   },
-  "E101": {
+  "E101a": {
+    "params": ["where", "path"],
+    "description": {
+       "en": "OCFL Object %s inventory manifest content path %s is repeated."
+    }
+  },
+  "E101b": {
     "params": ["where", "path"],
     "description": {
        "en": "OCFL Object %s inventory manifest content path %s used as both a directory and a file path."
diff --git a/ocfl/inventory_validator.py b/ocfl/inventory_validator.py
index 4a918b5..83a0f6e 100644
--- a/ocfl/inventory_validator.py
+++ b/ocfl/inventory_validator.py
@@ -157,7 +157,7 @@ class InventoryValidator():
             # Check for conflicting content paths
             for path in content_directories:
                 if path in content_paths:
-                    self.error("E101", path=path)
+                    self.error("E101b", path=path)
         return manifest_files, manifest_files_correct_format, unnormalized_digests
 
     def validate_fixity(self, fixity, manifest_files):
@@ -417,7 +417,10 @@ class InventoryValidator():
             if element in ('', '.', '..'):
                 self.error("E099", path=path)
                 return False
-        # Accumulate paths and directories
+        # Accumulate paths and directories if not seen before
+        if path in content_paths:
+            self.error("E101a", path=path)
+            return False
         content_paths.add(path)
         content_directories.add('/'.join([m.group(1)] + elements[0:-1]))
         return True


Test patch:
diff --git a/tests/test_inventory_validator.py b/tests/test_inventory_validator.py
index 5db1512..af61b9d 100644
--- a/tests/test_inventory_validator.py
+++ b/tests/test_inventory_validator.py
@@ -101,7 +101,7 @@ class TestAll(unittest.TestCase):
         log.clear()
         # Conflicting content paths
         iv.validate_manifest({"067eca3f5b024afa00aeac03a3c42dc0042bf43cba56104037abea8b365c0cf672f0e0c14c91b82bbce6b1464e231ac285d630a82cd4d4a7b194bea04d4b2eb7": ['v1/content/a', 'v1/content/a/b']})
-        self.assertEqual(log.errors, ['E101'])
+        self.assertEqual(log.errors, ['E101b'])
 
     def test_validate_fixity(self):
         """Test validate_fixity method."""
@@ -373,6 +373,9 @@ class TestAll(unittest.TestCase):
         log.clear()
         self.assertFalse(iv.check_content_path('v1/xyz/.', cp, cd))
         self.assertEqual(log.errors, ['E099'])
+        log.clear()
+        self.assertFalse(iv.check_content_path('v1/xyz/anything', cp, cd))
+        self.assertEqual(log.errors, ['E101a'])
         # Good cases
         log.clear()
         self.assertTrue(iv.check_content_path('v1/xyz/.secret/d', cp, cd))