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 XmlPatchBodyTest extends AbstractPatchBodyTest {
23 public XmlPatchBodyTest() {
24 super(XmlPatchBody::new);
28 public final void moduleDataTest() throws Exception {
29 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
30 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
31 <patch-id>test-patch</patch-id>
32 <comment>this is test patch</comment>
34 <edit-id>edit1</edit-id>
35 <operation>create</operation>
36 <target>/my-list2=my-leaf20</target>
38 <my-list2 xmlns="instance:identifier:patch:module">
39 <name>my-leaf20</name>
40 <my-leaf21>I am leaf21-0</my-leaf21>
41 <my-leaf22>I am leaf22-0</my-leaf22>
46 <edit-id>edit2</edit-id>
47 <operation>create</operation>
48 <target>/my-list2=my-leaf21</target>
50 <my-list2 xmlns="instance:identifier:patch:module">
51 <name>my-leaf21</name>
52 <my-leaf21>I am leaf21-1</my-leaf21>
53 <my-leaf22>I am leaf22-1</my-leaf22>
61 * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned.
64 public final void moduleDataValueMissingNegativeTest() throws Exception {
65 final var ex = assertThrows(RestconfDocumentedException.class,
66 () -> parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
67 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
68 <patch-id>test-patch</patch-id>
69 <comment>Test patch with missing value node for create operation</comment>
71 <edit-id>edit1</edit-id>
72 <operation>create</operation>
73 <target>/my-list2</target>
76 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
80 * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be
84 public final void moduleDataNotValueNotSupportedNegativeTest() throws Exception {
85 final var ex = assertThrows(RestconfDocumentedException.class,
86 () -> parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
87 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
88 <patch-id>test-patch</patch-id>
89 <comment>Test patch with not allowed value node for delete operation</comment>
91 <edit-id>edit1</edit-id>
92 <operation>delete</operation>
93 <target>/my-list2/my-leaf21</target>
95 <my-list2 xmlns="instance:identifier:patch:module">
96 <name>my-leaf20</name>
97 <my-leaf21>I am leaf21-0</my-leaf21>
98 <my-leaf22>I am leaf22-0</my-leaf22>
103 assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
107 * Test of YANG Patch with absolute target path.
110 public final void moduleDataAbsoluteTargetPathTest() throws Exception {
111 checkPatchContext(parse(mountPrefix(), """
112 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
113 <patch-id>test-patch</patch-id>
114 <comment>Test patch with absolute target path</comment>
116 <edit-id>edit1</edit-id>
117 <operation>create</operation>
118 <target>/instance-identifier-patch-module:patch-cont/my-list1=leaf1/my-list2=my-leaf20</target>
120 <my-list2 xmlns="instance:identifier:patch:module">
121 <name>my-leaf20</name>
122 <my-leaf21>I am leaf21-0</my-leaf21>
123 <my-leaf22>I am leaf22-0</my-leaf22>
128 <edit-id>edit2</edit-id>
129 <operation>create</operation>
130 <target>/instance-identifier-patch-module:patch-cont/my-list1=leaf1/my-list2=my-leaf21</target>
132 <my-list2 xmlns="instance:identifier:patch:module">
133 <name>my-leaf21</name>
134 <my-leaf21>I am leaf21-1</my-leaf21>
135 <my-leaf22>I am leaf22-1</my-leaf22>
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 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
149 <patch-id>test-patch</patch-id>
150 <comment>Test to create and replace data in container directly using / sign as a target</comment>
152 <edit-id>edit1</edit-id>
153 <operation>create</operation>
156 <patch-cont xmlns="instance:identifier:patch:module">
158 <name>my-list1 - A</name>
159 <my-leaf11>I am leaf11-0</my-leaf11>
160 <my-leaf12>I am leaf12-1</my-leaf12>
163 <name>my-list1 - B</name>
164 <my-leaf11>I am leaf11-0</my-leaf11>
165 <my-leaf12>I am leaf12-1</my-leaf12>
171 <edit-id>edit2</edit-id>
172 <operation>replace</operation>
175 <patch-cont xmlns="instance:identifier:patch:module">
177 <name>my-list1 - Replacing</name>
178 <my-leaf11>I am leaf11-0</my-leaf11>
179 <my-leaf12>I am leaf12-1</my-leaf12>
188 * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
191 public final void moduleDataMergeOperationOnListTest() throws Exception {
192 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
193 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
194 <patch-id>Test merge operation</patch-id>
195 <comment>This is test patch for merge operation on list</comment>
197 <edit-id>edit1</edit-id>
198 <operation>replace</operation>
199 <target>/my-list2=my-leaf20</target>
201 <my-list2 xmlns="instance:identifier:patch:module">
202 <name>my-leaf20</name>
203 <my-leaf21>I am leaf21-0</my-leaf21>
204 <my-leaf22>I am leaf22-0</my-leaf22>
209 <edit-id>edit2</edit-id>
210 <operation>merge</operation>
211 <target>/my-list2=my-leaf21</target>
213 <my-list2 xmlns="instance:identifier:patch:module">
214 <name>my-leaf21</name>
215 <my-leaf21>I am leaf21-1</my-leaf21>
216 <my-leaf22>I am leaf22-1</my-leaf22>
224 * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
227 public final void moduleDataMergeOperationOnContainerTest() throws Exception {
228 checkPatchContext(parse(mountPrefix() + "instance-identifier-patch-module:patch-cont", """
229 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
230 <patch-id>Test merge operation</patch-id>
231 <comment>This is test patch for merge operation on container</comment>
233 <edit-id>edit1</edit-id>
234 <operation>create</operation>
237 <patch-cont xmlns="instance:identifier:patch:module">
239 <name>my-list1 - A</name>
240 <my-leaf11>I am leaf11-0</my-leaf11>
241 <my-leaf12>I am leaf12-1</my-leaf12>
244 <name>my-list1 - B</name>
245 <my-leaf11>I am leaf11-0</my-leaf11>
246 <my-leaf12>I am leaf12-1</my-leaf12>
252 <edit-id>edit2</edit-id>
253 <operation>merge</operation>
256 <patch-cont xmlns="instance:identifier:patch:module">
258 <name>my-list1 - Merged</name>
259 <my-leaf11>I am leaf11-0</my-leaf11>
260 <my-leaf12>I am leaf12-1</my-leaf12>
269 * Test of Yang Patch on the top-level container with empty URI for data root.
272 public final void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
273 checkPatchContext(parse(mountPrefix(), """
274 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
275 <patch-id>test-patch</patch-id>
276 <comment>Test patch applied to the top-level container with empty URI</comment>
278 <edit-id>edit1</edit-id>
279 <operation>replace</operation>
280 <target>/instance-identifier-patch-module:patch-cont</target>
282 <patch-cont xmlns="instance:identifier:patch:module">
284 <name>my-leaf10</name>
293 * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
296 public final void modulePatchTargetTopLevelContainerWithFullPathURITest() throws Exception {
297 final var returnValue = parse(mountPrefix() + "instance-identifier-patch-module:patch-cont", """
298 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
299 <patch-id>test-patch</patch-id>
300 <comment>Test patch applied to the top-level container with '/' in target</comment>
302 <edit-id>edit1</edit-id>
303 <operation>replace</operation>
306 <patch-cont xmlns="instance:identifier:patch:module">
308 <name>my-leaf-set</name>
309 <my-leaf11>leaf-a</my-leaf11>
310 <my-leaf12>leaf-b</my-leaf12>
316 checkPatchContext(returnValue);
317 assertEquals(Builders.containerBuilder()
318 .withNodeIdentifier(new NodeIdentifier(PATCH_CONT_QNAME))
319 .withChild(Builders.mapBuilder()
320 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
321 .withChild(Builders.mapEntryBuilder()
322 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
323 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
324 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
325 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
328 .build(), returnValue.getData().get(0).getNode());
332 * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
335 public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
336 final var returnValue = parse(
337 mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
339 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
340 <patch-id>test-patch</patch-id>
341 <comment>Test patch applied to the second-level list with '/' in target</comment>
343 <edit-id>edit1</edit-id>
344 <operation>replace</operation>
347 <my-list1 xmlns="instance:identifier:patch:module">
348 <name>my-leaf-set</name>
349 <my-leaf11>leaf-a</my-leaf11>
350 <my-leaf12>leaf-b</my-leaf12>
355 checkPatchContext(returnValue);
356 assertEquals(Builders.mapBuilder()
357 .withNodeIdentifier(new NodeIdentifier(MY_LIST1_QNAME))
358 .withChild(Builders.mapEntryBuilder()
359 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST1_QNAME, LEAF_NAME_QNAME, "my-leaf-set"))
360 .withChild(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf-set"))
361 .withChild(ImmutableNodes.leafNode(MY_LEAF11_QNAME, "leaf-a"))
362 .withChild(ImmutableNodes.leafNode(MY_LEAF12_QNAME, "leaf-b"))
364 .build(), returnValue.getData().get(0).getNode());
368 * Test of Yang Patch on the top augmented element.
371 public final void moduleTargetTopLevelAugmentedContainerTest() throws Exception {
372 final var returnValue = parse(mountPrefix(), """
373 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
374 <patch-id>test-patch</patch-id>
375 <comment>This test patch for augmented element</comment>
377 <edit-id>edit1</edit-id>
378 <operation>replace</operation>
379 <target>/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug</target>
381 <container-aug xmlns="test-ns-aug">
382 <leaf-aug>data</leaf-aug>
387 checkPatchContext(returnValue);
388 assertEquals(Builders.containerBuilder()
389 .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
390 .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
391 .build(), returnValue.getData().get(0).getNode());
395 * Test of YANG Patch on the top system map node element.
398 public final void moduleTargetMapNodeTest() throws Exception {
399 final var returnValue = parse(mountPrefix(), """
400 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
401 <patch-id>map-patch</patch-id>
402 <comment>YANG patch comment</comment>
404 <edit-id>edit1</edit-id>
405 <operation>replace</operation>
406 <target>/map-model:cont-root/map-model:cont1/map-model:my-map=key</target>
408 <my-map xmlns="map:ns">
409 <key-leaf>key</key-leaf>
410 <data-leaf>data</data-leaf>
415 checkPatchContext(returnValue);
416 assertEquals(Builders.mapBuilder()
417 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
418 .withChild(Builders.mapEntryBuilder()
419 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
420 .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
421 .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
423 .build(), returnValue.getData().get(0).getNode());
427 * Test of YANG Patch on the leaf set node element.
430 public final void modulePatchTargetLeafSetNodeTest() throws Exception {
431 final var returnValue = parse(mountPrefix(), """
432 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
433 <patch-id>set-patch</patch-id>
434 <comment>YANG patch comment</comment>
436 <edit-id>edit1</edit-id>
437 <operation>replace</operation>
438 <target>/set-model:cont-root/set-model:cont1/set-model:my-set="data1"</target>
440 <my-set xmlns="set:ns">data1</my-set>
444 checkPatchContext(returnValue);
445 assertEquals(Builders.leafSetBuilder()
446 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
447 .withChild(Builders.leafSetEntryBuilder()
448 .withNodeIdentifier(new NodeWithValue<>(LEAF_SET_QNAME, "data1"))
451 .build(), returnValue.getData().get(0).getNode());
455 * Test of Yang Patch on the top unkeyed list element.
458 public final void moduleTargetUnkeyedListNodeTest() throws Exception {
459 final var returnValue = parse(mountPrefix(), """
460 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
461 <patch-id>list-patch</patch-id>
462 <comment>YANG patch comment</comment>
464 <edit-id>edit1</edit-id>
465 <operation>replace</operation>
466 <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
468 <unkeyed-list xmlns="list:ns">
475 checkPatchContext(returnValue);
476 assertEquals(Builders.unkeyedListBuilder()
477 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
478 .withChild(Builders.unkeyedListEntryBuilder()
479 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
480 .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
481 .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
483 .build(), returnValue.getData().get(0).getNode());
487 * Test of Yang Patch on the top case node element.
490 public final void moduleTargetCaseNodeTest() throws Exception {
491 final var returnValue = parse(mountPrefix(), """
492 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
493 <patch-id>choice-patch</patch-id>
494 <comment>YANG patch comment</comment>
496 <edit-id>edit1</edit-id>
497 <operation>replace</operation>
498 <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
500 <case-cont1 xmlns="choice:ns">
501 <case-leaf1>data</case-leaf1>
506 checkPatchContext(returnValue);
507 assertEquals(Builders.containerBuilder()
508 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
509 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
510 .build(), returnValue.getData().get(0).getNode());
514 * Test reading simple leaf value.
517 public final void modulePatchSimpleLeafValueTest() throws Exception {
518 final var returnValue = parse(mountPrefix() + "instance-identifier-patch-module:patch-cont/my-list1=leaf1", """
519 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
520 <patch-id>test-patch</patch-id>
521 <comment>this is test patch</comment>
523 <edit-id>edit1</edit-id>
524 <operation>replace</operation>
525 <target>/my-list2=my-leaf20/name</target>
527 <name xmlns="instance:identifier:patch:module">my-leaf20</name>
531 checkPatchContext(returnValue);
532 assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.getData().get(0).getNode());