Migrate restconf to MD-SAL APIs
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / ReadDataTransactionUtilTest.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.rests.utils;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.fail;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.when;
16 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
17
18 import com.google.common.collect.ImmutableList;
19 import java.util.Collections;
20 import java.util.Optional;
21 import javax.ws.rs.core.MultivaluedHashMap;
22 import javax.ws.rs.core.UriInfo;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.mockito.Mock;
26 import org.mockito.Mockito;
27 import org.mockito.MockitoAnnotations;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
31 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
32 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
33 import org.opendaylight.restconf.common.context.WriterParameters;
34 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
35 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
36 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
37 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
38 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
47 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
48 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51
52 public class ReadDataTransactionUtilTest {
53
54     private static final TestData DATA = new TestData();
55     private static final YangInstanceIdentifier.NodeIdentifier NODE_IDENTIFIER = new YangInstanceIdentifier
56             .NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
57
58     private TransactionVarsWrapper wrapper;
59     @Mock
60     private DOMTransactionChain transactionChain;
61     @Mock
62     private InstanceIdentifierContext<ContainerSchemaNode> context;
63     @Mock
64     private DOMDataTreeReadTransaction read;
65     @Mock
66     private SchemaContext schemaContext;
67     @Mock
68     private ContainerSchemaNode containerSchemaNode;
69     @Mock
70     private LeafSchemaNode containerChildNode;
71     private QName containerChildQName;
72
73     @Before
74     public void setUp() {
75         MockitoAnnotations.initMocks(this);
76
77         containerChildQName = QName.create("ns", "2016-02-28", "container-child");
78
79         when(transactionChain.newReadOnlyTransaction()).thenReturn(read);
80         when(context.getSchemaContext()).thenReturn(schemaContext);
81         when(context.getSchemaNode()).thenReturn(containerSchemaNode);
82         when(containerSchemaNode.getQName()).thenReturn(NODE_IDENTIFIER.getNodeType());
83         when(containerChildNode.getQName()).thenReturn(containerChildQName);
84         when(containerSchemaNode.getDataChildByName(containerChildQName)).thenReturn(containerChildNode);
85
86         DOMDataBroker mockDataBroker = Mockito.mock(DOMDataBroker.class);
87         Mockito.doReturn(transactionChain).when(mockDataBroker).createTransactionChain(Mockito.any());
88         wrapper = new TransactionVarsWrapper(this.context, null, new TransactionChainHandler(mockDataBroker));
89     }
90
91     @Test
92     public void readDataConfigTest() {
93         doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
94                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
95         doReturn(DATA.path).when(context).getInstanceIdentifier();
96         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
97         final NormalizedNode<?, ?> normalizedNode =
98                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
99         assertEquals(DATA.data3, normalizedNode);
100     }
101
102     @Test
103     public void readAllHavingOnlyConfigTest() {
104         doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
105                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
106         doReturn(immediateFluentFuture(Optional.empty())).when(read)
107                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
108         doReturn(DATA.path).when(context).getInstanceIdentifier();
109         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
110         final NormalizedNode<?, ?> normalizedNode =
111                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
112         assertEquals(DATA.data3, normalizedNode);
113     }
114
115     @Test
116     public void readAllHavingOnlyNonConfigTest() {
117         doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(read)
118                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
119         doReturn(immediateFluentFuture(Optional.empty())).when(read)
120                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
121         doReturn(DATA.path2).when(context).getInstanceIdentifier();
122         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
123         final NormalizedNode<?, ?> normalizedNode =
124                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
125         assertEquals(DATA.data2, normalizedNode);
126     }
127
128     @Test
129     public void readDataNonConfigTest() {
130         doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(read)
131                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
132         doReturn(DATA.path2).when(context).getInstanceIdentifier();
133         final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
134         final NormalizedNode<?, ?> normalizedNode =
135                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
136         assertEquals(DATA.data2, normalizedNode);
137     }
138
139     @Test
140     public void readContainerDataAllTest() {
141         doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
142                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
143         doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
144                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
145         doReturn(DATA.path).when(context).getInstanceIdentifier();
146         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
147         final NormalizedNode<?, ?> normalizedNode =
148                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
149         final ContainerNode checkingData = Builders
150                 .containerBuilder()
151                 .withNodeIdentifier(NODE_IDENTIFIER)
152                 .withChild(DATA.contentLeaf)
153                 .withChild(DATA.contentLeaf2)
154                 .build();
155         assertEquals(checkingData, normalizedNode);
156     }
157
158     @Test
159     public void readContainerDataConfigNoValueOfContentTest() {
160         doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
161                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
162         doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
163                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
164         doReturn(DATA.path).when(context).getInstanceIdentifier();
165         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
166                 RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
167         final ContainerNode checkingData = Builders
168                 .containerBuilder()
169                 .withNodeIdentifier(NODE_IDENTIFIER)
170                 .withChild(DATA.contentLeaf)
171                 .withChild(DATA.contentLeaf2)
172                 .build();
173         assertEquals(checkingData, normalizedNode);
174     }
175
176     @Test
177     public void readListDataAllTest() {
178         doReturn(immediateFluentFuture(Optional.of(DATA.listData))).when(read)
179                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
180         doReturn(immediateFluentFuture(Optional.of(DATA.listData2))).when(read)
181                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
182         doReturn(DATA.path3).when(context).getInstanceIdentifier();
183         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
184         final NormalizedNode<?, ?> normalizedNode =
185                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
186         final MapNode checkingData = Builders
187                 .mapBuilder()
188                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
189                 .withChild(DATA.checkData)
190                 .build();
191         assertEquals(checkingData, normalizedNode);
192     }
193
194     @Test
195     public void readOrderedListDataAllTest() {
196         doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode1))).when(read)
197                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
198         doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode2))).when(read)
199                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
200         doReturn(DATA.path3).when(context).getInstanceIdentifier();
201
202         final NormalizedNode<?, ?> normalizedNode =
203                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
204
205         final MapNode expectedData = Builders.orderedMapBuilder()
206                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname)).withChild(DATA.checkData)
207                 .build();
208         assertEquals(expectedData, normalizedNode);
209     }
210
211     @Test
212     public void readUnkeyedListDataAllTest() {
213         doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode1))).when(read)
214                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
215         doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode2))).when(read)
216                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
217         doReturn(DATA.path3).when(context).getInstanceIdentifier();
218
219         final NormalizedNode<?, ?> normalizedNode =
220                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
221
222         final UnkeyedListNode expectedData = Builders.unkeyedListBuilder()
223                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
224                 .withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(
225                         new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
226                         .withChild(DATA.unkeyedListEntryNode1.getValue().iterator().next())
227                         .withChild(DATA.unkeyedListEntryNode2.getValue().iterator().next()).build()).build();
228         assertEquals(expectedData, normalizedNode);
229     }
230
231     @Test
232     public void readLeafListDataAllTest() {
233         doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode1))).when(read)
234                 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
235         doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode2))).when(read)
236                 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
237         doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
238
239         final NormalizedNode<?, ?> normalizedNode =
240                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
241
242         final LeafSetNode<String> expectedData = Builders.<String>leafSetBuilder().withNodeIdentifier(
243                 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
244                         ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.leafSetNode1.getValue())
245                         .addAll(DATA.leafSetNode2.getValue()).build()).build();
246         assertEquals(expectedData, normalizedNode);
247     }
248
249     @Test
250     public void readOrderedLeafListDataAllTest() {
251         doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode1))).when(read)
252                 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
253         doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode2))).when(read)
254                 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
255         doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
256
257         final NormalizedNode<?, ?> normalizedNode =
258                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
259
260         final LeafSetNode<String> expectedData = Builders.<String>orderedLeafSetBuilder().withNodeIdentifier(
261                 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
262                         ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.orderedLeafSetNode1.getValue())
263                         .addAll(DATA.orderedLeafSetNode2.getValue()).build()).build();
264         assertEquals(expectedData, normalizedNode);
265     }
266
267     @Test
268     public void readDataWrongPathOrNoContentTest() {
269         doReturn(immediateFluentFuture(Optional.empty())).when(read)
270                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
271         doReturn(DATA.path2).when(context).getInstanceIdentifier();
272         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
273         final NormalizedNode<?, ?> normalizedNode =
274                 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
275         assertNull(normalizedNode);
276     }
277
278     @Test(expected = RestconfDocumentedException.class)
279     public void readDataFailTest() {
280         final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
281         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
282                 valueOfContent, wrapper, schemaContext);
283         assertNull(normalizedNode);
284     }
285
286     /**
287      * Test of parsing default parameters from URI request.
288      */
289     @Test
290     public void parseUriParametersDefaultTest() {
291         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
292         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
293
294         // no parameters, default values should be used
295         when(uriInfo.getQueryParameters()).thenReturn(parameters);
296
297         final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
298
299         assertEquals("Not correctly parsed URI parameter",
300                 RestconfDataServiceConstant.ReadData.ALL, parsedParameters.getContent());
301         assertNull("Not correctly parsed URI parameter",
302                 parsedParameters.getDepth());
303         assertNull("Not correctly parsed URI parameter",
304                 parsedParameters.getFields());
305     }
306
307     /**
308      * Test of parsing user defined parameters from URI request.
309      */
310     @Test
311     public void parseUriParametersUserDefinedTest() {
312         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
313         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
314
315         final String content = "config";
316         final String depth = "10";
317         final String fields = containerChildQName.getLocalName();
318
319         parameters.put("content", Collections.singletonList(content));
320         parameters.put("depth", Collections.singletonList(depth));
321         parameters.put("fields", Collections.singletonList(fields));
322
323         when(uriInfo.getQueryParameters()).thenReturn(parameters);
324
325         final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
326
327         // content
328         assertEquals("Not correctly parsed URI parameter",
329                 content, parsedParameters.getContent());
330
331         // depth
332         assertNotNull("Not correctly parsed URI parameter",
333                 parsedParameters.getDepth());
334         assertEquals("Not correctly parsed URI parameter",
335                 depth, parsedParameters.getDepth().toString());
336
337         // fields
338         assertNotNull("Not correctly parsed URI parameter",
339                 parsedParameters.getFields());
340         assertEquals("Not correctly parsed URI parameter",
341                 1, parsedParameters.getFields().size());
342         assertEquals("Not correctly parsed URI parameter",
343                 1, parsedParameters.getFields().get(0).size());
344         assertEquals("Not correctly parsed URI parameter",
345                 containerChildQName, parsedParameters.getFields().get(0).iterator().next());
346     }
347
348     /**
349      * Negative test of parsing request URI parameters when content parameter has not allowed value.
350      */
351     @Test
352     public void parseUriParametersContentParameterNegativeTest() {
353         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
354         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
355
356         parameters.put("content", Collections.singletonList("not-allowed-parameter-value"));
357         when(uriInfo.getQueryParameters()).thenReturn(parameters);
358
359         try {
360             ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
361             fail("Test expected to fail due to not allowed parameter value");
362         } catch (final RestconfDocumentedException e) {
363             // Bad request
364             assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
365             assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
366             assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
367         }
368     }
369
370     /**
371      * Negative test of parsing request URI parameters when depth parameter has not allowed value.
372      */
373     @Test
374     public void parseUriParametersDepthParameterNegativeTest() {
375         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
376         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
377
378         // inserted value is not allowed
379         parameters.put("depth", Collections.singletonList("bounded"));
380         when(uriInfo.getQueryParameters()).thenReturn(parameters);
381
382         try {
383             ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
384             fail("Test expected to fail due to not allowed parameter value");
385         } catch (final RestconfDocumentedException e) {
386             // Bad request
387             assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
388             assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
389             assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
390         }
391     }
392
393     /**
394      * Negative test of parsing request URI parameters when depth parameter has not allowed value (less than minimum).
395      */
396     @Test
397     public void parseUriParametersDepthMinimalParameterNegativeTest() {
398         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
399         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
400
401         // inserted value is too low
402         parameters.put(
403                 "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MIN_DEPTH - 1)));
404         when(uriInfo.getQueryParameters()).thenReturn(parameters);
405
406         try {
407             ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
408             fail("Test expected to fail due to not allowed parameter value");
409         } catch (final RestconfDocumentedException e) {
410             // Bad request
411             assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
412             assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
413             assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
414         }
415     }
416
417     /**
418      * Negative test of parsing request URI parameters when depth parameter has not allowed value (more than maximum).
419      */
420     @Test
421     public void parseUriParametersDepthMaximalParameterNegativeTest() {
422         final UriInfo uriInfo = Mockito.mock(UriInfo.class);
423         final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
424
425         // inserted value is too high
426         parameters.put(
427                 "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MAX_DEPTH + 1)));
428         when(uriInfo.getQueryParameters()).thenReturn(parameters);
429
430         try {
431             ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
432             fail("Test expected to fail due to not allowed parameter value");
433         } catch (final RestconfDocumentedException e) {
434             // Bad request
435             assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
436             assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
437             assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
438         }
439     }
440 }