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.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
26 import java.util.Map.Entry;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.BeforeClass;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.junit.rules.ExpectedException;
34 import org.mockito.Mock;
35 import org.mockito.MockitoAnnotations;
36 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
37 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
38 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
39 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
40 import org.opendaylight.restconf.Draft18;
41 import org.opendaylight.restconf.Draft18.IetfYangLibrary;
42 import org.opendaylight.restconf.Draft18.MonitoringModule;
43 import org.opendaylight.restconf.Draft18.MonitoringModule.QueryParams;
44 import org.opendaylight.restconf.Draft18.RestconfModule;
45 import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
51 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
53 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
59 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode;
60 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
62 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
63 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.Module;
65 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
68 * Unit tests for {@link RestconfMappingNodeUtil}
70 public class RestconfMappingNodeUtilTest {
72 public ExpectedException thrown = ExpectedException.none();
74 @Mock private ListSchemaNode mockStreamList;
75 @Mock private LeafSchemaNode leafName;
76 @Mock private LeafSchemaNode leafDescription;
77 @Mock private LeafSchemaNode leafReplaySupport;
78 @Mock private LeafSchemaNode leafReplayLog;
79 @Mock private LeafSchemaNode leafEvents;
81 private static Set<Module> modules;
82 private static SchemaContext schemaContext;
83 private static SchemaContext schemaContextCapabilites;
85 private static Set<Module> modulesRest;
87 private Set<DataSchemaNode> allStreamChildNodes;
90 public static void loadTestSchemaContextAndModules() throws Exception {
91 RestconfMappingNodeUtilTest.schemaContext = TestRestconfUtils.loadSchemaContext(
92 "/modules/restconf-module-testing");
93 RestconfMappingNodeUtilTest.schemaContextCapabilites = TestRestconfUtils.loadSchemaContext("/modules");
94 RestconfMappingNodeUtilTest.modules = schemaContextCapabilites.getModules();
95 RestconfMappingNodeUtilTest.modulesRest =
96 TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules();
100 public void setup() throws Exception {
101 MockitoAnnotations.initMocks(this);
103 when(this.leafName.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.NAME));
104 when(this.leafDescription.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.DESCRIPTION));
105 when(this.leafReplaySupport.getQName()).thenReturn(
106 QName.create("", RestconfMappingNodeConstants.REPLAY_SUPPORT));
107 when(this.leafReplayLog.getQName()).thenReturn(QName.create(RestconfMappingNodeConstants.REPLAY_LOG));
108 when(this.leafEvents.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.EVENTS));
110 this.allStreamChildNodes = Sets.newHashSet(
111 this.leafName, this.leafDescription, this.leafReplaySupport, this.leafReplayLog, this.leafEvents);
115 * Test of writing modules into {@link RestconfModule#MODULE_LIST_SCHEMA_NODE} and checking if modules were
119 public void restconfMappingNodeTest() {
120 // write modules into list module in Restconf
121 final Module ietfYangLibMod =
122 schemaContext.findModuleByNamespaceAndRevision(IetfYangLibrary.URI_MODULE, IetfYangLibrary.DATE);
123 final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> modules =
124 RestconfMappingNodeUtil.mapModulesByIetfYangLibraryYang(RestconfMappingNodeUtilTest.modules,
125 ietfYangLibMod, schemaContext, "1");
127 // verify loaded modules
128 verifyLoadedModules((ContainerNode) modules);
132 public void restconfStateCapabilitesTest() {
133 final Module monitoringModule = schemaContextCapabilites
134 .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
135 final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode =
136 RestconfMappingNodeUtil.mapCapabilites(monitoringModule);
137 assertNotNull(normNode);
138 final List<Object> listOfValues = new ArrayList<>();
140 for (final DataContainerChild<? extends PathArgument, ?> child : ((ContainerNode) normNode).getValue()) {
141 if (child.getNodeType().equals(MonitoringModule.CONT_CAPABILITES_QNAME)) {
142 for (final DataContainerChild<? extends PathArgument, ?> dataContainerChild : ((ContainerNode) child)
144 for (final Object entry : ((LeafSetNode) dataContainerChild).getValue()) {
145 listOfValues.add(((LeafSetEntryNode) entry).getValue());
150 Assert.assertTrue(listOfValues.contains(QueryParams.DEPTH));
151 Assert.assertTrue(listOfValues.contains(QueryParams.FIELDS));
152 Assert.assertTrue(listOfValues.contains(QueryParams.FILTER));
153 Assert.assertTrue(listOfValues.contains(QueryParams.REPLAY));
154 Assert.assertTrue(listOfValues.contains(QueryParams.WITH_DEFAULTS));
158 * Positive test of writing one stream to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} and checking if stream
159 * was correctly written.
162 public void toStreamEntryNodeTest() {
164 final String stream1 = "stream-1";
166 // get list stream node from Restconf module
167 final ListSchemaNode listStream = (ListSchemaNode) RestconfSchemaUtil.getRestconfSchemaNode(
168 getTestingRestconfModule("ietf-restconf"), MonitoringModule.STREAM_LIST_SCHEMA_NODE);
170 // write stream to list stream node
171 final MapEntryNode mapEntryNode = RestconfMappingNodeUtil.toStreamEntryNode(stream1, listStream);
174 verifyStream(stream1, mapEntryNode);
178 * Try to map streams when {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} is <code>null</code>.
179 * Test is expected to fail catching <code>IllegalStateException</code>.
182 public void toStreamEntryNodeNullListStreamNegativeTest() {
183 this.thrown.expect(IllegalStateException.class);
184 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", null);
188 * Test trying to map streams to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which is not of type list.
189 * Test is expected to fail with <code>IllegalStateException</code>.
192 public void toStreamEntryNodeIllegalListStreamNegativeTest() {
193 this.thrown.expect(IllegalStateException.class);
194 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mock(LeafSchemaNode.class));
198 * Test case with target {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which does not contain any child nodes.
199 * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
200 * compared to expected values.
203 public void toStreamEntryNodeSchemaNodeWithoutChildsNegativeTest() {
204 final ListSchemaNode mockListNode = mock(ListSchemaNode.class);
205 when(mockListNode.getChildNodes()).thenReturn(Collections.EMPTY_SET);
208 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mockListNode);
209 fail("Test should fail due to no child nodes in"
210 + MonitoringModule.STREAM_LIST_SCHEMA_NODE
212 } catch (final RestconfDocumentedException e) {
213 assertEquals("Error type is not correct",
214 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
215 assertEquals("Error tag is not correct",
216 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
217 assertEquals("Error status code is not correct",
218 404, e.getErrors().get(0).getErrorTag().getStatusCode());
223 * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#NAME}.
224 * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
225 * compared to expected values.
228 public void toStreamEntryNodeMissingStreamNameNegativeTest() {
229 prepareMockListWithMissingLeaf(this.leafName);
232 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
233 fail("Test should fail due to missing "
234 + RestconfMappingNodeConstants.NAME
235 + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
236 } catch (final RestconfDocumentedException e) {
237 assertEquals("Error type is not correct",
238 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
239 assertEquals("Error tag is not correct",
240 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
241 assertEquals("Error status code is not correct",
242 404, e.getErrors().get(0).getErrorTag().getStatusCode());
247 * Test case when target list stream does not contain child with name
248 * {@link RestconfMappingNodeConstants#DESCRIPTION}. Test is catching <code>RestconfDocumentedException</code> and
249 * checking error type and error tag.
252 public void toStreamEntryNodeMissingStreamDescriptionNegativeTest() {
253 prepareMockListWithMissingLeaf(this.leafDescription);
256 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
257 fail("Test should fail due to missing "
258 + RestconfMappingNodeConstants.DESCRIPTION
259 + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
260 } catch (final RestconfDocumentedException e) {
261 assertEquals("Error type is not correct",
262 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
263 assertEquals("Error tag is not correct",
264 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
265 assertEquals("Error status code is not correct",
266 404, e.getErrors().get(0).getErrorTag().getStatusCode());
271 * Test case when target list stream does not contain child with name
272 * {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}. Test is catching <code>RestconfDocumentedException</code>
273 * and checking error type and error tag.
276 public void toStreamEntryNodeMissingStreamReplaySupportNegativeTest() {
277 prepareMockListWithMissingLeaf(this.leafReplaySupport);
280 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
281 fail("Test should fail due to missing "
282 + RestconfMappingNodeConstants.REPLAY_SUPPORT
283 + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
284 } catch (final RestconfDocumentedException e) {
285 assertEquals("Error type is not correct",
286 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
287 assertEquals("Error tag is not correct",
288 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
289 assertEquals("Error status code is not correct",
290 404, e.getErrors().get(0).getErrorTag().getStatusCode());
295 * Test case when target list stream does not contain child with name
296 * {@link RestconfMappingNodeConstants#REPLAY_LOG}. Test is catching <code>RestconfDocumentedException</code> and
297 * checking error type and error tag.
300 public void toStreamEntryNodeMissingStreamReplayLogNegativeTest() {
301 prepareMockListWithMissingLeaf(this.leafReplayLog);
304 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
305 fail("Test should fail due to missing "
306 + RestconfMappingNodeConstants.REPLAY_LOG
307 + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
308 } catch (final RestconfDocumentedException e) {
309 assertEquals("Error type is not correct",
310 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
311 assertEquals("Error tag is not correct",
312 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
313 assertEquals("Error status code is not correct",
314 404, e.getErrors().get(0).getErrorTag().getStatusCode());
319 * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#EVENTS}.
320 * Test is catching <code>RestconfDocumentedException</code> and checking error type, error tag and error status
324 public void toStreamEntryNodeMissingStreamEventsNegativeTest() {
325 prepareMockListWithMissingLeaf(this.leafEvents);
328 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
329 fail("Test should fail due to missing "
330 + RestconfMappingNodeConstants.EVENTS
331 + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
332 } catch (final RestconfDocumentedException e) {
333 assertEquals("Error type is not correct",
334 ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
335 assertEquals("Error tag is not correct",
336 ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
337 assertEquals("Error status code is not correct",
338 404, e.getErrors().get(0).getErrorTag().getStatusCode());
343 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#NAME}. Test is
344 * expecting <code>IllegalStateException</code>.
347 public void toStreamEntryNodeStreamNameNegativeTest() {
348 prepareMockListWithIllegalLeaf(this.leafName);
350 this.thrown.expect(IllegalStateException.class);
351 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
355 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#DESCRIPTION}.
356 * Test is expecting <code>IllegalStateException</code>.
359 public void toStreamEntryNodeStreamDescriptionNegativeTest() {
360 prepareMockListWithIllegalLeaf(this.leafDescription);
362 this.thrown.expect(IllegalStateException.class);
363 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
367 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}.
368 * Test is expecting <code>IllegalStateException</code>.
371 public void toStreamEntryNodeStreamReplaySupportNegativeTest() {
372 prepareMockListWithIllegalLeaf(this.leafReplaySupport);
374 this.thrown.expect(IllegalStateException.class);
375 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
379 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_LOG}.
380 * Test is expecting <code>IllegalStateException</code>.
383 public void toStreamEntryNodeStreamReplayLogNegativeTest() {
384 prepareMockListWithIllegalLeaf(this.leafReplayLog);
386 this.thrown.expect(IllegalStateException.class);
387 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
391 * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#EVENTS}. Test is
392 * expecting <code>IllegalStateException</code>.
395 public void toStreamEntryNodeStreamEventsNegativeTest() {
396 prepareMockListWithIllegalLeaf(this.leafEvents);
398 this.thrown.expect(IllegalStateException.class);
399 RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
403 * Verify loaded modules
405 * @param containerNode
408 private void verifyLoadedModules(final ContainerNode containerNode) {
410 final Map<String, String> loadedModules = new HashMap<>();
412 for (final DataContainerChild<? extends PathArgument, ?> child : containerNode.getValue()) {
413 if (child instanceof LeafNode) {
414 assertEquals(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME, ((LeafNode) child).getNodeType());
416 if (child instanceof MapNode) {
417 assertEquals(IetfYangLibrary.MODULE_QNAME_LIST, ((MapNode) child).getNodeType());
418 for (final MapEntryNode mapEntryNode : ((MapNode) child).getValue()) {
420 String revision = "";
421 for (final DataContainerChild<? extends PathArgument, ?> dataContainerChild : mapEntryNode
423 switch (dataContainerChild.getNodeType().getLocalName()) {
424 case IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF:
425 name = String.valueOf(((LeafNode) dataContainerChild).getValue());
427 case IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF:
428 revision = String.valueOf(((LeafNode) dataContainerChild).getValue());
432 loadedModules.put(name, revision);
437 verifyLoadedModules(RestconfMappingNodeUtilTest.modulesRest, loadedModules);
441 * Verify if correct modules were loaded into Restconf module by comparison with modules from
442 * <code>SchemaContext</code>.
443 * @param expectedModules Modules from <code>SchemaContext</code>
444 * @param loadedModules Loaded modules into Restconf module
446 private final void verifyLoadedModules(final Set<Module> expectedModules,
447 final Map<String, String> loadedModules) {
448 assertEquals("Number of loaded modules is not as expected", expectedModules.size(), loadedModules.size());
449 for (final Module m : expectedModules) {
450 final String name = m.getName();
452 final String revision = loadedModules.get(name);
453 assertNotNull("Expected module not found", revision);
454 assertEquals("Not correct revision of loaded module",
455 SimpleDateFormatUtil.getRevisionFormat().format(m.getRevision()), revision);
457 loadedModules.remove(name);
462 * Verify if a stream was correctly written into {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} node in Restconf
464 * @param streamName Expected stream name
465 * @param streamNode Writetn strem node from Restconf module
467 private final void verifyStream(final String streamName, final MapEntryNode streamNode) {
468 assertNotNull("Stream node can not be null", streamNode);
469 final Iterator entries = ((AbstractImmutableDataContainerAttrNode) streamNode)
470 .getChildren().entrySet().iterator();
471 boolean notAllowedKey = false;
473 while (entries.hasNext()) {
474 final Entry e = ((AbstractMap.SimpleImmutableEntry) entries.next());
475 final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
478 case RestconfMappingNodeConstants.NAME :
479 assertEquals("Stream name value is not as expected",
480 streamName, ((LeafNode) e.getValue()).getValue());
482 case RestconfMappingNodeConstants.DESCRIPTION :
483 assertEquals("Stream description value is not as expected",
484 RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
486 case RestconfMappingNodeConstants.REPLAY_SUPPORT :
487 assertEquals("Stream replay support value is not as expected",
488 RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
490 case RestconfMappingNodeConstants.REPLAY_LOG :
491 assertEquals("Stream replay log value is not as expected",
492 RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
494 case RestconfMappingNodeConstants.EVENTS :
495 assertEquals("Stream events value is not as expected",
496 RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
499 notAllowedKey = true;
504 assertFalse("Not allowed key in list stream found", notAllowedKey);
508 * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
509 * name or by namespace. This method is looking for Restconf test module by its name.
510 * @param s Testing Restconf module name
511 * @return Restconf module
513 private Module getTestingRestconfModule(final String s) {
514 return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
515 s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
519 * Updates {@link this#mockStreamList} to NOT contains specified leaf.
520 * @param leaf Leaf to be missing
522 private void prepareMockListWithMissingLeaf(final LeafSchemaNode leaf) {
523 // prepare set of leaf without selected leaf
524 final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
525 childLeafs.remove(leaf);
527 // mock list leaf nodes
528 when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
532 * Updates {@link this#mockStreamList} to contains specified leaf which is not of type {@link LeafSchemaNode}.
533 * @param leaf Leaf to be changes
535 private void prepareMockListWithIllegalLeaf(final LeafSchemaNode leaf) {
536 // prepare set of leaf without selected leaf
537 final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
538 childLeafs.remove(leaf);
540 // add leaf-list with the same local name as removed leaf
541 final String localName = leaf.getQName().getLocalName();
542 final LeafListSchemaNode mockLeafList = mock(LeafListSchemaNode.class);
543 when(mockLeafList.getQName()).thenReturn(QName.create("", localName));
544 childLeafs.add(mockLeafList);
546 // mock list leaf nodes
547 when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);