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 inputStream = JsonPatchBodyTest.class.getResourceAsStream(
136 "/instanceidentifier/json/jsonPATCHdataValueNotSupported.json");
137 final var ex = assertThrows(RestconfDocumentedException.class,
138 () -> parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", inputStream));
139 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
143 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
146 public final void modulePatchCompleteTargetInURITest() throws Exception {
147 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont",
148 JsonPatchBodyTest.class.getResourceAsStream(
149 "/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json")));
153 * Test of YANG Patch merge operation on list. Test consists of two edit operations - replace and merge.
156 public final void modulePatchMergeOperationOnListTest() throws Exception {
157 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1",
158 JsonPatchBodyTest.class.getResourceAsStream(
159 "/instanceidentifier/json/jsonPATCHMergeOperationOnList.json")));
163 * Test of YANG Patch merge operation on container. Test consists of two edit operations - create and merge.
166 public final void modulePatchMergeOperationOnContainerTest() throws Exception {
167 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont", """
169 "ietf-yang-patch:yang-patch" : {
170 "patch-id" : "Test merge operation",
171 "comment" : "This is test patch for merge operation on container",
175 "operation": "create",
181 "name": "my-list1 - A",
182 "my-leaf11": "I am leaf11-0",
183 "my-leaf12": "I am leaf12-1"
186 "name": "my-list1 - B",
187 "my-leaf11": "I am leaf11-0",
188 "my-leaf12": "I am leaf12-1"
196 "operation": "merge",
201 "name": "my-list1 - Merged",
202 "my-leaf11": "I am leaf11-0",
203 "my-leaf12": "I am leaf12-1"
214 * Test reading simple leaf value.
217 public final void modulePatchSimpleLeafValueTest() throws Exception {
218 final var returnValue = parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1",
219 JsonPatchBodyTest.class.getResourceAsStream("/instanceidentifier/json/jsonPATCHSimpleLeafValue.json"));
220 checkPatchContext(returnValue);
221 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.getData().get(0).getNode());
225 * Test of YANG Patch on the top-level container with empty URI for data root.
228 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
229 checkPatchContext(parse(mountPrefix(),
230 JsonPatchBodyTest.class.getResourceAsStream(
231 "/instanceidentifier/json/jsonPATCHTargetTopLevelContainerWithEmptyURI.json")));
235 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
238 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
239 final var returnValue = parse(mountPrefix() + "instance-identifier-patch-module:patch-cont", """
241 "ietf-yang-patch:yang-patch": {
242 "patch-id": "test-patch",
243 "comment": "Test patch applied to the top-level container with '/' in target",
247 "operation": "replace",
253 "name": "my-leaf-set",
254 "my-leaf11": "leaf-a",
255 "my-leaf12": "leaf-b"
264 checkPatchContext(returnValue);
265 assertEquals(Builders.containerBuilder()
266 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
267 .withChild(Builders.mapBuilder()
268 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
269 .withChild(Builders.mapEntryBuilder()
270 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
271 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
272 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
273 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
276 .build(), returnValue.getData().get(0).getNode());
280 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
283 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
284 final var returnValue = parse(
285 mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set", """
287 "ietf-yang-patch:yang-patch": {
288 "patch-id": "test-patch",
289 "comment": "Test patch applied to the second-level list with '/' in target",
293 "operation": "replace",
298 "name": "my-leaf-set",
299 "my-leaf11": "leaf-a",
300 "my-leaf12": "leaf-b"
308 checkPatchContext(returnValue);
309 assertEquals(Builders.mapBuilder()
310 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
311 .withChild(Builders.mapEntryBuilder()
312 .withNodeIdentifier(NodeIdentifierWithPredicates.of(
313 MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
314 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
315 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
316 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
318 .build(), returnValue.getData().get(0).getNode());
322 * Test of Yang Patch on the top augmented element.
325 public final void modulePatchTargetTopLevelAugmentedContainerTest() throws Exception {
326 final var returnValue = parse(mountPrefix(), """
328 "ietf-yang-patch:yang-patch": {
329 "patch-id": "test-patch",
330 "comment": "comment",
334 "operation": "replace",
335 "target": "/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug",
345 checkPatchContext(returnValue);
346 assertEquals(Builders.containerBuilder()
347 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
348 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
349 .build(), returnValue.getData().get(0).getNode());
353 * Test of YANG Patch on the system map node element.
356 public final void modulePatchTargetMapNodeTest() throws Exception {
357 final var returnValue = parse(mountPrefix(), """
359 "ietf-yang-patch:yang-patch": {
360 "patch-id": "map-patch",
361 "comment": "comment",
365 "operation": "replace",
366 "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
377 checkPatchContext(returnValue);
378 assertEquals(Builders.mapBuilder()
379 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
380 .withChild(Builders.mapEntryBuilder()
381 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
382 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
383 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
385 .build(), returnValue.getData().get(0).getNode());
389 * Test of Yang Patch on the leaf set node element.
392 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
393 final var returnValue = parse(mountPrefix(), """
395 "ietf-yang-patch:yang-patch": {
396 "patch-id": "set-patch",
397 "comment": "comment",
401 "operation": "replace",
402 "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
404 "my-set": [ "data1" ]
410 checkPatchContext(returnValue);
411 assertEquals(Builders.leafSetBuilder()
412 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
413 .withChild(Builders.leafSetEntryBuilder()
414 .withNodeIdentifier(new NodeWithValue<>(LEAF_SET_QNAME, "data1"))
417 .build(), returnValue.getData().get(0).getNode());
421 * Test of Yang Patch on the unkeyed list node element.
424 public final void modulePatchTargetUnkeyedListNodeTest() throws Exception {
425 final var returnValue = parse(mountPrefix(), """
427 "ietf-yang-patch:yang-patch": {
428 "patch-id": "list-patch",
429 "comment": "comment",
433 "operation": "replace",
434 "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
445 checkPatchContext(returnValue);
446 assertEquals(Builders.unkeyedListBuilder()
447 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
448 .withChild(Builders.unkeyedListEntryBuilder()
449 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
450 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
451 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
453 .build(), returnValue.getData().get(0).getNode());
457 * Test of Yang Patch on the case node element.
460 public final void modulePatchTargetCaseNodeTest() throws Exception {
461 final var returnValue = parse(mountPrefix(), """
463 "ietf-yang-patch:yang-patch": {
464 "patch-id": "choice-patch",
465 "comment": "comment",
469 "operation": "replace",
470 "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
480 checkPatchContext(returnValue);
481 assertEquals(Builders.containerBuilder()
482 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
483 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
484 .build(), returnValue.getData().get(0).getNode());