Bug 7933: NPE when posting using XML
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / rest / impl / test / providers / TestXmlBodyReader.java
1 /**
2  * Copyright (c) 2015 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
9 package org.opendaylight.controller.sal.rest.impl.test.providers;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14
15 import com.google.common.base.Optional;
16 import com.google.common.collect.Sets;
17 import java.io.File;
18 import java.io.InputStream;
19 import java.net.URI;
20 import java.text.ParseException;
21 import java.util.Collection;
22 import javax.ws.rs.core.MediaType;
23 import org.junit.Assert;
24 import org.junit.BeforeClass;
25 import org.junit.Test;
26 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
27 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
28 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
29 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
30 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
38 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
39 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
40 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.Module;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
44
45 /**
46  * sal-rest-connector
47  * org.opendaylight.controller.sal.rest.impl.test.providers
48  *
49  *
50  *
51  * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
52  *
53  * Created: Mar 7, 2015
54  */
55 public class TestXmlBodyReader extends AbstractBodyReaderTest {
56
57     private final XmlNormalizedNodeBodyReader xmlBodyReader;
58     private static SchemaContext schemaContext;
59     private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = initializeInstanceIdentifierModule();
60
61     private static QNameModule initializeInstanceIdentifierModule() {
62         try {
63             return QNameModule.create(URI.create("instance:identifier:module"),
64                 SimpleDateFormatUtil.getRevisionFormat().parse("2014-01-17"));
65         } catch (final ParseException e) {
66             throw new Error(e);
67         }
68     }
69
70     public TestXmlBodyReader() throws Exception {
71         super();
72         this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
73     }
74
75     @Override
76     protected MediaType getMediaType() {
77         return new MediaType(MediaType.APPLICATION_XML, null);
78     }
79
80     @BeforeClass
81     public static void initialization() throws Exception {
82         final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang");
83         testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
84         schemaContext = YangParserTestUtils.parseYangSources(testFiles);
85         controllerContext.setSchemas(schemaContext);
86     }
87
88     @Test
89     public void moduleDataTest() throws Exception {
90         final DataSchemaNode dataSchemaNode =
91                 schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
92         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName());
93         final String uri = "instance-identifier-module:cont";
94         mockBodyReader(uri, this.xmlBodyReader, false);
95         final InputStream inputStream = TestXmlBodyReader.class
96                 .getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
97         final NormalizedNodeContext returnValue = this.xmlBodyReader
98                 .readFrom(null, null, null, this.mediaType, null, inputStream);
99         checkNormalizedNodeContext(returnValue);
100         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
101     }
102
103     @Test
104     public void moduleSubContainerDataPutTest() throws Exception {
105         final DataSchemaNode dataSchemaNode =
106                 schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
107         final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
108         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
109         final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName);
110         final String uri = "instance-identifier-module:cont/cont1";
111         mockBodyReader(uri, this.xmlBodyReader, false);
112         final InputStream inputStream = TestXmlBodyReader.class
113                 .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
114         final NormalizedNodeContext returnValue = this.xmlBodyReader
115                 .readFrom(null, null, null, this.mediaType, null, inputStream);
116         checkNormalizedNodeContext(returnValue);
117         checkExpectValueNormalizeNodeContext(dataSchemaNodeOnPath, returnValue, dataII);
118     }
119
120     @Test
121     public void moduleSubContainerDataPostTest() throws Exception {
122         final DataSchemaNode dataSchemaNode =
123                 schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
124         final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
125         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
126         final String uri = "instance-identifier-module:cont";
127         mockBodyReader(uri, this.xmlBodyReader, true);
128         final InputStream inputStream = TestXmlBodyReader.class
129                 .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
130         final NormalizedNodeContext returnValue = this.xmlBodyReader
131                 .readFrom(null, null, null, this.mediaType, null, inputStream);
132         checkNormalizedNodeContext(returnValue);
133         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
134     }
135
136     @Test
137     public void moduleSubContainerAugmentDataPostTest() throws Exception {
138         final DataSchemaNode dataSchemaNode =
139                 schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
140         final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
141         final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment");
142         final YangInstanceIdentifier.AugmentationIdentifier augII = new YangInstanceIdentifier.AugmentationIdentifier(
143                 Sets.newHashSet(contAugmentQName));
144         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
145                 .node(augII).node(contAugmentQName);
146         final String uri = "instance-identifier-module:cont";
147         mockBodyReader(uri, this.xmlBodyReader, true);
148         final InputStream inputStream = TestXmlBodyReader.class
149                 .getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml");
150         final NormalizedNodeContext returnValue = this.xmlBodyReader
151                 .readFrom(null, null, null, this.mediaType, null, inputStream);
152         checkNormalizedNodeContext(returnValue);
153         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
154     }
155
156     @Test
157     public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
158         final DataSchemaNode dataSchemaNode =
159                 schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
160         final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
161         final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1");
162         final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
163         final QName containerQName = QName.create(augmentChoice1QName, "case-choice-case-container1");
164         final YangInstanceIdentifier.AugmentationIdentifier augChoice1II = new YangInstanceIdentifier.AugmentationIdentifier(
165                 Sets.newHashSet(augmentChoice1QName));
166         final YangInstanceIdentifier.AugmentationIdentifier augChoice2II = new YangInstanceIdentifier.AugmentationIdentifier(
167                 Sets.newHashSet(augmentChoice2QName));
168         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
169                 .node(augChoice1II).node(augmentChoice1QName).node(augChoice2II).node(augmentChoice2QName)
170                 .node(containerQName);
171         final String uri = "instance-identifier-module:cont";
172         mockBodyReader(uri, this.xmlBodyReader, true);
173         final InputStream inputStream = TestXmlBodyReader.class
174                 .getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml");
175         final NormalizedNodeContext returnValue = this.xmlBodyReader
176                 .readFrom(null, null, null, this.mediaType, null, inputStream);
177         checkNormalizedNodeContext(returnValue);
178         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
179     }
180
181     @Test
182     public void rpcModuleInputTest() throws Exception {
183         final String uri = "invoke-rpc-module:rpc-test";
184         mockBodyReader(uri, this.xmlBodyReader, true);
185         final InputStream inputStream = TestXmlBodyReader.class
186                 .getResourceAsStream("/invoke-rpc/xml/rpc-input.xml");
187         final NormalizedNodeContext returnValue = this.xmlBodyReader
188                 .readFrom(null, null, null, this.mediaType, null, inputStream);
189         checkNormalizedNodeContext(returnValue);
190         final ContainerNode contNode = (ContainerNode) returnValue.getData();
191         final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of(QName.create(contNode.getNodeType(), "cont"));
192         final Optional<DataContainerChild<? extends PathArgument, ?>> contDataNodePotential = contNode.getChild(yangCont
193                 .getLastPathArgument());
194         assertTrue(contDataNodePotential.isPresent());
195         final ContainerNode contDataNode = (ContainerNode) contDataNodePotential.get();
196         final YangInstanceIdentifier yangLeaf = YangInstanceIdentifier.of(QName.create(contDataNode.getNodeType(), "lf"));
197         final Optional<DataContainerChild<? extends PathArgument, ?>> leafDataNode = contDataNode.getChild(yangLeaf
198                 .getLastPathArgument());
199         assertTrue(leafDataNode.isPresent());
200         assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().getValue().toString()));
201     }
202
203     private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
204             final NormalizedNodeContext nnContext) {
205         checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null);
206     }
207
208     private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
209             final NormalizedNodeContext nnContext, final YangInstanceIdentifier dataNodeIdent) {
210         assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode());
211         assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
212         assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
213     }
214
215     /**
216      * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
217      * used to distinguish between them to find correct one. Check if container was found not only according to its name
218      * but also by correct namespace used in payload.
219      */
220     @Test
221     public void findFooContainerUsingNamespaceTest() throws Exception {
222         mockBodyReader("", this.xmlBodyReader, true);
223         final InputStream inputStream = TestXmlBodyReader.class
224                 .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
225         final NormalizedNodeContext returnValue = this.xmlBodyReader
226                 .readFrom(null, null, null, this.mediaType, null, inputStream);
227
228         // check return value
229         checkNormalizedNodeContext(returnValue);
230         // check if container was found both according to its name and namespace
231         assertEquals("Not correct container found, name was ignored",
232                 "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
233         assertEquals("Not correct container found, namespace was ignored",
234                 "foo:module", returnValue.getData().getNodeType().getNamespace().toString());
235     }
236
237     /**
238      * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
239      * used to distinguish between them to find correct one. Check if container was found not only according to its name
240      * but also by correct namespace used in payload.
241      */
242     @Test
243     public void findBarContainerUsingNamespaceTest() throws Exception {
244         mockBodyReader("", this.xmlBodyReader, true);
245         final InputStream inputStream = TestXmlBodyReader.class
246                 .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
247         final NormalizedNodeContext returnValue = this.xmlBodyReader
248                 .readFrom(null, null, null, this.mediaType, null, inputStream);
249
250         // check return value
251         checkNormalizedNodeContext(returnValue);
252         // check if container was found both according to its name and namespace
253         assertEquals("Not correct container found, name was ignored",
254                 "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
255         assertEquals("Not correct container found, namespace was ignored",
256                 "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
257     }
258
259     /**
260      * Test PUT operation when message root element is not the same as the last element in request URI.
261      * PUT operation message should always start with schema node from URI otherwise exception should be
262      * thrown.
263      */
264     @Test
265     public void wrongRootElementTest() throws Exception {
266         mockBodyReader("instance-identifier-module:cont", this.xmlBodyReader, false);
267         final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream(
268                 "/instanceidentifier/xml/bug7933.xml");
269         try {
270             this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream);
271             Assert.fail("Test should fail due to malformed PUT operation message");
272         } catch (final RestconfDocumentedException exception) {
273             final RestconfError restconfError = exception.getErrors().get(0);
274             Assert.assertEquals(RestconfError.ErrorType.PROTOCOL, restconfError.getErrorType());
275             Assert.assertEquals(RestconfError.ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag());
276         }
277     }
278 }