Skip to content

Commit 24b5e86

Browse files
authored
Merge pull request #116 from dave-shawley/validate-patch-doc
Fix #110 - validate patch document during creation
2 parents 511cbc2 + 3bb3351 commit 24b5e86

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# .coveragerc to control coverage.py
22
[run]
33
branch = True
4+
source = jsonpatch
45

56
[report]
7+
show_missing = True
68
# Regexes for lines to exclude from consideration
79
exclude_lines =
810
# Have to re-enable the standard pragma

jsonpatch.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ def __init__(self, patch):
225225
'copy': CopyOperation,
226226
}
227227

228+
# Verify that the structure of the patch document
229+
# is correct by retrieving each patch element.
230+
# Much of the validation is done in the initializer
231+
# though some is delayed until the patch is applied.
232+
for op in self.patch:
233+
self._get_operation(op)
234+
228235
def __str__(self):
229236
"""str(self) -> self.to_string()"""
230237
return self.to_string()

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
coverage
12
wheel
23
pypandoc

tests.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,85 @@ def test_create_with_pointer(self):
729729
self.assertEqual(result, expected)
730730

731731

732+
class JsonPatchCreationTest(unittest.TestCase):
733+
734+
def test_creation_fails_with_invalid_patch(self):
735+
invalid_patches = [
736+
{ 'path': '/foo', 'value': 'bar'},
737+
{'op': 0xADD, 'path': '/foo', 'value': 'bar'},
738+
{'op': 'boo', 'path': '/foo', 'value': 'bar'},
739+
{'op': 'add', 'value': 'bar'},
740+
]
741+
for patch in invalid_patches:
742+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
743+
jsonpatch.JsonPatch([patch])
744+
745+
with self.assertRaises(jsonpointer.JsonPointerException):
746+
jsonpatch.JsonPatch([{'op': 'add', 'path': 'foo', 'value': 'bar'}])
747+
748+
749+
class UtilityMethodTests(unittest.TestCase):
750+
751+
def test_boolean_coercion(self):
752+
empty_patch = jsonpatch.JsonPatch([])
753+
self.assertFalse(empty_patch)
754+
755+
def test_patch_equality(self):
756+
p = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'bar'}])
757+
q = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'bar'}])
758+
different_op = jsonpatch.JsonPatch([{'op': 'remove', 'path': '/foo'}])
759+
different_path = jsonpatch.JsonPatch([{'op': 'add', 'path': '/bar', 'value': 'bar'}])
760+
different_value = jsonpatch.JsonPatch([{'op': 'add', 'path': '/foo', 'value': 'foo'}])
761+
self.assertNotEqual(p, different_op)
762+
self.assertNotEqual(p, different_path)
763+
self.assertNotEqual(p, different_value)
764+
self.assertEqual(p, q)
765+
766+
def test_operation_equality(self):
767+
add = jsonpatch.AddOperation({'path': '/new-element', 'value': 'new-value'})
768+
add2 = jsonpatch.AddOperation({'path': '/new-element', 'value': 'new-value'})
769+
rm = jsonpatch.RemoveOperation({'path': '/target'})
770+
self.assertEqual(add, add2)
771+
self.assertNotEqual(add, rm)
772+
773+
def test_add_operation_structure(self):
774+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
775+
jsonpatch.AddOperation({'path': '/'}).apply({})
776+
777+
def test_replace_operation_structure(self):
778+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
779+
jsonpatch.ReplaceOperation({'path': '/'}).apply({})
780+
781+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
782+
jsonpatch.ReplaceOperation({'path': '/top/-', 'value': 'foo'}).apply({'top': {'inner': 'value'}})
783+
784+
with self.assertRaises(jsonpatch.JsonPatchConflict):
785+
jsonpatch.ReplaceOperation({'path': '/top/missing', 'value': 'foo'}).apply({'top': {'inner': 'value'}})
786+
787+
def test_move_operation_structure(self):
788+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
789+
jsonpatch.MoveOperation({'path': '/target'}).apply({})
790+
791+
with self.assertRaises(jsonpatch.JsonPatchConflict):
792+
jsonpatch.MoveOperation({'from': '/source', 'path': '/target'}).apply({})
793+
794+
def test_test_operation_structure(self):
795+
with self.assertRaises(jsonpatch.JsonPatchTestFailed):
796+
jsonpatch.TestOperation({'path': '/target'}).apply({})
797+
798+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
799+
jsonpatch.TestOperation({'path': '/target'}).apply({'target': 'value'})
800+
801+
def test_copy_operation_structure(self):
802+
with self.assertRaises(jsonpatch.InvalidJsonPatch):
803+
jsonpatch.CopyOperation({'path': '/target'}).apply({})
804+
805+
with self.assertRaises(jsonpatch.JsonPatchConflict):
806+
jsonpatch.CopyOperation({'path': '/target', 'from': '/source'}).apply({})
807+
808+
with self.assertRaises(jsonpatch.JsonPatchConflict):
809+
jsonpatch.CopyOperation({'path': '/target', 'from': '/source'}).apply({})
810+
732811

733812
if __name__ == '__main__':
734813
modules = ['jsonpatch']
@@ -745,6 +824,8 @@ def get_suite():
745824
suite.addTest(unittest.makeSuite(ConflictTests))
746825
suite.addTest(unittest.makeSuite(OptimizationTests))
747826
suite.addTest(unittest.makeSuite(JsonPointerTests))
827+
suite.addTest(unittest.makeSuite(JsonPatchCreationTest))
828+
suite.addTest(unittest.makeSuite(UtilityMethodTests))
748829
return suite
749830

750831

0 commit comments

Comments
 (0)