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 XmlPatchBodyTest extends AbstractPatchBodyTest {
21 public XmlPatchBodyTest() {
22 super(XmlPatchBody::new);
26 public final void moduleDataTest() throws Exception {
27 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
28 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
29 <patch-id>test-patch</patch-id>
30 <comment>this is test patch</comment>
32 <edit-id>edit1</edit-id>
33 <operation>create</operation>
34 <target>/my-list2=my-leaf20</target>
36 <my-list2 xmlns="instance:identifier:patch:module">
37 <name>my-leaf20</name>
38 <my-leaf21>I am leaf21-0</my-leaf21>
39 <my-leaf22>I am leaf22-0</my-leaf22>
44 <edit-id>edit2</edit-id>
45 <operation>create</operation>
46 <target>/my-list2=my-leaf21</target>
48 <my-list2 xmlns="instance:identifier:patch:module">
49 <name>my-leaf21</name>
50 <my-leaf21>I am leaf21-1</my-leaf21>
51 <my-leaf22>I am leaf22-1</my-leaf22>
59 * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned.
62 public final void moduleDataValueMissingNegativeTest() throws Exception {
63 final var ex = assertThrows(RestconfDocumentedException.class,
64 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
65 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
66 <patch-id>test-patch</patch-id>
67 <comment>Test patch with missing value node for create operation</comment>
69 <edit-id>edit1</edit-id>
70 <operation>create</operation>
71 <target>/my-list2</target>
74 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
78 * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be
82 public final void moduleDataNotValueNotSupportedNegativeTest() throws Exception {
83 final var ex = assertThrows(RestconfDocumentedException.class,
84 () -> parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
85 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
86 <patch-id>test-patch</patch-id>
87 <comment>Test patch with not allowed value node for delete operation</comment>
89 <edit-id>edit1</edit-id>
90 <operation>delete</operation>
91 <target>/my-list2/my-leaf21</target>
93 <my-list2 xmlns="instance:identifier:patch:module">
94 <name>my-leaf20</name>
95 <my-leaf21>I am leaf21-0</my-leaf21>
96 <my-leaf22>I am leaf22-0</my-leaf22>
101 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
105 * Test of YANG Patch with absolute target path.
108 public final void moduleDataAbsoluteTargetPathTest() throws Exception {
109 checkPatchContext(parse(mountPrefix(), "", """
110 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
111 <patch-id>test-patch</patch-id>
112 <comment>Test patch with absolute target path</comment>
114 <edit-id>edit1</edit-id>
115 <operation>create</operation>
116 <target>/instance-identifier-patch-module:patch-cont/my-list1=leaf1/my-list2=my-leaf20</target>
118 <my-list2 xmlns="instance:identifier:patch:module">
119 <name>my-leaf20</name>
120 <my-leaf21>I am leaf21-0</my-leaf21>
121 <my-leaf22>I am leaf22-0</my-leaf22>
126 <edit-id>edit2</edit-id>
127 <operation>create</operation>
128 <target>/instance-identifier-patch-module:patch-cont/my-list1=leaf1/my-list2=my-leaf21</target>
130 <my-list2 xmlns="instance:identifier:patch:module">
131 <name>my-leaf21</name>
132 <my-leaf21>I am leaf21-1</my-leaf21>
133 <my-leaf22>I am leaf22-1</my-leaf22>
141 * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
144 public final void modulePatchCompleteTargetInURITest() throws Exception {
145 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
146 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
147 <patch-id>test-patch</patch-id>
148 <comment>Test to create and replace data in container directly using / sign as a target</comment>
150 <edit-id>edit1</edit-id>
151 <operation>create</operation>
154 <patch-cont xmlns="instance:identifier:patch:module">
156 <name>my-list1 - A</name>
157 <my-leaf11>I am leaf11-0</my-leaf11>
158 <my-leaf12>I am leaf12-1</my-leaf12>
161 <name>my-list1 - B</name>
162 <my-leaf11>I am leaf11-0</my-leaf11>
163 <my-leaf12>I am leaf12-1</my-leaf12>
169 <edit-id>edit2</edit-id>
170 <operation>replace</operation>
173 <patch-cont xmlns="instance:identifier:patch:module">
175 <name>my-list1 - Replacing</name>
176 <my-leaf11>I am leaf11-0</my-leaf11>
177 <my-leaf12>I am leaf12-1</my-leaf12>
186 * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
189 public final void moduleDataMergeOperationOnListTest() throws Exception {
190 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
191 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
192 <patch-id>Test merge operation</patch-id>
193 <comment>This is test patch for merge operation on list</comment>
195 <edit-id>edit1</edit-id>
196 <operation>replace</operation>
197 <target>/my-list2=my-leaf20</target>
199 <my-list2 xmlns="instance:identifier:patch:module">
200 <name>my-leaf20</name>
201 <my-leaf21>I am leaf21-0</my-leaf21>
202 <my-leaf22>I am leaf22-0</my-leaf22>
207 <edit-id>edit2</edit-id>
208 <operation>merge</operation>
209 <target>/my-list2=my-leaf21</target>
211 <my-list2 xmlns="instance:identifier:patch:module">
212 <name>my-leaf21</name>
213 <my-leaf21>I am leaf21-1</my-leaf21>
214 <my-leaf22>I am leaf22-1</my-leaf22>
222 * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
225 public final void moduleDataMergeOperationOnContainerTest() throws Exception {
226 checkPatchContext(parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
227 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
228 <patch-id>Test merge operation</patch-id>
229 <comment>This is test patch for merge operation on container</comment>
231 <edit-id>edit1</edit-id>
232 <operation>create</operation>
235 <patch-cont xmlns="instance:identifier:patch:module">
237 <name>my-list1 - A</name>
238 <my-leaf11>I am leaf11-0</my-leaf11>
239 <my-leaf12>I am leaf12-1</my-leaf12>
242 <name>my-list1 - B</name>
243 <my-leaf11>I am leaf11-0</my-leaf11>
244 <my-leaf12>I am leaf12-1</my-leaf12>
250 <edit-id>edit2</edit-id>
251 <operation>merge</operation>
254 <patch-cont xmlns="instance:identifier:patch:module">
256 <name>my-list1 - Merged</name>
257 <my-leaf11>I am leaf11-0</my-leaf11>
258 <my-leaf12>I am leaf12-1</my-leaf12>
267 * Test of Yang Patch on the top-level container with empty URI for data root.
270 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
271 checkPatchContext(parse(mountPrefix(), "", """
272 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
273 <patch-id>test-patch</patch-id>
274 <comment>Test patch applied to the top-level container with empty URI</comment>
276 <edit-id>edit1</edit-id>
277 <operation>replace</operation>
278 <target>/instance-identifier-patch-module:patch-cont</target>
280 <patch-cont xmlns="instance:identifier:patch:module">
282 <name>my-leaf10</name>
291 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
294 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
295 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont", """
296 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
297 <patch-id>test-patch</patch-id>
298 <comment>Test patch applied to the top-level container with '/' in target</comment>
300 <edit-id>edit1</edit-id>
301 <operation>replace</operation>
304 <patch-cont xmlns="instance:identifier:patch:module">
306 <name>my-leaf-set</name>
307 <my-leaf11>leaf-a</my-leaf11>
308 <my-leaf12>leaf-b</my-leaf12>
314 checkPatchContext(returnValue);
315 assertEquals(ImmutableNodes.newContainerBuilder()
316 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
317 .withChild(ImmutableNodes.newSystemMapBuilder()
318 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
319 .withChild(ImmutableNodes.newMapEntryBuilder()
320 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
321 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
322 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
323 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
326 .build(), returnValue.entities().get(0).getNode());
330 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
333 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
334 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
336 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
337 <patch-id>test-patch</patch-id>
338 <comment>Test patch applied to the second-level list with '/' in target</comment>
340 <edit-id>edit1</edit-id>
341 <operation>replace</operation>
344 <my-list1 xmlns="instance:identifier:patch:module">
345 <name>my-leaf-set</name>
346 <my-leaf11>leaf-a</my-leaf11>
347 <my-leaf12>leaf-b</my-leaf12>
352 checkPatchContext(returnValue);
353 assertEquals(ImmutableNodes.newSystemMapBuilder()
354 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
355 .withChild(ImmutableNodes.newMapEntryBuilder()
356 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
357 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
358 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
359 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
361 .build(), returnValue.entities().get(0).getNode());
365 * Test of Yang Patch on the top augmented element.
368 public final void moduleTargetTopLevelAugmentedContainerTest() throws Exception {
369 final var returnValue = parse(mountPrefix(), "", """
370 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
371 <patch-id>test-patch</patch-id>
372 <comment>This test patch for augmented element</comment>
374 <edit-id>edit1</edit-id>
375 <operation>replace</operation>
376 <target>/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug</target>
378 <container-aug xmlns="test-ns-aug">
379 <leaf-aug>data</leaf-aug>
384 checkPatchContext(returnValue);
385 assertEquals(ImmutableNodes.newContainerBuilder()
386 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
387 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
388 .build(), returnValue.entities().get(0).getNode());
392 * Test of YANG Patch on the top system map node element.
395 public final void moduleTargetMapNodeTest() throws Exception {
396 final var returnValue = parse(mountPrefix(), "", """
397 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
398 <patch-id>map-patch</patch-id>
399 <comment>YANG patch comment</comment>
401 <edit-id>edit1</edit-id>
402 <operation>replace</operation>
403 <target>/map-model:cont-root/map-model:cont1/map-model:my-map=key</target>
405 <my-map xmlns="map:ns">
406 <key-leaf>key</key-leaf>
407 <data-leaf>data</data-leaf>
412 checkPatchContext(returnValue);
413 assertEquals(ImmutableNodes.newSystemMapBuilder()
414 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
415 .withChild(ImmutableNodes.newMapEntryBuilder()
416 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
417 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
418 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
420 .build(), returnValue.entities().get(0).getNode());
424 * Test of YANG Patch on the leaf set node element.
427 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
428 final var returnValue = parse(mountPrefix(), "", """
429 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
430 <patch-id>set-patch</patch-id>
431 <comment>YANG patch comment</comment>
433 <edit-id>edit1</edit-id>
434 <operation>replace</operation>
435 <target>/set-model:cont-root/set-model:cont1/set-model:my-set="data1"</target>
437 <my-set xmlns="set:ns">data1</my-set>
441 checkPatchContext(returnValue);
442 assertEquals(ImmutableNodes.newSystemLeafSetBuilder()
443 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
444 .withChildValue("data1")
445 .build(), returnValue.entities().get(0).getNode());
449 * Test of Yang Patch on the top unkeyed list element.
452 public final void moduleTargetUnkeyedListNodeTest() throws Exception {
453 final var returnValue = parse(mountPrefix(), "", """
454 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
455 <patch-id>list-patch</patch-id>
456 <comment>YANG patch comment</comment>
458 <edit-id>edit1</edit-id>
459 <operation>replace</operation>
460 <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
462 <unkeyed-list xmlns="list:ns">
469 checkPatchContext(returnValue);
470 assertEquals(ImmutableNodes.newUnkeyedListBuilder()
471 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
472 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
473 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
474 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
475 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
477 .build(), returnValue.entities().get(0).getNode());
481 * Test of Yang Patch on the top case node element.
484 public final void moduleTargetCaseNodeTest() throws Exception {
485 final var returnValue = parse(mountPrefix(), "", """
486 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
487 <patch-id>choice-patch</patch-id>
488 <comment>YANG patch comment</comment>
490 <edit-id>edit1</edit-id>
491 <operation>replace</operation>
492 <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
494 <case-cont1 xmlns="choice:ns">
495 <case-leaf1>data</case-leaf1>
500 checkPatchContext(returnValue);
501 assertEquals(ImmutableNodes.newContainerBuilder()
502 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
503 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
504 .build(), returnValue.entities().get(0).getNode());
508 * Test reading simple leaf value.
511 public final void modulePatchSimpleLeafValueTest() throws Exception {
512 final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
513 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
514 <patch-id>test-patch</patch-id>
515 <comment>this is test patch</comment>
517 <edit-id>edit1</edit-id>
518 <operation>replace</operation>
519 <target>/my-list2=my-leaf20/name</target>
521 <name xmlns="instance:identifier:patch:module">my-leaf20</name>
525 checkPatchContext(returnValue);
526 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.entities().get(0).getNode());