Remove AbstractNormalizedNodeBodyReader
[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.api.YangInstanceIdentifier.NodeWithValue;
19 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
20 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
21
22 public class XmlPatchBodyTest extends AbstractPatchBodyTest {
23     public XmlPatchBodyTest() {
24         super(XmlPatchBody::new);
25     }
26
27     @Test
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>
33                 <edit>
34                     <edit-id>edit1</edit-id>
35                     <operation>create</operation>
36                     <target>/my-list2=my-leaf20</target>
37                     <value>
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>
42                         </my-list2>
43                     </value>
44                 </edit>
45                 <edit>
46                     <edit-id>edit2</edit-id>
47                     <operation>create</operation>
48                     <target>/my-list2=my-leaf21</target>
49                     <value>
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>
54                         </my-list2>
55                     </value>
56                 </edit>
57             </yang-patch>"""));
58     }
59
60     /**
61      * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned.
62      */
63     @Test
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>
70                     <edit>
71                         <edit-id>edit1</edit-id>
72                         <operation>create</operation>
73                         <target>/my-list2</target>
74                     </edit>
75                 </yang-patch>"""));
76         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
77     }
78
79     /**
80      * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be
81      * returned.
82      */
83     @Test
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>
90                     <edit>
91                         <edit-id>edit1</edit-id>
92                         <operation>delete</operation>
93                         <target>/my-list2/my-leaf21</target>
94                         <value>
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>
99                             </my-list2>
100                         </value>
101                     </edit>
102                 </yang-patch>"""));
103         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
104     }
105
106     /**
107      * Test of YANG Patch with absolute target path.
108      */
109     @Test
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>
115                 <edit>
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>
119                     <value>
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>
124                         </my-list2>
125                     </value>
126                 </edit>
127                 <edit>
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>
131                     <value>
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>
136                         </my-list2>
137                     </value>
138                 </edit>
139             </yang-patch>"""));
140     }
141
142     /**
143      * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
144      */
145     @Test
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>
151                 <edit>
152                     <edit-id>edit1</edit-id>
153                     <operation>create</operation>
154                     <target>/</target>
155                     <value>
156                         <patch-cont xmlns="instance:identifier:patch:module">
157                             <my-list1>
158                                 <name>my-list1 - A</name>
159                                 <my-leaf11>I am leaf11-0</my-leaf11>
160                                 <my-leaf12>I am leaf12-1</my-leaf12>
161                             </my-list1>
162                             <my-list1>
163                                 <name>my-list1 - B</name>
164                                 <my-leaf11>I am leaf11-0</my-leaf11>
165                                 <my-leaf12>I am leaf12-1</my-leaf12>
166                             </my-list1>
167                         </patch-cont>
168                     </value>
169                 </edit>
170                 <edit>
171                     <edit-id>edit2</edit-id>
172                     <operation>replace</operation>
173                     <target>/</target>
174                     <value>
175                         <patch-cont xmlns="instance:identifier:patch:module">
176                             <my-list1>
177                                 <name>my-list1 - Replacing</name>
178                                 <my-leaf11>I am leaf11-0</my-leaf11>
179                                 <my-leaf12>I am leaf12-1</my-leaf12>
180                             </my-list1>
181                         </patch-cont>
182                     </value>
183                 </edit>
184             </yang-patch>"""));
185     }
186
187     /**
188      * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
189      */
190     @Test
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>
196                 <edit>
197                     <edit-id>edit1</edit-id>
198                     <operation>replace</operation>
199                     <target>/my-list2=my-leaf20</target>
200                     <value>
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>
205                         </my-list2>
206                     </value>
207                 </edit>
208                 <edit>
209                     <edit-id>edit2</edit-id>
210                     <operation>merge</operation>
211                     <target>/my-list2=my-leaf21</target>
212                     <value>
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>
217                         </my-list2>
218                     </value>
219                 </edit>
220             </yang-patch>"""));
221     }
222
223     /**
224      * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
225      */
226     @Test
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>
232                 <edit>
233                     <edit-id>edit1</edit-id>
234                     <operation>create</operation>
235                     <target>/</target>
236                     <value>
237                         <patch-cont xmlns="instance:identifier:patch:module">
238                             <my-list1>
239                                 <name>my-list1 - A</name>
240                                 <my-leaf11>I am leaf11-0</my-leaf11>
241                                 <my-leaf12>I am leaf12-1</my-leaf12>
242                             </my-list1>
243                             <my-list1>
244                                 <name>my-list1 - B</name>
245                                 <my-leaf11>I am leaf11-0</my-leaf11>
246                                 <my-leaf12>I am leaf12-1</my-leaf12>
247                             </my-list1>
248                         </patch-cont>
249                     </value>
250                 </edit>
251                 <edit>
252                     <edit-id>edit2</edit-id>
253                     <operation>merge</operation>
254                     <target>/</target>
255                     <value>
256                         <patch-cont xmlns="instance:identifier:patch:module">
257                             <my-list1>
258                                 <name>my-list1 - Merged</name>
259                                 <my-leaf11>I am leaf11-0</my-leaf11>
260                                 <my-leaf12>I am leaf12-1</my-leaf12>
261                             </my-list1>
262                         </patch-cont>
263                     </value>
264                 </edit>
265             </yang-patch>"""));
266     }
267
268     /**
269      * Test of Yang Patch on the top-level container with empty URI for data root.
270      */
271     @Test
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>
277                 <edit>
278                     <edit-id>edit1</edit-id>
279                     <operation>replace</operation>
280                     <target>/instance-identifier-patch-module:patch-cont</target>
281                     <value>
282                         <patch-cont xmlns="instance:identifier:patch:module">
283                             <my-list1>
284                                 <name>my-leaf10</name>
285                             </my-list1>
286                         </patch-cont>
287                     </value>
288                 </edit>
289             </yang-patch>"""));
290     }
291
292     /**
293      * Test of YANG Patch on the top-level container with the full path in the URI and "/" in 'target'.
294      */
295     @Test
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>
301                 <edit>
302                     <edit-id>edit1</edit-id>
303                     <operation>replace</operation>
304                     <target>/</target>
305                     <value>
306                         <patch-cont xmlns="instance:identifier:patch:module">
307                             <my-list1>
308                                 <name>my-leaf-set</name>
309                                 <my-leaf11>leaf-a</my-leaf11>
310                                 <my-leaf12>leaf-b</my-leaf12>
311                             </my-list1>
312                         </patch-cont>
313                     </value>
314                 </edit>
315             </yang-patch>""");
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"))
326                     .build())
327                 .build())
328             .build(), returnValue.getData().get(0).getNode());
329     }
330
331     /**
332      * Test of YANG Patch on the second-level list with the full path in the URI and "/" in 'target'.
333      */
334     @Test
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",
338             """
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>
342                     <edit>
343                         <edit-id>edit1</edit-id>
344                         <operation>replace</operation>
345                         <target>/</target>
346                         <value>
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>
351                             </my-list1>
352                         </value>
353                     </edit>
354                 </yang-patch>""");
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"))
363                 .build())
364             .build(), returnValue.getData().get(0).getNode());
365     }
366
367     /**
368      * Test of Yang Patch on the top augmented element.
369      */
370     @Test
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>
376                 <edit>
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>
380                     <value>
381                         <container-aug xmlns="test-ns-aug">
382                             <leaf-aug>data</leaf-aug>
383                         </container-aug>
384                     </value>
385                 </edit>
386             </yang-patch>""");
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());
392     }
393
394     /**
395      * Test of YANG Patch on the top system map node element.
396      */
397     @Test
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>
403                 <edit>
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>
407                     <value>
408                         <my-map xmlns="map:ns">
409                             <key-leaf>key</key-leaf>
410                             <data-leaf>data</data-leaf>
411                         </my-map>
412                     </value>
413                 </edit>
414             </yang-patch>""");
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"))
422                 .build())
423             .build(), returnValue.getData().get(0).getNode());
424     }
425
426     /**
427      * Test of YANG Patch on the leaf set node element.
428      */
429     @Test
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>
435                 <edit>
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>
439                     <value>
440                         <my-set xmlns="set:ns">data1</my-set>
441                     </value>
442                 </edit>
443             </yang-patch>""");
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"))
449                 .withValue("data1")
450                 .build())
451             .build(), returnValue.getData().get(0).getNode());
452     }
453
454     /**
455      * Test of Yang Patch on the top unkeyed list element.
456      */
457     @Test
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>
463                 <edit>
464                     <edit-id>edit1</edit-id>
465                     <operation>replace</operation>
466                     <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
467                     <value>
468                         <unkeyed-list xmlns="list:ns">
469                             <leaf1>data1</leaf1>
470                             <leaf2>data2</leaf2>
471                         </unkeyed-list>
472                     </value>
473                 </edit>
474             </yang-patch>""");
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"))
482                 .build())
483             .build(), returnValue.getData().get(0).getNode());
484     }
485
486     /**
487      * Test of Yang Patch on the top case node element.
488      */
489     @Test
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>
495                 <edit>
496                     <edit-id>edit1</edit-id>
497                     <operation>replace</operation>
498                     <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
499                     <value>
500                         <case-cont1 xmlns="choice:ns">
501                             <case-leaf1>data</case-leaf1>
502                         </case-cont1>
503                     </value>
504                 </edit>
505             </yang-patch>""");
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());
511     }
512
513     /**
514      * Test reading simple leaf value.
515      */
516     @Test
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>
522                 <edit>
523                     <edit-id>edit1</edit-id>
524                     <operation>replace</operation>
525                     <target>/my-list2=my-leaf20/name</target>
526                     <value>
527                         <name xmlns="instance:identifier:patch:module">my-leaf20</name>
528                     </value>
529                 </edit>
530             </yang-patch>""");
531         checkPatchContext(returnValue);
532         assertEquals(ImmutableNodes.leafNode(LEAF_NAME_QNAME, "my-leaf20"), returnValue.getData().get(0).getNode());
533     }
534 }