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.yangtools.yang.common.ErrorTag;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
19 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
20 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
22 public class JsonPatchBodyTest extends AbstractPatchBodyTest {
23 public JsonPatchBodyTest() {
24 super(JsonPatchBody::new);
28 public final void modulePatchDataTest() throws Exception {
29 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
31 "ietf-yang-patch:yang-patch" : {
32 "patch-id" : "test-patch",
33 "comment" : "this is test patch",
37 "operation": "replace",
38 "target": "/my-list2=my-leaf20",
42 "my-leaf21": "I am leaf21-0",
43 "my-leaf22": "I am leaf22-0"
49 "operation": "replace",
50 "target": "/my-list2=my-leaf20",
54 "my-leaf21": "I am leaf21-1",
55 "my-leaf22": "I am leaf22-1",
56 "my-leaf-list": ["listelement"]
66 * Test of successful Patch consisting of create and delete Patch operations.
69 public final void modulePatchCreateAndDeleteTest() throws Exception {
70 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
72 "ietf-yang-patch:yang-patch" : {
73 "patch-id" : "test-patch",
74 "comment" : "this is test patch",
82 "my-leaf21": "I am leaf20"
86 "my-leaf21": "I am leaf21-1",
87 "my-leaf22": "I am leaf21-2"
91 "target": "/my-list2=my-leaf20",
96 "operation": "delete",
97 "target": "/my-list2=my-leaf20"
105 * Test trying to use Patch create operation which requires value without value. Test should fail with
106 * {@link RestconfDocumentedException} with error code 400.
109 public final void modulePatchValueMissingNegativeTest() throws Exception {
110 final var ex = assertThrows(RestconfDocumentedException.class,
111 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
113 "ietf-yang-patch:yang-patch" : {
114 "patch-id" : "test-patch",
115 "comment" : "this is test patch",
119 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
121 "operation": "create"
126 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
130 * Test trying to use value with Patch delete operation which does not support value. Test should fail with
131 * {@link RestconfDocumentedException} with error code 400.
134 public final void modulePatchValueNotSupportedNegativeTest() throws Exception {
135 final var ex = assertThrows(RestconfDocumentedException.class,
136 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
138 "ietf-yang-patch:yang-patch" : {
139 "patch-id" : "test-patch",
140 "comment" : "this is test patch",
144 "operation": "delete",
145 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
158 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
162 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
165 public final void modulePatchCompleteTargetInURITest() throws Exception {
166 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
168 "ietf-yang-patch:yang-patch" : {
169 "patch-id" : "test-patch",
170 "comment" : "Test to create and replace data in container directly using / sign as a target",
174 "operation": "create",
180 "name": "my-list1 - A",
181 "my-leaf11": "I am leaf11-0",
182 "my-leaf12": "I am leaf12-1"
185 "name": "my-list1 - B",
186 "my-leaf11": "I am leaf11-0",
187 "my-leaf12": "I am leaf12-1"
195 "operation": "replace",
200 "name": "my-list1 - Replacing",
201 "my-leaf11": "I am leaf11-0",
202 "my-leaf12": "I am leaf12-1"
213 * Test of YANG Patch merge operation on list. Test consists of two edit operations - replace and merge.
216 public final void modulePatchMergeOperationOnListTest() throws Exception {
217 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
219 "ietf-yang-patch:yang-patch" : {
220 "patch-id" : "Test merge operation",
221 "comment" : "This is test patch for merge operation on list",
225 "operation": "replace",
226 "target": "/my-list2=my-leaf20",
230 "my-leaf21": "I am leaf21-0",
231 "my-leaf22": "I am leaf22-0"
237 "operation": "merge",
238 "target": "/my-list2=my-leaf21",
242 "my-leaf21": "I am leaf21-1",
243 "my-leaf22": "I am leaf22-1"
253 * Test of YANG Patch merge operation on container. Test consists of two edit operations - create and merge.
256 public final void modulePatchMergeOperationOnContainerTest() throws Exception {
257 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
259 "ietf-yang-patch:yang-patch" : {
260 "patch-id" : "Test merge operation",
261 "comment" : "This is test patch for merge operation on container",
265 "operation": "create",
271 "name": "my-list1 - A",
272 "my-leaf11": "I am leaf11-0",
273 "my-leaf12": "I am leaf12-1"
276 "name": "my-list1 - B",
277 "my-leaf11": "I am leaf11-0",
278 "my-leaf12": "I am leaf12-1"
286 "operation": "merge",
291 "name": "my-list1 - Merged",
292 "my-leaf11": "I am leaf11-0",
293 "my-leaf12": "I am leaf12-1"
304 * Test reading simple leaf value.
307 public final void modulePatchSimpleLeafValueTest() throws Exception {
308 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
310 "ietf-yang-patch:yang-patch" : {
311 "patch-id" : "test-patch",
312 "comment" : "this is test patch for simple leaf value",
316 "operation": "replace",
317 "target": "/my-list2=my-leaf20/name",
325 checkPatchContext(returnValue);
326 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.entities().get(0).getNode());
330 * Test of YANG Patch on the top-level container with empty URI for data root.
333 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
334 checkPatchContext(parse(mountPrefix(), "", """
336 "ietf-yang-patch:yang-patch" : {
337 "patch-id" : "test-patch",
338 "comment" : "Test patch applied to the top-level container with empty URI",
342 "operation": "replace",
343 "target": "/instance-identifier-patch-module:patch-cont",
360 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
363 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
364 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
366 "ietf-yang-patch:yang-patch": {
367 "patch-id": "test-patch",
368 "comment": "Test patch applied to the top-level container with '/' in target",
372 "operation": "replace",
378 "name": "my-leaf-set",
379 "my-leaf11": "leaf-a",
380 "my-leaf12": "leaf-b"
389 checkPatchContext(returnValue);
390 assertEquals(Builders.containerBuilder()
391 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
392 .withChild(Builders.mapBuilder()
393 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
394 .withChild(Builders.mapEntryBuilder()
395 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
396 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
397 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
398 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
401 .build(), returnValue.entities().get(0).getNode());
405 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
408 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
409 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
412 "ietf-yang-patch:yang-patch": {
413 "patch-id": "test-patch",
414 "comment": "Test patch applied to the second-level list with '/' in target",
418 "operation": "replace",
423 "name": "my-leaf-set",
424 "my-leaf11": "leaf-a",
425 "my-leaf12": "leaf-b"
433 checkPatchContext(returnValue);
434 assertEquals(Builders.mapBuilder()
435 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
436 .withChild(Builders.mapEntryBuilder()
437 .withNodeIdentifier(NodeIdentifierWithPredicates.of(
438 MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
439 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
440 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
441 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
443 .build(), returnValue.entities().get(0).getNode());
447 * Test of Yang Patch on the top augmented element.
450 public final void modulePatchTargetTopLevelAugmentedContainerTest() throws Exception {
451 final var returnValue = parse(mountPrefix(), "", """
453 "ietf-yang-patch:yang-patch": {
454 "patch-id": "test-patch",
455 "comment": "comment",
459 "operation": "replace",
460 "target": "/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug",
470 checkPatchContext(returnValue);
471 assertEquals(Builders.containerBuilder()
472 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
473 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
474 .build(), returnValue.entities().get(0).getNode());
478 * Test of YANG Patch on the system map node element.
481 public final void modulePatchTargetMapNodeTest() throws Exception {
482 final var returnValue = parse(mountPrefix(), "", """
484 "ietf-yang-patch:yang-patch": {
485 "patch-id": "map-patch",
486 "comment": "comment",
490 "operation": "replace",
491 "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
502 checkPatchContext(returnValue);
503 assertEquals(Builders.mapBuilder()
504 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
505 .withChild(Builders.mapEntryBuilder()
506 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
507 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
508 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
510 .build(), returnValue.entities().get(0).getNode());
514 * Test of Yang Patch on the leaf set node element.
517 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
518 final var returnValue = parse(mountPrefix(), "", """
520 "ietf-yang-patch:yang-patch": {
521 "patch-id": "set-patch",
522 "comment": "comment",
526 "operation": "replace",
527 "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
529 "my-set": [ "data1" ]
535 checkPatchContext(returnValue);
536 assertEquals(Builders.leafSetBuilder()
537 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
538 .withChild(Builders.leafSetEntryBuilder()
539 .withNodeIdentifier(new NodeWithValue<>(LEAF_SET_QNAME, "data1"))
542 .build(), returnValue.entities().get(0).getNode());
546 * Test of Yang Patch on the unkeyed list node element.
549 public final void modulePatchTargetUnkeyedListNodeTest() throws Exception {
550 final var returnValue = parse(mountPrefix(), "", """
552 "ietf-yang-patch:yang-patch": {
553 "patch-id": "list-patch",
554 "comment": "comment",
558 "operation": "replace",
559 "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
570 checkPatchContext(returnValue);
571 assertEquals(Builders.unkeyedListBuilder()
572 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
573 .withChild(Builders.unkeyedListEntryBuilder()
574 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
575 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
576 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
578 .build(), returnValue.entities().get(0).getNode());
582 * Test of Yang Patch on the case node element.
585 public final void modulePatchTargetCaseNodeTest() throws Exception {
586 final var returnValue = parse(mountPrefix(), "", """
588 "ietf-yang-patch:yang-patch": {
589 "patch-id": "choice-patch",
590 "comment": "comment",
594 "operation": "replace",
595 "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
605 checkPatchContext(returnValue);
606 assertEquals(Builders.containerBuilder()
607 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
608 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
609 .build(), returnValue.entities().get(0).getNode());