2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.utils.mapping;
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;
24 import java.util.Map.Entry;
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;
62 * Unit tests for {@link RestconfMappingNodeUtil}
64 public class RestconfMappingNodeUtilTest {
66 public ExpectedException thrown = ExpectedException.none();
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;
75 private static Set<Module> modules;
76 private static SchemaContext schemaContext;
78 private static Set<Module> modulesRest;
79 private Set<DataSchemaNode> allStreamChildNodes;
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();
91 public void setup() throws Exception {
92 MockitoAnnotations.initMocks(this);
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));
101 this.allStreamChildNodes = Sets.newHashSet(
102 this.leafName, this.leafDescription, this.leafReplaySupport, this.leafReplayLog, this.leafEvents);
106 * Test of writing modules into {@link RestconfModule#MODULE_LIST_SCHEMA_NODE} and checking if modules were
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");
118 // verify loaded modules
119 verifyLoadedModules((ContainerNode) modules);
123 * Positive test of writing one stream to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} and checking if stream
124 * was correctly written.
127 public void toStreamEntryNodeTest() {
129 final String stream1 = "stream-1";
131 // get list stream node from Restconf module
132 final ListSchemaNode listStream = (ListSchemaNode) RestconfSchemaUtil.getRestconfSchemaNode(
133 getTestingRestconfModule("ietf-restconf"), MonitoringModule.STREAM_LIST_SCHEMA_NODE);
135 // write stream to list stream node
136 final MapEntryNode mapEntryNode = RestconfMappingNodeUtil.toStreamEntryNode(stream1, listStream);
139 verifyStream(stream1, mapEntryNode);
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>.
147 public void toStreamEntryNodeNullListStreamNegativeTest() {
148 this.thrown.expect(IllegalStateException.class);
149 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", null);
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>.
157 public void toStreamEntryNodeIllegalListStreamNegativeTest() {
158 this.thrown.expect(IllegalStateException.class);
159 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mock(LeafSchemaNode.class));
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.
168 public void toStreamEntryNodeSchemaNodeWithoutChildsNegativeTest() {
169 final ListSchemaNode mockListNode = mock(ListSchemaNode.class);
170 when(mockListNode.getChildNodes()).thenReturn(Collections.EMPTY_SET);
173 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mockListNode);
174 fail("Test should fail due to no child nodes in"
175 + MonitoringModule.STREAM_LIST_SCHEMA_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());
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.
193 public void toStreamEntryNodeMissingStreamNameNegativeTest() {
194 prepareMockListWithMissingLeaf(this.leafName);
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());
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.
217 public void toStreamEntryNodeMissingStreamDescriptionNegativeTest() {
218 prepareMockListWithMissingLeaf(this.leafDescription);
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());
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.
241 public void toStreamEntryNodeMissingStreamReplaySupportNegativeTest() {
242 prepareMockListWithMissingLeaf(this.leafReplaySupport);
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());
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.
265 public void toStreamEntryNodeMissingStreamReplayLogNegativeTest() {
266 prepareMockListWithMissingLeaf(this.leafReplayLog);
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());
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
289 public void toStreamEntryNodeMissingStreamEventsNegativeTest() {
290 prepareMockListWithMissingLeaf(this.leafEvents);
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());
308 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#NAME}. Test is
309 * expecting <code>IllegalStateException</code>.
312 public void toStreamEntryNodeStreamNameNegativeTest() {
313 prepareMockListWithIllegalLeaf(this.leafName);
315 this.thrown.expect(IllegalStateException.class);
316 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
320 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#DESCRIPTION}.
321 * Test is expecting <code>IllegalStateException</code>.
324 public void toStreamEntryNodeStreamDescriptionNegativeTest() {
325 prepareMockListWithIllegalLeaf(this.leafDescription);
327 this.thrown.expect(IllegalStateException.class);
328 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
332 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}.
333 * Test is expecting <code>IllegalStateException</code>.
336 public void toStreamEntryNodeStreamReplaySupportNegativeTest() {
337 prepareMockListWithIllegalLeaf(this.leafReplaySupport);
339 this.thrown.expect(IllegalStateException.class);
340 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
344 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_LOG}.
345 * Test is expecting <code>IllegalStateException</code>.
348 public void toStreamEntryNodeStreamReplayLogNegativeTest() {
349 prepareMockListWithIllegalLeaf(this.leafReplayLog);
351 this.thrown.expect(IllegalStateException.class);
352 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
356 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#EVENTS}. Test is
357 * expecting <code>IllegalStateException</code>.
360 public void toStreamEntryNodeStreamEventsNegativeTest() {
361 prepareMockListWithIllegalLeaf(this.leafEvents);
363 this.thrown.expect(IllegalStateException.class);
364 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
368 * Verify loaded modules
370 * @param containerNode
373 private void verifyLoadedModules(final ContainerNode containerNode) {
375 final Map<String, String> loadedModules = new HashMap<>();
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());
381 if (child instanceof MapNode) {
382 assertEquals(IetfYangLibrary.MODULE_QNAME_LIST, ((MapNode) child).getNodeType());
383 for (final MapEntryNode mapEntryNode : ((MapNode) child).getValue()) {
385 String revision = "";
386 for (final DataContainerChild<? extends PathArgument, ?> dataContainerChild : mapEntryNode
388 switch (dataContainerChild.getNodeType().getLocalName()) {
389 case IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF:
390 name = String.valueOf(((LeafNode) dataContainerChild).getValue());
392 case IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF:
393 revision = String.valueOf(((LeafNode) dataContainerChild).getValue());
397 loadedModules.put(name, revision);
402 verifyLoadedModules(RestconfMappingNodeUtilTest.modulesRest, loadedModules);
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
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();
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);
422 loadedModules.remove(name);
427 * Verify if a stream was correctly written into {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} node in Restconf
429 * @param streamName Expected stream name
430 * @param streamNode Writetn strem node from Restconf module
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;
438 while (entries.hasNext()) {
439 final Entry e = ((AbstractMap.SimpleImmutableEntry) entries.next());
440 final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
443 case RestconfMappingNodeConstants.NAME :
444 assertEquals("Stream name value is not as expected",
445 streamName, ((LeafNode) e.getValue()).getValue());
447 case RestconfMappingNodeConstants.DESCRIPTION :
448 assertEquals("Stream description value is not as expected",
449 RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
451 case RestconfMappingNodeConstants.REPLAY_SUPPORT :
452 assertEquals("Stream replay support value is not as expected",
453 RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
455 case RestconfMappingNodeConstants.REPLAY_LOG :
456 assertEquals("Stream replay log value is not as expected",
457 RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
459 case RestconfMappingNodeConstants.EVENTS :
460 assertEquals("Stream events value is not as expected",
461 RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
464 notAllowedKey = true;
469 assertFalse("Not allowed key in list stream found", notAllowedKey);
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
478 private Module getTestingRestconfModule(final String s) {
479 return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
480 s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
484 * Updates {@link this#mockStreamList} to NOT contains specified leaf.
485 * @param leaf Leaf to be missing
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);
492 // mock list leaf nodes
493 when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
497 * Updates {@link this#mockStreamList} to contains specified leaf which is not of type {@link LeafSchemaNode}.
498 * @param leaf Leaf to be changes
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);
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);
511 // mock list leaf nodes
512 when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);