2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.databind;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
13 import org.junit.Test;
14 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
15 import org.opendaylight.restconf.server.api.JsonPatchBody;
16 import org.opendaylight.yangtools.yang.common.ErrorTag;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
19 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
21 public class JsonPatchBodyTest extends AbstractPatchBodyTest {
22 public JsonPatchBodyTest() {
23 super(JsonPatchBody::new);
27 public final void modulePatchDataTest() throws Exception {
28 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
30 "ietf-yang-patch:yang-patch" : {
31 "patch-id" : "test-patch",
32 "comment" : "this is test patch",
36 "operation": "replace",
37 "target": "/my-list2=my-leaf20",
41 "my-leaf21": "I am leaf21-0",
42 "my-leaf22": "I am leaf22-0"
48 "operation": "replace",
49 "target": "/my-list2=my-leaf20",
53 "my-leaf21": "I am leaf21-1",
54 "my-leaf22": "I am leaf22-1",
55 "my-leaf-list": ["listelement"]
65 * Test of successful Patch consisting of create and delete Patch operations.
68 public final void modulePatchCreateAndDeleteTest() throws Exception {
69 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
71 "ietf-yang-patch:yang-patch" : {
72 "patch-id" : "test-patch",
73 "comment" : "this is test patch",
81 "my-leaf21": "I am leaf20"
85 "my-leaf21": "I am leaf21-1",
86 "my-leaf22": "I am leaf21-2"
90 "target": "/my-list2=my-leaf20",
95 "operation": "delete",
96 "target": "/my-list2=my-leaf20"
104 * Test trying to use Patch create operation which requires value without value. Test should fail with
105 * {@link RestconfDocumentedException} with error code 400.
108 public final void modulePatchValueMissingNegativeTest() throws Exception {
109 final var ex = assertThrows(RestconfDocumentedException.class,
110 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
112 "ietf-yang-patch:yang-patch" : {
113 "patch-id" : "test-patch",
114 "comment" : "this is test patch",
118 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
120 "operation": "create"
125 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
129 * Test trying to use value with Patch delete operation which does not support value. Test should fail with
130 * {@link RestconfDocumentedException} with error code 400.
133 public final void modulePatchValueNotSupportedNegativeTest() throws Exception {
134 final var ex = assertThrows(RestconfDocumentedException.class,
135 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
137 "ietf-yang-patch:yang-patch" : {
138 "patch-id" : "test-patch",
139 "comment" : "this is test patch",
143 "operation": "delete",
144 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
157 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
161 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
164 public final void modulePatchCompleteTargetInURITest() throws Exception {
165 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
167 "ietf-yang-patch:yang-patch" : {
168 "patch-id" : "test-patch",
169 "comment" : "Test to create and replace data in container directly using / sign as a target",
173 "operation": "create",
179 "name": "my-list1 - A",
180 "my-leaf11": "I am leaf11-0",
181 "my-leaf12": "I am leaf12-1"
184 "name": "my-list1 - B",
185 "my-leaf11": "I am leaf11-0",
186 "my-leaf12": "I am leaf12-1"
194 "operation": "replace",
199 "name": "my-list1 - Replacing",
200 "my-leaf11": "I am leaf11-0",
201 "my-leaf12": "I am leaf12-1"
212 * Test of YANG Patch merge operation on list. Test consists of two edit operations - replace and merge.
215 public final void modulePatchMergeOperationOnListTest() throws Exception {
216 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
218 "ietf-yang-patch:yang-patch" : {
219 "patch-id" : "Test merge operation",
220 "comment" : "This is test patch for merge operation on list",
224 "operation": "replace",
225 "target": "/my-list2=my-leaf20",
229 "my-leaf21": "I am leaf21-0",
230 "my-leaf22": "I am leaf22-0"
236 "operation": "merge",
237 "target": "/my-list2=my-leaf21",
241 "my-leaf21": "I am leaf21-1",
242 "my-leaf22": "I am leaf22-1"
252 * Test of YANG Patch merge operation on container. Test consists of two edit operations - create and merge.
255 public final void modulePatchMergeOperationOnContainerTest() throws Exception {
256 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
258 "ietf-yang-patch:yang-patch" : {
259 "patch-id" : "Test merge operation",
260 "comment" : "This is test patch for merge operation on container",
264 "operation": "create",
270 "name": "my-list1 - A",
271 "my-leaf11": "I am leaf11-0",
272 "my-leaf12": "I am leaf12-1"
275 "name": "my-list1 - B",
276 "my-leaf11": "I am leaf11-0",
277 "my-leaf12": "I am leaf12-1"
285 "operation": "merge",
290 "name": "my-list1 - Merged",
291 "my-leaf11": "I am leaf11-0",
292 "my-leaf12": "I am leaf12-1"
303 * Test reading simple leaf value.
306 public final void modulePatchSimpleLeafValueTest() throws Exception {
307 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
309 "ietf-yang-patch:yang-patch" : {
310 "patch-id" : "test-patch",
311 "comment" : "this is test patch for simple leaf value",
315 "operation": "replace",
316 "target": "/my-list2=my-leaf20/name",
324 checkPatchContext(returnValue);
325 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.entities().get(0).getNode());
329 * Test of YANG Patch on the top-level container with empty URI for data root.
332 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
333 checkPatchContext(parse(mountPrefix(), "", """
335 "ietf-yang-patch:yang-patch" : {
336 "patch-id" : "test-patch",
337 "comment" : "Test patch applied to the top-level container with empty URI",
341 "operation": "replace",
342 "target": "/instance-identifier-patch-module:patch-cont",
359 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
362 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
363 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
365 "ietf-yang-patch:yang-patch": {
366 "patch-id": "test-patch",
367 "comment": "Test patch applied to the top-level container with '/' in target",
371 "operation": "replace",
377 "name": "my-leaf-set",
378 "my-leaf11": "leaf-a",
379 "my-leaf12": "leaf-b"
388 checkPatchContext(returnValue);
389 assertEquals(ImmutableNodes.newContainerBuilder()
390 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
391 .withChild(ImmutableNodes.newSystemMapBuilder()
392 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
393 .withChild(ImmutableNodes.newMapEntryBuilder()
394 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
395 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
396 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
397 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
400 .build(), returnValue.entities().get(0).getNode());
404 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
407 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
408 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
411 "ietf-yang-patch:yang-patch": {
412 "patch-id": "test-patch",
413 "comment": "Test patch applied to the second-level list with '/' in target",
417 "operation": "replace",
422 "name": "my-leaf-set",
423 "my-leaf11": "leaf-a",
424 "my-leaf12": "leaf-b"
432 checkPatchContext(returnValue);
433 assertEquals(ImmutableNodes.newSystemMapBuilder()
434 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
435 .withChild(ImmutableNodes.newMapEntryBuilder()
436 .withNodeIdentifier(NodeIdentifierWithPredicates.of(
437 MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
438 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
439 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
440 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
442 .build(), returnValue.entities().get(0).getNode());
446 * Test of Yang Patch on the top augmented element.
449 public final void modulePatchTargetTopLevelAugmentedContainerTest() throws Exception {
450 final var returnValue = parse(mountPrefix(), "", """
452 "ietf-yang-patch:yang-patch": {
453 "patch-id": "test-patch",
454 "comment": "comment",
458 "operation": "replace",
459 "target": "/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug",
469 checkPatchContext(returnValue);
470 assertEquals(ImmutableNodes.newContainerBuilder()
471 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
472 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
473 .build(), returnValue.entities().get(0).getNode());
477 * Test of YANG Patch on the system map node element.
480 public final void modulePatchTargetMapNodeTest() throws Exception {
481 final var returnValue = parse(mountPrefix(), "", """
483 "ietf-yang-patch:yang-patch": {
484 "patch-id": "map-patch",
485 "comment": "comment",
489 "operation": "replace",
490 "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
501 checkPatchContext(returnValue);
502 assertEquals(ImmutableNodes.newSystemMapBuilder()
503 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
504 .withChild(ImmutableNodes.newMapEntryBuilder()
505 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
506 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
507 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
509 .build(), returnValue.entities().get(0).getNode());
513 * Test of Yang Patch on the leaf set node element.
516 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
517 final var returnValue = parse(mountPrefix(), "", """
519 "ietf-yang-patch:yang-patch": {
520 "patch-id": "set-patch",
521 "comment": "comment",
525 "operation": "replace",
526 "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
528 "my-set": [ "data1" ]
534 checkPatchContext(returnValue);
535 assertEquals(ImmutableNodes.newSystemLeafSetBuilder()
536 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
537 .withChildValue("data1")
538 .build(), returnValue.entities().get(0).getNode());
542 * Test of Yang Patch on the unkeyed list node element.
545 public final void modulePatchTargetUnkeyedListNodeTest() throws Exception {
546 final var returnValue = parse(mountPrefix(), "", """
548 "ietf-yang-patch:yang-patch": {
549 "patch-id": "list-patch",
550 "comment": "comment",
554 "operation": "replace",
555 "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
566 checkPatchContext(returnValue);
567 assertEquals(ImmutableNodes.newUnkeyedListBuilder()
568 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
569 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
570 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
571 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
572 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
574 .build(), returnValue.entities().get(0).getNode());
578 * Test of Yang Patch on the case node element.
581 public final void modulePatchTargetCaseNodeTest() throws Exception {
582 final var returnValue = parse(mountPrefix(), "", """
584 "ietf-yang-patch:yang-patch": {
585 "patch-id": "choice-patch",
586 "comment": "comment",
590 "operation": "replace",
591 "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
601 checkPatchContext(returnValue);
602 assertEquals(ImmutableNodes.newContainerBuilder()
603 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
604 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
605 .build(), returnValue.entities().get(0).getNode());