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.spi.node.ImmutableNodes;
20 public class JsonPatchBodyTest extends AbstractPatchBodyTest {
21 public JsonPatchBodyTest() {
22 super(JsonPatchBody::new);
26 public final void modulePatchDataTest() throws Exception {
27 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
29 "ietf-yang-patch:yang-patch" : {
30 "patch-id" : "test-patch",
31 "comment" : "this is test patch",
35 "operation": "replace",
36 "target": "/my-list2=my-leaf20",
40 "my-leaf21": "I am leaf21-0",
41 "my-leaf22": "I am leaf22-0"
47 "operation": "replace",
48 "target": "/my-list2=my-leaf20",
52 "my-leaf21": "I am leaf21-1",
53 "my-leaf22": "I am leaf22-1",
54 "my-leaf-list": ["listelement"]
64 * Test of successful Patch consisting of create and delete Patch operations.
67 public final void modulePatchCreateAndDeleteTest() throws Exception {
68 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
70 "ietf-yang-patch:yang-patch" : {
71 "patch-id" : "test-patch",
72 "comment" : "this is test patch",
80 "my-leaf21": "I am leaf20"
84 "my-leaf21": "I am leaf21-1",
85 "my-leaf22": "I am leaf21-2"
89 "target": "/my-list2=my-leaf20",
94 "operation": "delete",
95 "target": "/my-list2=my-leaf20"
103 * Test trying to use Patch create operation which requires value without value. Test should fail with
104 * {@link RestconfDocumentedException} with error code 400.
107 public final void modulePatchValueMissingNegativeTest() throws Exception {
108 final var ex = assertThrows(RestconfDocumentedException.class,
109 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
111 "ietf-yang-patch:yang-patch" : {
112 "patch-id" : "test-patch",
113 "comment" : "this is test patch",
117 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
119 "operation": "create"
124 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
128 * Test trying to use value with Patch delete operation which does not support value. Test should fail with
129 * {@link RestconfDocumentedException} with error code 400.
132 public final void modulePatchValueNotSupportedNegativeTest() throws Exception {
133 final var ex = assertThrows(RestconfDocumentedException.class,
134 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
136 "ietf-yang-patch:yang-patch" : {
137 "patch-id" : "test-patch",
138 "comment" : "this is test patch",
142 "operation": "delete",
143 "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name=\
156 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
160 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
163 public final void modulePatchCompleteTargetInURITest() throws Exception {
164 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
166 "ietf-yang-patch:yang-patch" : {
167 "patch-id" : "test-patch",
168 "comment" : "Test to create and replace data in container directly using / sign as a target",
172 "operation": "create",
178 "name": "my-list1 - A",
179 "my-leaf11": "I am leaf11-0",
180 "my-leaf12": "I am leaf12-1"
183 "name": "my-list1 - B",
184 "my-leaf11": "I am leaf11-0",
185 "my-leaf12": "I am leaf12-1"
193 "operation": "replace",
198 "name": "my-list1 - Replacing",
199 "my-leaf11": "I am leaf11-0",
200 "my-leaf12": "I am leaf12-1"
211 * Test of YANG Patch merge operation on list. Test consists of two edit operations - replace and merge.
214 public final void modulePatchMergeOperationOnListTest() throws Exception {
215 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
217 "ietf-yang-patch:yang-patch" : {
218 "patch-id" : "Test merge operation",
219 "comment" : "This is test patch for merge operation on list",
223 "operation": "replace",
224 "target": "/my-list2=my-leaf20",
228 "my-leaf21": "I am leaf21-0",
229 "my-leaf22": "I am leaf22-0"
235 "operation": "merge",
236 "target": "/my-list2=my-leaf21",
240 "my-leaf21": "I am leaf21-1",
241 "my-leaf22": "I am leaf22-1"
251 * Test of YANG Patch merge operation on container. Test consists of two edit operations - create and merge.
254 public final void modulePatchMergeOperationOnContainerTest() throws Exception {
255 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
257 "ietf-yang-patch:yang-patch" : {
258 "patch-id" : "Test merge operation",
259 "comment" : "This is test patch for merge operation on container",
263 "operation": "create",
269 "name": "my-list1 - A",
270 "my-leaf11": "I am leaf11-0",
271 "my-leaf12": "I am leaf12-1"
274 "name": "my-list1 - B",
275 "my-leaf11": "I am leaf11-0",
276 "my-leaf12": "I am leaf12-1"
284 "operation": "merge",
289 "name": "my-list1 - Merged",
290 "my-leaf11": "I am leaf11-0",
291 "my-leaf12": "I am leaf12-1"
302 * Test reading simple leaf value.
305 public final void modulePatchSimpleLeafValueTest() throws Exception {
306 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
308 "ietf-yang-patch:yang-patch" : {
309 "patch-id" : "test-patch",
310 "comment" : "this is test patch for simple leaf value",
314 "operation": "replace",
315 "target": "/my-list2=my-leaf20/name",
323 checkPatchContext(returnValue);
324 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.entities().get(0).getNode());
328 * Test of YANG Patch on the top-level container with empty URI for data root.
331 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
332 checkPatchContext(parse(mountPrefix(), "", """
334 "ietf-yang-patch:yang-patch" : {
335 "patch-id" : "test-patch",
336 "comment" : "Test patch applied to the top-level container with empty URI",
340 "operation": "replace",
341 "target": "/instance-identifier-patch-module:patch-cont",
358 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
361 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
362 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
364 "ietf-yang-patch:yang-patch": {
365 "patch-id": "test-patch",
366 "comment": "Test patch applied to the top-level container with '/' in target",
370 "operation": "replace",
376 "name": "my-leaf-set",
377 "my-leaf11": "leaf-a",
378 "my-leaf12": "leaf-b"
387 checkPatchContext(returnValue);
388 assertEquals(ImmutableNodes.newContainerBuilder()
389 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
390 .withChild(ImmutableNodes.newSystemMapBuilder()
391 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
392 .withChild(ImmutableNodes.newMapEntryBuilder()
393 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
394 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
395 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
396 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
399 .build(), returnValue.entities().get(0).getNode());
403 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
406 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
407 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
410 "ietf-yang-patch:yang-patch": {
411 "patch-id": "test-patch",
412 "comment": "Test patch applied to the second-level list with '/' in target",
416 "operation": "replace",
421 "name": "my-leaf-set",
422 "my-leaf11": "leaf-a",
423 "my-leaf12": "leaf-b"
431 checkPatchContext(returnValue);
432 assertEquals(ImmutableNodes.newSystemMapBuilder()
433 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
434 .withChild(ImmutableNodes.newMapEntryBuilder()
435 .withNodeIdentifier(NodeIdentifierWithPredicates.of(
436 MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
437 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
438 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
439 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
441 .build(), returnValue.entities().get(0).getNode());
445 * Test of Yang Patch on the top augmented element.
448 public final void modulePatchTargetTopLevelAugmentedContainerTest() throws Exception {
449 final var returnValue = parse(mountPrefix(), "", """
451 "ietf-yang-patch:yang-patch": {
452 "patch-id": "test-patch",
453 "comment": "comment",
457 "operation": "replace",
458 "target": "/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug",
468 checkPatchContext(returnValue);
469 assertEquals(ImmutableNodes.newContainerBuilder()
470 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
471 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
472 .build(), returnValue.entities().get(0).getNode());
476 * Test of YANG Patch on the system map node element.
479 public final void modulePatchTargetMapNodeTest() throws Exception {
480 final var returnValue = parse(mountPrefix(), "", """
482 "ietf-yang-patch:yang-patch": {
483 "patch-id": "map-patch",
484 "comment": "comment",
488 "operation": "replace",
489 "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
500 checkPatchContext(returnValue);
501 assertEquals(ImmutableNodes.newSystemMapBuilder()
502 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
503 .withChild(ImmutableNodes.newMapEntryBuilder()
504 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
505 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
506 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
508 .build(), returnValue.entities().get(0).getNode());
512 * Test of Yang Patch on the leaf set node element.
515 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
516 final var returnValue = parse(mountPrefix(), "", """
518 "ietf-yang-patch:yang-patch": {
519 "patch-id": "set-patch",
520 "comment": "comment",
524 "operation": "replace",
525 "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
527 "my-set": [ "data1" ]
533 checkPatchContext(returnValue);
534 assertEquals(ImmutableNodes.newSystemLeafSetBuilder()
535 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
536 .withChildValue("data1")
537 .build(), returnValue.entities().get(0).getNode());
541 * Test of Yang Patch on the unkeyed list node element.
544 public final void modulePatchTargetUnkeyedListNodeTest() throws Exception {
545 final var returnValue = parse(mountPrefix(), "", """
547 "ietf-yang-patch:yang-patch": {
548 "patch-id": "list-patch",
549 "comment": "comment",
553 "operation": "replace",
554 "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
565 checkPatchContext(returnValue);
566 assertEquals(ImmutableNodes.newUnkeyedListBuilder()
567 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
568 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
569 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
570 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
571 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
573 .build(), returnValue.entities().get(0).getNode());
577 * Test of Yang Patch on the case node element.
580 public final void modulePatchTargetCaseNodeTest() throws Exception {
581 final var returnValue = parse(mountPrefix(), "", """
583 "ietf-yang-patch:yang-patch": {
584 "patch-id": "choice-patch",
585 "comment": "comment",
589 "operation": "replace",
590 "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
600 checkPatchContext(returnValue);
601 assertEquals(ImmutableNodes.newContainerBuilder()
602 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
603 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
604 .build(), returnValue.entities().get(0).getNode());