diff options
author | Stefan Kögl <stefan@skoegl.net> | 2020-11-20 13:26:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-20 13:26:48 +0100 |
commit | 24b5e86dd66824dce232ea07e95d39a67b1dd735 (patch) | |
tree | bbcec9e1ba705b381b10f7bf9160b25271c62004 | |
parent | 511cbc25ec27068fa698818382ec19b6653f34ca (diff) | |
parent | 3bb33518194b0cbc6e1512dbeb2ac5ef548d8c72 (diff) | |
download | python-json-patch-24b5e86dd66824dce232ea07e95d39a67b1dd735.tar.gz |
Merge pull request #116 from dave-shawley/validate-patch-doc
Fix #110 - validate patch document during creation
-rw-r--r-- | .coveragerc | 2 | ||||
-rw-r--r-- | jsonpatch.py | 7 | ||||
-rw-r--r-- | requirements-dev.txt | 1 | ||||
-rwxr-xr-x | tests.py | 81 |
4 files changed, 91 insertions, 0 deletions
diff --git a/.coveragerc b/.coveragerc index 2a98e09..f0d91db 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,8 +1,10 @@ # .coveragerc to control coverage.py [run] branch = True +source = jsonpatch [report] +show_missing = True # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma diff --git a/jsonpatch.py b/jsonpatch.py index d7c1988..dc54efc 100644 --- a/jsonpatch.py +++ b/jsonpatch.py @@ -225,6 +225,13 @@ class JsonPatch(object): 'copy': CopyOperation, } + # Verify that the structure of the patch document + # is correct by retrieving each patch element. + # Much of the validation is done in the initializer + # though some is delayed until the patch is applied. + for op in self.patch: + self._get_operation(op) + def __str__(self): """str(self) -> self.to_string()""" return self.to_string() diff --git a/requirements-dev.txt b/requirements-dev.txt index 21daf9a..c729ece 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,3 @@ +coverage wheel pypandoc @@ -729,6 +729,85 @@ class JsonPointerTests(unittest.TestCase): self.assertEqual(result, expected) +class JsonPatchCreationTest(unittest.TestCase): + + def test_creation_fails_with_invalid_patch(self): + invalid_patches = [ + { 'path': '/foo', 'value': 'bar'}, + {'op': 0xADD, 'path': '/foo', 'value': 'bar'}, + {'op': 'boo', 'path': '/foo', 'value': 'bar'}, + {'op': 'add', 'value': 'bar'}, + ] + for patch in invalid_patches: + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.JsonPatch([patch]) + + with self.assertRaises(jsonpointer.JsonPointerException): + jsonpatch.JsonPatch([{'op': 'add', 'path': 'foo', 'value': 'bar'}]) + + +class UtilityMethodTests(unittest.TestCase): + + def test_boolean_coercion(self): + empty_patch = jsonpatch.JsonPatch([]) + self.assertFalse(empty_patch) + + def test_patch_equality(self): + p = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'bar'}]) + q = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'bar'}]) + different_op = jsonpatch.JsonPatch([{'op': 'remove', 'path': '/foo'}]) + different_path = jsonpatch.JsonPatch([{'op': 'add', 'path': '/bar', 'value': 'bar'}]) + different_value = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'foo'}]) + self.assertNotEqual(p, different_op) + self.assertNotEqual(p, different_path) + self.assertNotEqual(p, different_value) + self.assertEqual(p, q) + + def test_operation_equality(self): + add = jsonpatch.AddOperation({'path': '/new-element', 'value': 'new-value'}) + add2 = jsonpatch.AddOperation({'path': '/new-element', 'value': 'new-value'}) + rm = jsonpatch.RemoveOperation({'path': '/target'}) + self.assertEqual(add, add2) + self.assertNotEqual(add, rm) + + def test_add_operation_structure(self): + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.AddOperation({'path': '/'}).apply({}) + + def test_replace_operation_structure(self): + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.ReplaceOperation({'path': '/'}).apply({}) + + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.ReplaceOperation({'path': '/top/-', 'value': 'foo'}).apply({'top': {'inner': 'value'}}) + + with self.assertRaises(jsonpatch.JsonPatchConflict): + jsonpatch.ReplaceOperation({'path': '/top/missing', 'value': 'foo'}).apply({'top': {'inner': 'value'}}) + + def test_move_operation_structure(self): + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.MoveOperation({'path': '/target'}).apply({}) + + with self.assertRaises(jsonpatch.JsonPatchConflict): + jsonpatch.MoveOperation({'from': '/source', 'path': '/target'}).apply({}) + + def test_test_operation_structure(self): + with self.assertRaises(jsonpatch.JsonPatchTestFailed): + jsonpatch.TestOperation({'path': '/target'}).apply({}) + + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.TestOperation({'path': '/target'}).apply({'target': 'value'}) + + def test_copy_operation_structure(self): + with self.assertRaises(jsonpatch.InvalidJsonPatch): + jsonpatch.CopyOperation({'path': '/target'}).apply({}) + + with self.assertRaises(jsonpatch.JsonPatchConflict): + jsonpatch.CopyOperation({'path': '/target', 'from': '/source'}).apply({}) + + with self.assertRaises(jsonpatch.JsonPatchConflict): + jsonpatch.CopyOperation({'path': '/target', 'from': '/source'}).apply({}) + if __name__ == '__main__': modules = ['jsonpatch'] @@ -745,6 +824,8 @@ if __name__ == '__main__': suite.addTest(unittest.makeSuite(ConflictTests)) suite.addTest(unittest.makeSuite(OptimizationTests)) suite.addTest(unittest.makeSuite(JsonPointerTests)) + suite.addTest(unittest.makeSuite(JsonPatchCreationTest)) + suite.addTest(unittest.makeSuite(UtilityMethodTests)) return suite |