Further warnings mitigation
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / databind / XmlPatchBodyTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.nb.rfc8040.databind;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12
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;
19
20 public class XmlPatchBodyTest extends AbstractPatchBodyTest {
21     public XmlPatchBodyTest() {
22         super(XmlPatchBody::new);
23     }
24
25     @Test
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>
31                 <edit>
32                     <edit-id>edit1</edit-id>
33                     <operation>create</operation>
34                     <target>/my-list2=my-leaf20</target>
35                     <value>
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>
40                         </my-list2>
41                     </value>
42                 </edit>
43                 <edit>
44                     <edit-id>edit2</edit-id>
45                     <operation>create</operation>
46                     <target>/my-list2=my-leaf21</target>
47                     <value>
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>
52                         </my-list2>
53                     </value>
54                 </edit>
55             </yang-patch>"""));
56     }
57
58     /**
59      * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned.
60      */
61     @Test
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>
68                     <edit>
69                         <edit-id>edit1</edit-id>
70                         <operation>create</operation>
71                         <target>/my-list2</target>
72                     </edit>
73                 </yang-patch>"""));
74         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
75     }
76
77     /**
78      * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be
79      * returned.
80      */
81     @Test
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>
88                     <edit>
89                         <edit-id>edit1</edit-id>
90                         <operation>delete</operation>
91                         <target>/my-list2/my-leaf21</target>
92                         <value>
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>
97                             </my-list2>
98                         </value>
99                     </edit>
100                 </yang-patch>"""));
101         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
102     }
103
104     /**
105      * Test of YANG Patch with absolute target path.
106      */
107     @Test
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>
113                 <edit>
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>
117                     <value>
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>
122                         </my-list2>
123                     </value>
124                 </edit>
125                 <edit>
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>
129                     <value>
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>
134                         </my-list2>
135                     </value>
136                 </edit>
137             </yang-patch>"""));
138     }
139
140     /**
141      * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
142      */
143     @Test
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>
149                 <edit>
150                     <edit-id>edit1</edit-id>
151                     <operation>create</operation>
152                     <target>/</target>
153                     <value>
154                         <patch-cont xmlns="instance:identifier:patch:module">
155                             <my-list1>
156                                 <name>my-list1 - A</name>
157                                 <my-leaf11>I am leaf11-0</my-leaf11>
158                                 <my-leaf12>I am leaf12-1</my-leaf12>
159                             </my-list1>
160                             <my-list1>
161                                 <name>my-list1 - B</name>
162                                 <my-leaf11>I am leaf11-0</my-leaf11>
163                                 <my-leaf12>I am leaf12-1</my-leaf12>
164                             </my-list1>
165                         </patch-cont>
166                     </value>
167                 </edit>
168                 <edit>
169                     <edit-id>edit2</edit-id>
170                     <operation>replace</operation>
171                     <target>/</target>
172                     <value>
173                         <patch-cont xmlns="instance:identifier:patch:module">
174                             <my-list1>
175                                 <name>my-list1 - Replacing</name>
176                                 <my-leaf11>I am leaf11-0</my-leaf11>
177                                 <my-leaf12>I am leaf12-1</my-leaf12>
178                             </my-list1>
179                         </patch-cont>
180                     </value>
181                 </edit>
182             </yang-patch>"""));
183     }
184
185     /**
186      * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
187      */
188     @Test
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>
194                 <edit>
195                     <edit-id>edit1</edit-id>
196                     <operation>replace</operation>
197                     <target>/my-list2=my-leaf20</target>
198                     <value>
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>
203                         </my-list2>
204                     </value>
205                 </edit>
206                 <edit>
207                     <edit-id>edit2</edit-id>
208                     <operation>merge</operation>
209                     <target>/my-list2=my-leaf21</target>
210                     <value>
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>
215                         </my-list2>
216                     </value>
217                 </edit>
218             </yang-patch>"""));
219     }
220
221     /**
222      * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
223      */
224     @Test
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>
230                 <edit>
231                     <edit-id>edit1</edit-id>
232                     <operation>create</operation>
233                     <target>/</target>
234                     <value>
235                         <patch-cont xmlns="instance:identifier:patch:module">
236                             <my-list1>
237                                 <name>my-list1 - A</name>
238                                 <my-leaf11>I am leaf11-0</my-leaf11>
239                                 <my-leaf12>I am leaf12-1</my-leaf12>
240                             </my-list1>
241                             <my-list1>
242                                 <name>my-list1 - B</name>
243                                 <my-leaf11>I am leaf11-0</my-leaf11>
244                                 <my-leaf12>I am leaf12-1</my-leaf12>
245                             </my-list1>
246                         </patch-cont>
247                     </value>
248                 </edit>
249                 <edit>
250                     <edit-id>edit2</edit-id>
251                     <operation>merge</operation>
252                     <target>/</target>
253                     <value>
254                         <patch-cont xmlns="instance:identifier:patch:module">
255                             <my-list1>
256                                 <name>my-list1 - Merged</name>
257                                 <my-leaf11>I am leaf11-0</my-leaf11>
258                                 <my-leaf12>I am leaf12-1</my-leaf12>
259                             </my-list1>
260                         </patch-cont>
261                     </value>
262                 </edit>
263             </yang-patch>"""));
264     }
265
266     /**
267      * Test of Yang Patch on the top-level container with empty URI for data root.
268      */
269     @Test
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>
275                 <edit>
276                     <edit-id>edit1</edit-id>
277                     <operation>replace</operation>
278                     <target>/instance-identifier-patch-module:patch-cont</target>
279                     <value>
280                         <patch-cont xmlns="instance:identifier:patch:module">
281                             <my-list1>
282                                 <name>my-leaf10</name>
283                             </my-list1>
284                         </patch-cont>
285                     </value>
286                 </edit>
287             </yang-patch>"""));
288     }
289
290     /**
291      * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
292      */
293     @Test
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>
299                 <edit>
300                     <edit-id>edit1</edit-id>
301                     <operation>replace</operation>
302                     <target>/</target>
303                     <value>
304                         <patch-cont xmlns="instance:identifier:patch:module">
305                             <my-list1>
306                                 <name>my-leaf-set</name>
307                                 <my-leaf11>leaf-a</my-leaf11>
308                                 <my-leaf12>leaf-b</my-leaf12>
309                             </my-list1>
310                         </patch-cont>
311                     </value>
312                 </edit>
313             </yang-patch>""");
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"))
324                     .build())
325                 .build())
326             .build(), returnValue.entities().get(0).getNode());
327     }
328
329     /**
330      * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
331      */
332     @Test
333     public final void modulePatchTargetSecondLevelListWithFullPathURITest() throws Exception {
334         final var returnValue = parse(mountPrefix(), "instance-identifier-patch-module:patch-cont/my-list1=my-leaf-set",
335             """
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>
339                     <edit>
340                         <edit-id>edit1</edit-id>
341                         <operation>replace</operation>
342                         <target>/</target>
343                         <value>
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>
348                             </my-list1>
349                         </value>
350                     </edit>
351                 </yang-patch>""");
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"))
360                 .build())
361             .build(), returnValue.entities().get(0).getNode());
362     }
363
364     /**
365      * Test of Yang Patch on the top augmented element.
366      */
367     @Test
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>
373                 <edit>
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>
377                     <value>
378                         <container-aug xmlns="test-ns-aug">
379                             <leaf-aug>data</leaf-aug>
380                         </container-aug>
381                     </value>
382                 </edit>
383             </yang-patch>""");
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());
389     }
390
391     /**
392      * Test of YANG Patch on the top system map node element.
393      */
394     @Test
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>
400                 <edit>
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>
404                     <value>
405                         <my-map xmlns="map:ns">
406                             <key-leaf>key</key-leaf>
407                             <data-leaf>data</data-leaf>
408                         </my-map>
409                     </value>
410                 </edit>
411             </yang-patch>""");
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"))
419                 .build())
420             .build(), returnValue.entities().get(0).getNode());
421     }
422
423     /**
424      * Test of YANG Patch on the leaf set node element.
425      */
426     @Test
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>
432                 <edit>
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>
436                     <value>
437                         <my-set xmlns="set:ns">data1</my-set>
438                     </value>
439                 </edit>
440             </yang-patch>""");
441         checkPatchContext(returnValue);
442         assertEquals(ImmutableNodes.newSystemLeafSetBuilder()
443             .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
444             .withChildValue("data1")
445             .build(), returnValue.entities().get(0).getNode());
446     }
447
448     /**
449      * Test of Yang Patch on the top unkeyed list element.
450      */
451     @Test
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>
457                 <edit>
458                     <edit-id>edit1</edit-id>
459                     <operation>replace</operation>
460                     <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
461                     <value>
462                         <unkeyed-list xmlns="list:ns">
463                             <leaf1>data1</leaf1>
464                             <leaf2>data2</leaf2>
465                         </unkeyed-list>
466                     </value>
467                 </edit>
468             </yang-patch>""");
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"))
476                 .build())
477             .build(), returnValue.entities().get(0).getNode());
478     }
479
480     /**
481      * Test of Yang Patch on the top case node element.
482      */
483     @Test
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>
489                 <edit>
490                     <edit-id>edit1</edit-id>
491                     <operation>replace</operation>
492                     <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
493                     <value>
494                         <case-cont1 xmlns="choice:ns">
495                             <case-leaf1>data</case-leaf1>
496                         </case-cont1>
497                     </value>
498                 </edit>
499             </yang-patch>""");
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());
505     }
506
507     /**
508      * Test reading simple leaf value.
509      */
510     @Test
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>
516                 <edit>
517                     <edit-id>edit1</edit-id>
518                     <operation>replace</operation>
519                     <target>/my-list2=my-leaf20/name</target>
520                     <value>
521                         <name xmlns="instance:identifier:patch:module">my-leaf20</name>
522                     </value>
523                 </edit>
524             </yang-patch>""");
525         checkPatchContext(returnValue);
526         assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.entities().get(0).getNode());
527     }
528 }