Bug 5679 - ietf-yang-library module implemetation
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / restconf / utils / mapping / RestconfMappingNodeUtilTest.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.utils.mapping;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.fail;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.when;
16 import com.google.common.collect.Sets;
17 import java.util.AbstractMap;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import org.junit.Before;
27 import org.junit.BeforeClass;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.rules.ExpectedException;
31 import org.mockito.Mock;
32 import org.mockito.MockitoAnnotations;
33 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
34 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
35 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
36 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
37 import org.opendaylight.restconf.Draft18;
38 import org.opendaylight.restconf.Draft18.IetfYangLibrary;
39 import org.opendaylight.restconf.Draft18.MonitoringModule;
40 import org.opendaylight.restconf.Draft18.RestconfModule;
41 import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
47 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
49 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode;
54 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
57 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
58 import org.opendaylight.yangtools.yang.model.api.Module;
59 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
60
61 /**
62  * Unit tests for {@link RestconfMappingNodeUtil}
63  */
64 public class RestconfMappingNodeUtilTest {
65     @Rule
66     public ExpectedException thrown = ExpectedException.none();
67
68     @Mock private ListSchemaNode mockStreamList;
69     @Mock private LeafSchemaNode leafName;
70     @Mock private LeafSchemaNode leafDescription;
71     @Mock private LeafSchemaNode leafReplaySupport;
72     @Mock private LeafSchemaNode leafReplayLog;
73     @Mock private LeafSchemaNode leafEvents;
74
75     private static Set<Module> modules;
76     private static SchemaContext schemaContext;
77
78     private static Set<Module> modulesRest;
79     private Set<DataSchemaNode> allStreamChildNodes;
80
81     @BeforeClass
82     public static void loadTestSchemaContextAndModules() throws Exception {
83         RestconfMappingNodeUtilTest.schemaContext = TestRestconfUtils.loadSchemaContext(
84                 "/modules/restconf-module-testing");
85         RestconfMappingNodeUtilTest.modules = TestRestconfUtils.loadSchemaContext("/modules").getModules();
86         RestconfMappingNodeUtilTest.modulesRest =
87                 TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules();
88     }
89
90     @Before
91     public void setup() throws Exception {
92         MockitoAnnotations.initMocks(this);
93
94         when(this.leafName.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.NAME));
95         when(this.leafDescription.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.DESCRIPTION));
96         when(this.leafReplaySupport.getQName()).thenReturn(
97                 QName.create("", RestconfMappingNodeConstants.REPLAY_SUPPORT));
98         when(this.leafReplayLog.getQName()).thenReturn(QName.create(RestconfMappingNodeConstants.REPLAY_LOG));
99         when(this.leafEvents.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.EVENTS));
100
101         this.allStreamChildNodes = Sets.newHashSet(
102                 this.leafName, this.leafDescription, this.leafReplaySupport, this.leafReplayLog, this.leafEvents);
103     }
104
105     /**
106      * Test of writing modules into {@link RestconfModule#MODULE_LIST_SCHEMA_NODE} and checking if modules were
107      * correctly written.
108      */
109     @Test
110     public void restconfMappingNodeTest() {
111         // write modules into list module in Restconf
112         final Module ietfYangLibMod =
113                 schemaContext.findModuleByNamespaceAndRevision(IetfYangLibrary.URI_MODULE, IetfYangLibrary.DATE);
114         final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> modules =
115                 RestconfMappingNodeUtil.mapModulesByIetfYangLibraryYang(RestconfMappingNodeUtilTest.modules,
116                         ietfYangLibMod, schemaContext, "1");
117
118         // verify loaded modules
119         verifyLoadedModules((ContainerNode) modules);
120     }
121
122     /**
123      * Positive test of writing one stream to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} and checking if stream
124      * was correctly written.
125      */
126     @Test
127     public void toStreamEntryNodeTest() {
128         // test stream name
129         final String stream1 = "stream-1";
130
131         // get list stream node from Restconf module
132         final ListSchemaNode listStream = (ListSchemaNode) RestconfSchemaUtil.getRestconfSchemaNode(
133                 getTestingRestconfModule("ietf-restconf"), MonitoringModule.STREAM_LIST_SCHEMA_NODE);
134
135         // write stream to list stream node
136         final MapEntryNode mapEntryNode = RestconfMappingNodeUtil.toStreamEntryNode(stream1, listStream);
137
138         // verify
139         verifyStream(stream1, mapEntryNode);
140     }
141
142     /**
143      * Try to map streams when {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} is <code>null</code>.
144      * Test is expected to fail catching <code>IllegalStateException</code>.
145      */
146     @Test
147     public void toStreamEntryNodeNullListStreamNegativeTest() {
148         this.thrown.expect(IllegalStateException.class);
149         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", null);
150     }
151
152     /**
153      * Test trying to map streams to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which is not of type list.
154      * Test is expected to fail with <code>IllegalStateException</code>.
155      */
156     @Test
157     public void toStreamEntryNodeIllegalListStreamNegativeTest() {
158         this.thrown.expect(IllegalStateException.class);
159         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mock(LeafSchemaNode.class));
160     }
161
162     /**
163      * Test case with target {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which does not contain any child nodes.
164      * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
165      * compared to expected values.
166      */
167     @Test
168     public void toStreamEntryNodeSchemaNodeWithoutChildsNegativeTest() {
169         final ListSchemaNode mockListNode = mock(ListSchemaNode.class);
170         when(mockListNode.getChildNodes()).thenReturn(Collections.EMPTY_SET);
171
172        try {
173            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mockListNode);
174            fail("Test should fail due to no child nodes in"
175                    + MonitoringModule.STREAM_LIST_SCHEMA_NODE
176                    + " node");
177        } catch (final RestconfDocumentedException e) {
178            assertEquals("Error type is not correct",
179                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
180            assertEquals("Error tag is not correct",
181                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
182            assertEquals("Error status code is not correct",
183                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
184        }
185     }
186
187     /**
188      * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#NAME}.
189      * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
190      * compared to expected values.
191      */
192     @Test
193     public void toStreamEntryNodeMissingStreamNameNegativeTest() {
194         prepareMockListWithMissingLeaf(this.leafName);
195
196         try {
197             RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
198             fail("Test should fail due to missing "
199                     + RestconfMappingNodeConstants.NAME
200                     + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
201         } catch (final RestconfDocumentedException e) {
202             assertEquals("Error type is not correct",
203                     ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
204             assertEquals("Error tag is not correct",
205                     ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
206             assertEquals("Error status code is not correct",
207                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
208         }
209     }
210
211     /**
212      * Test case when target list stream does not contain child with name
213      * {@link RestconfMappingNodeConstants#DESCRIPTION}. Test is catching <code>RestconfDocumentedException</code> and
214      * checking error type and error tag.
215      */
216     @Test
217     public void toStreamEntryNodeMissingStreamDescriptionNegativeTest() {
218         prepareMockListWithMissingLeaf(this.leafDescription);
219
220         try {
221             RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
222             fail("Test should fail due to missing "
223                     + RestconfMappingNodeConstants.DESCRIPTION
224                     + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
225         } catch (final RestconfDocumentedException e) {
226             assertEquals("Error type is not correct",
227                     ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
228             assertEquals("Error tag is not correct",
229                     ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
230             assertEquals("Error status code is not correct",
231                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
232         }
233     }
234
235     /**
236      * Test case when target list stream does not contain child with name
237      * {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}. Test is catching <code>RestconfDocumentedException</code>
238      * and checking error type and error tag.
239      */
240     @Test
241     public void toStreamEntryNodeMissingStreamReplaySupportNegativeTest() {
242         prepareMockListWithMissingLeaf(this.leafReplaySupport);
243
244         try {
245             RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
246             fail("Test should fail due to missing "
247                     + RestconfMappingNodeConstants.REPLAY_SUPPORT
248                     + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
249         } catch (final RestconfDocumentedException e) {
250             assertEquals("Error type is not correct",
251                     ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
252             assertEquals("Error tag is not correct",
253                     ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
254             assertEquals("Error status code is not correct",
255                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
256         }
257     }
258
259     /**
260      * Test case when target list stream does not contain child with name
261      * {@link RestconfMappingNodeConstants#REPLAY_LOG}. Test is catching <code>RestconfDocumentedException</code> and
262      * checking error type and error tag.
263      */
264     @Test
265     public void toStreamEntryNodeMissingStreamReplayLogNegativeTest() {
266         prepareMockListWithMissingLeaf(this.leafReplayLog);
267
268         try {
269             RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
270             fail("Test should fail due to missing "
271                     + RestconfMappingNodeConstants.REPLAY_LOG
272                     + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
273         } catch (final RestconfDocumentedException e) {
274             assertEquals("Error type is not correct",
275                     ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
276             assertEquals("Error tag is not correct",
277                     ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
278             assertEquals("Error status code is not correct",
279                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
280         }
281     }
282
283     /**
284      * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#EVENTS}.
285      * Test is catching <code>RestconfDocumentedException</code> and checking error type, error tag and error status
286      * code.
287      */
288     @Test
289     public void toStreamEntryNodeMissingStreamEventsNegativeTest() {
290         prepareMockListWithMissingLeaf(this.leafEvents);
291
292         try {
293             RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
294             fail("Test should fail due to missing "
295                     + RestconfMappingNodeConstants.EVENTS
296                     + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
297         } catch (final RestconfDocumentedException e) {
298             assertEquals("Error type is not correct",
299                     ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
300             assertEquals("Error tag is not correct",
301                     ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
302             assertEquals("Error status code is not correct",
303                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
304         }
305     }
306
307     /**
308      * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#NAME}. Test is
309      * expecting <code>IllegalStateException</code>.
310      */
311     @Test
312     public void toStreamEntryNodeStreamNameNegativeTest() {
313         prepareMockListWithIllegalLeaf(this.leafName);
314
315         this.thrown.expect(IllegalStateException.class);
316         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
317     }
318
319     /**
320      * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#DESCRIPTION}.
321      * Test is expecting <code>IllegalStateException</code>.
322      */
323     @Test
324     public void toStreamEntryNodeStreamDescriptionNegativeTest() {
325         prepareMockListWithIllegalLeaf(this.leafDescription);
326
327         this.thrown.expect(IllegalStateException.class);
328         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
329     }
330
331     /**
332      * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}.
333      * Test is expecting <code>IllegalStateException</code>.
334      */
335     @Test
336     public void toStreamEntryNodeStreamReplaySupportNegativeTest() {
337         prepareMockListWithIllegalLeaf(this.leafReplaySupport);
338
339         this.thrown.expect(IllegalStateException.class);
340         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
341     }
342
343     /**
344      * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_LOG}.
345      * Test is expecting <code>IllegalStateException</code>.
346      */
347     @Test
348     public void toStreamEntryNodeStreamReplayLogNegativeTest() {
349         prepareMockListWithIllegalLeaf(this.leafReplayLog);
350
351         this.thrown.expect(IllegalStateException.class);
352         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
353     }
354
355     /**
356      * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#EVENTS}. Test is
357      * expecting <code>IllegalStateException</code>.
358      */
359     @Test
360     public void toStreamEntryNodeStreamEventsNegativeTest() {
361         prepareMockListWithIllegalLeaf(this.leafEvents);
362
363         this.thrown.expect(IllegalStateException.class);
364         RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
365     }
366
367     /**
368      * Verify loaded modules
369      *
370      * @param containerNode
371      *            - modules
372      */
373     private void verifyLoadedModules(final ContainerNode containerNode) {
374
375         final Map<String, String> loadedModules = new HashMap<>();
376
377         for (final DataContainerChild<? extends PathArgument, ?> child : containerNode.getValue()) {
378             if (child instanceof LeafNode) {
379                 assertEquals(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME, ((LeafNode) child).getNodeType());
380             }
381             if (child instanceof MapNode) {
382                 assertEquals(IetfYangLibrary.MODULE_QNAME_LIST, ((MapNode) child).getNodeType());
383                 for (final MapEntryNode mapEntryNode : ((MapNode) child).getValue()) {
384                     String name = "";
385                     String revision = "";
386                     for (final DataContainerChild<? extends PathArgument, ?> dataContainerChild : mapEntryNode
387                             .getValue()) {
388                         switch (dataContainerChild.getNodeType().getLocalName()) {
389                             case IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF:
390                                 name = String.valueOf(((LeafNode) dataContainerChild).getValue());
391                                 break;
392                             case IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF:
393                                 revision = String.valueOf(((LeafNode) dataContainerChild).getValue());
394                                 break;
395                         }
396                     }
397                     loadedModules.put(name, revision);
398                 }
399             }
400         }
401
402         verifyLoadedModules(RestconfMappingNodeUtilTest.modulesRest, loadedModules);
403     }
404
405     /**
406      * Verify if correct modules were loaded into Restconf module by comparison with modules from
407      * <code>SchemaContext</code>.
408      * @param expectedModules Modules from <code>SchemaContext</code>
409      * @param loadedModules Loaded modules into Restconf module
410      */
411     private final void verifyLoadedModules(final Set<Module> expectedModules,
412                                            final Map<String, String> loadedModules) {
413         assertEquals("Number of loaded modules is not as expected", expectedModules.size(), loadedModules.size());
414         for (final Module m : expectedModules) {
415             final String name = m.getName();
416
417             final String revision = loadedModules.get(name);
418             assertNotNull("Expected module not found", revision);
419             assertEquals("Not correct revision of loaded module",
420                     SimpleDateFormatUtil.getRevisionFormat().format(m.getRevision()), revision);
421
422             loadedModules.remove(name);
423         }
424     }
425
426     /**
427      * Verify if a stream was correctly written into {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} node in Restconf
428      * module.
429      * @param streamName Expected stream name
430      * @param streamNode Writetn strem node from Restconf module
431      */
432     private final void verifyStream(final String streamName, final MapEntryNode streamNode) {
433         assertNotNull("Stream node can not be null", streamNode);
434         final Iterator entries = ((AbstractImmutableDataContainerAttrNode) streamNode)
435                 .getChildren().entrySet().iterator();
436         boolean notAllowedKey = false;
437
438         while (entries.hasNext()) {
439             final Entry e = ((AbstractMap.SimpleImmutableEntry) entries.next());
440             final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
441
442             switch (key) {
443                 case RestconfMappingNodeConstants.NAME :
444                     assertEquals("Stream name value is not as expected",
445                             streamName, ((LeafNode) e.getValue()).getValue());
446                     break;
447                 case RestconfMappingNodeConstants.DESCRIPTION :
448                     assertEquals("Stream description value is not as expected",
449                             RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
450                     break;
451                 case RestconfMappingNodeConstants.REPLAY_SUPPORT :
452                     assertEquals("Stream replay support value is not as expected",
453                             RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
454                     break;
455                 case RestconfMappingNodeConstants.REPLAY_LOG :
456                     assertEquals("Stream replay log value is not as expected",
457                             RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
458                     break;
459                 case RestconfMappingNodeConstants.EVENTS :
460                     assertEquals("Stream events value is not as expected",
461                             RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
462                     break;
463                 default:
464                     notAllowedKey = true;
465                     break;
466             }
467         }
468
469         assertFalse("Not allowed key in list stream found", notAllowedKey);
470     }
471
472     /**
473      * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
474      * name or by namespace. This method is looking for Restconf test module by its name.
475      * @param s Testing Restconf module name
476      * @return Restconf module
477      */
478     private Module getTestingRestconfModule(final String s) {
479         return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
480                 s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
481     }
482
483     /**
484      * Updates {@link this#mockStreamList} to NOT contains specified leaf.
485      * @param leaf Leaf to be missing
486      */
487     private void prepareMockListWithMissingLeaf(final LeafSchemaNode leaf) {
488         // prepare set of leaf without selected leaf
489         final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
490         childLeafs.remove(leaf);
491
492         // mock list leaf nodes
493         when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
494     }
495
496     /**
497      * Updates {@link this#mockStreamList} to contains specified leaf which is not of type {@link LeafSchemaNode}.
498      * @param leaf Leaf to be changes
499      */
500     private void prepareMockListWithIllegalLeaf(final LeafSchemaNode leaf) {
501         // prepare set of leaf without selected leaf
502         final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
503         childLeafs.remove(leaf);
504
505         // add leaf-list with the same local name as removed leaf
506         final String localName = leaf.getQName().getLocalName();
507         final LeafListSchemaNode mockLeafList = mock(LeafListSchemaNode.class);
508         when(mockLeafList.getQName()).thenReturn(QName.create("", localName));
509         childLeafs.add(mockLeafList);
510
511         // mock list leaf nodes
512         when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
513     }
514 }