Test Json/Xml PatchBodyReaders on mixin nodes
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / jersey / providers / patch / XmlPatchBodyReaderTest.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.jersey.providers.patch;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12
13 import java.io.ByteArrayInputStream;
14 import java.io.InputStream;
15 import java.nio.charset.StandardCharsets;
16 import javax.ws.rs.core.MediaType;
17 import org.junit.BeforeClass;
18 import org.junit.Ignore;
19 import org.junit.Test;
20 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
21 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
22 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.XmlBodyReaderTest;
23 import org.opendaylight.yangtools.yang.common.ErrorTag;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
27 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
28 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
30
31 public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
32     private static EffectiveModelContext schemaContext;
33
34     private final XmlPatchBodyReader xmlToPatchBodyReader;
35
36     public XmlPatchBodyReaderTest() throws Exception {
37         super(schemaContext);
38         xmlToPatchBodyReader = new XmlPatchBodyReader(databindProvider, mountPointService);
39     }
40
41     @Override
42     protected MediaType getMediaType() {
43         return new MediaType(MediaType.APPLICATION_XML, null);
44     }
45
46     @BeforeClass
47     public static void initialization() {
48         schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
49     }
50
51     @Test
52     public void moduleDataTest() throws Exception {
53         mockBodyReader("instance-identifier-patch-module:patch-cont/my-list1=leaf1", xmlToPatchBodyReader, false);
54         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null,
55             XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlPATCHdata.xml")));
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 void moduleDataValueMissingNegativeTest() throws Exception {
63         mockBodyReader("instance-identifier-patch-module:patch-cont/my-list1=leaf1", xmlToPatchBodyReader, false);
64         final InputStream inputStream = XmlBodyReaderTest.class.getResourceAsStream(
65             "/instanceidentifier/xml/xmlPATCHdataValueMissing.xml");
66         final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
67             () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
68         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
69     }
70
71     /**
72      * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be
73      * returned.
74      */
75     @Test
76     public void moduleDataNotValueNotSupportedNegativeTest() throws Exception {
77         mockBodyReader("instance-identifier-patch-module:patch-cont/my-list1=leaf1", xmlToPatchBodyReader, false);
78         final InputStream inputStream = XmlBodyReaderTest.class.getResourceAsStream(
79             "/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml");
80         final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
81             () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
82         assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag());
83     }
84
85     /**
86      * Test of Yang Patch with absolute target path.
87      */
88     @Test
89     public void moduleDataAbsoluteTargetPathTest() throws Exception {
90         mockBodyReader("", xmlToPatchBodyReader, false);
91         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null,
92             XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml")));
93     }
94
95     /**
96      * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign.
97      */
98     @Test
99     public void modulePatchCompleteTargetInURITest() throws Exception {
100         mockBodyReader("instance-identifier-patch-module:patch-cont", xmlToPatchBodyReader, false);
101         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null,
102             XmlBodyReaderTest.class.getResourceAsStream(
103                 "/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml")));
104     }
105
106     /**
107      * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge.
108      */
109     @Test
110     public void moduleDataMergeOperationOnListTest() throws Exception {
111         mockBodyReader("instance-identifier-patch-module:patch-cont/my-list1=leaf1", xmlToPatchBodyReader, false);
112         final InputStream inputStream = XmlBodyReaderTest.class.getResourceAsStream(
113             "/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml");
114         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
115     }
116
117     /**
118      * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge.
119      */
120     @Test
121     public void moduleDataMergeOperationOnContainerTest() throws Exception {
122         mockBodyReader("instance-identifier-patch-module:patch-cont", xmlToPatchBodyReader, false);
123         final InputStream inputStream = XmlBodyReaderTest.class
124                 .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml");
125         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
126     }
127
128     /**
129      * Test of Yang Patch on the top-level container with empty URI for data root.
130      */
131     @Test
132     public void modulePatchTargetTopLevelContainerWithEmptyURITest() throws Exception {
133         mockBodyReader("", xmlToPatchBodyReader, false);
134         final InputStream inputStream = XmlBodyReaderTest.class
135                 .getResourceAsStream("/instanceidentifier/xml/xmlPATCHTargetTopLevelContainerWithEmptyURI.xml");
136         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
137     }
138
139     /**
140      * Test of Yang Patch on the top system map node element.
141      */
142     @Test
143     public void moduleTargetMapNodeTest() throws Exception {
144         mockBodyReader("", xmlToPatchBodyReader, false);
145         final var inputStream = new ByteArrayInputStream("""
146             <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
147                 <patch-id>map-patch</patch-id>
148                 <comment>YANG patch comment</comment>
149                 <edit>
150                     <edit-id>edit1</edit-id>
151                     <operation>replace</operation>
152                     <target>/map-model:cont-root/map-model:cont1/map-model:my-map=key</target>
153                     <value>
154                         <my-map xmlns="map:ns">
155                             <key-leaf>key</key-leaf>
156                             <data-leaf>data</data-leaf>
157                         </my-map>
158                     </value>
159                 </edit>
160             </yang-patch>
161             """.getBytes(StandardCharsets.UTF_8));
162         final var expectedData = Builders.mapBuilder()
163                 .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
164                 .withChild(Builders.mapEntryBuilder()
165                         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
166                         .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
167                         .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
168                         .build())
169                 .build();
170         final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
171         checkPatchContext(returnValue);
172         final var data = returnValue.getData().get(0).getNode();
173         assertEquals(MAP_CONT_QNAME, data.getIdentifier().getNodeType());
174         assertEquals(expectedData, data);
175     }
176
177     /**
178      * Test of Yang Patch on the leaf set node element.
179      * TODO: Remove ignore when NETCONF-937 will be resolved
180      */
181     @Ignore
182     @Test
183     public void modulePatchTargetLeafSetNodeTest() throws Exception {
184         mockBodyReader("", xmlToPatchBodyReader, false);
185         final var inputStream = new ByteArrayInputStream("""
186             <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
187                 <patch-id>set-patch</patch-id>
188                 <comment>YANG patch comment</comment>
189                 <edit>
190                     <edit-id>edit1</edit-id>
191                     <operation>replace</operation>
192                     <target>/set-model:cont-root/set-model:cont1/set-model:my-set="data1"</target>
193                     <value>
194                         <my-set xmlns="set:ns">data1</my-set>
195                     </value>
196                 </edit>
197             </yang-patch>
198             """.getBytes(StandardCharsets.UTF_8));
199         final var expectedData = Builders.leafSetBuilder()
200                 .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
201                 .withChild(Builders.leafSetEntryBuilder()
202                         .withNodeIdentifier(new NodeWithValue(LEAF_SET_QNAME, "data1"))
203                         .withValue("data1")
204                         .build())
205                 .build();
206         final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
207         checkPatchContext(returnValue);
208         final var data = returnValue.getData().get(0).getNode();
209         assertEquals(LEAF_SET_QNAME, data.getIdentifier().getNodeType());
210         assertEquals(expectedData, data);
211     }
212
213     /**
214      * Test of Yang Patch on the top unkeyed list element.
215      */
216     @Test
217     public void moduleTargetUnkeyedListNodeTest() throws Exception {
218         mockBodyReader("", xmlToPatchBodyReader, false);
219         final var inputStream = new ByteArrayInputStream("""
220             <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
221                 <patch-id>list-patch</patch-id>
222                 <comment>YANG patch comment</comment>
223                 <edit>
224                     <edit-id>edit1</edit-id>
225                     <operation>replace</operation>
226                     <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
227                     <value>
228                         <unkeyed-list xmlns="list:ns">
229                             <leaf1>data1</leaf1>
230                             <leaf2>data2</leaf2>
231                         </unkeyed-list>
232                     </value>
233                 </edit>
234             </yang-patch>
235             """.getBytes(StandardCharsets.UTF_8));
236         final var expectedData = Builders.unkeyedListBuilder()
237                 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
238                 .withChild(Builders.unkeyedListEntryBuilder()
239                         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
240                         .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
241                         .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
242                         .build())
243                 .build();
244         final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
245         checkPatchContext(returnValue);
246         final var data = returnValue.getData().get(0).getNode();
247         assertEquals(LIST_QNAME, data.getIdentifier().getNodeType());
248         assertEquals(expectedData, data);
249     }
250
251     /**
252      * Test of Yang Patch on the top case node element.
253      */
254     @Test
255     public void moduleTargetCaseNodeTest() throws Exception {
256         mockBodyReader("", xmlToPatchBodyReader, false);
257         final var inputStream = new ByteArrayInputStream("""
258             <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
259                 <patch-id>choice-patch</patch-id>
260                 <comment>YANG patch comment</comment>
261                 <edit>
262                     <edit-id>edit1</edit-id>
263                     <operation>replace</operation>
264                     <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
265                     <value>
266                         <case-cont1 xmlns="choice:ns">
267                             <case-leaf1>data</case-leaf1>
268                         </case-cont1>
269                     </value>
270                 </edit>
271             </yang-patch>
272             """.getBytes(StandardCharsets.UTF_8));
273         final var expectedData = Builders.containerBuilder()
274                 .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
275                 .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
276                 .build();
277         final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
278         checkPatchContext(returnValue);
279         final var data = returnValue.getData().get(0).getNode();
280         assertEquals(CHOICE_CONT_QNAME, data.getIdentifier().getNodeType());
281         assertEquals(expectedData, data);
282     }
283 }