Bug 5526 - Testing - RestconfModulesService
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / restconf / rest / services / impl / RestconfStreamsServiceTest.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
9 package org.opendaylight.restconf.rest.services.impl;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Mockito.when;
17 import static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.EMPTY;
18 import com.google.common.collect.Iterables;
19 import com.google.common.collect.Lists;
20 import java.util.AbstractMap;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import org.junit.AfterClass;
28 import org.junit.Before;
29 import org.junit.BeforeClass;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.rules.ExpectedException;
33 import org.mockito.Mock;
34 import org.mockito.MockitoAnnotations;
35 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
36 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
37 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
38 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
39 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
40 import org.opendaylight.restconf.Draft11;
41 import org.opendaylight.restconf.handlers.SchemaContextHandler;
42 import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
43 import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
44 import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants;
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.impl.schema.nodes.AbstractImmutableDataContainerAttrNode;
51 import org.opendaylight.yangtools.yang.model.api.Module;
52 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
53
54 /**
55  * Unit tests for {@link RestconfStreamsServiceImpl}
56  */
57 public class RestconfStreamsServiceTest {
58     private static final List<String> expectedStreams = Arrays.asList(new String[] {"stream-1", "stream-2", "stream-3"});
59
60     @Rule public ExpectedException thrown = ExpectedException.none();
61
62     @Mock private SchemaContextHandler contextHandler;
63     @Mock private SchemaContext mockSchemaContext;
64
65     // service under test
66     private RestconfStreamsService streamsService;
67
68     // schema context with testing Restconf modules
69     private SchemaContext schemaContext;
70
71     @Before
72     public void setup() throws Exception {
73         MockitoAnnotations.initMocks(this);
74
75         this.schemaContext = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing");
76         this.streamsService = new RestconfStreamsServiceImpl(this.contextHandler);
77     }
78
79     @BeforeClass
80     public static void setupTestStreams() {
81         // clean
82         Notificator.removeAllListeners();
83
84         // put test streams
85         Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0));
86         Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1));
87         Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2));
88     }
89
90     @AfterClass
91     public static void removeTestStreams() {
92         Notificator.removeAllListeners();
93     }
94
95     /**
96      * Test of successful initialization of streams service.
97      */
98     @Test
99     public void restconfStreamsServiceImplInitTest() {
100         assertNotNull("Streams service should be initialized and not null", this.streamsService);
101     }
102
103     /**
104      * Positive test to get all available streams supported by the server. Loaded streams are compared to expected
105      * streams.
106      */
107     @Test
108     public void getAvailableStreamsTest() throws Exception {
109         // prepare conditions - get correct Restconf module
110         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
111         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
112                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
113                 .thenReturn(getTestingRestconfModule("ietf-restconf"));
114
115         // make test
116         final NormalizedNodeContext nodeContext = this.streamsService.getAvailableStreams(null);
117
118         // verify loaded streams
119         assertNotNull("Normalized node context should not be null", nodeContext);
120         verifyStreams(((ContainerNode) nodeContext.getData()).getValue());
121     }
122
123     /**
124      * Try to get all available streams supported by the server when current <code>SchemaContext</code> is
125      * <code>null</code> expecting <code>NullPointerException</code>.
126      */
127     @Test
128     public void getAvailableStreamsNullSchemaContextNegativeTest() {
129         // prepare conditions - returned SchemaContext is null
130         when(this.contextHandler.get()).thenReturn(null);
131
132         // make test
133         this.thrown.expect(NullPointerException.class);
134         this.streamsService.getAvailableStreams(null);
135     }
136
137     /**
138      * Try to get all available streams supported by the server when Restconf module is missing in
139      * <code>SchemaContext</code> expecting <code>NullPointerException</code>.
140      */
141     @Test
142     public void getAvailableStreamsMissingRestconfModuleNegativeTest() {
143         // prepare conditions - get null Restconf module
144         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
145         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
146                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
147
148         // make test
149         this.thrown.expect(NullPointerException.class);
150         this.streamsService.getAvailableStreams(null);
151     }
152
153     /**
154      * Try to get all available streams supported by the server when Restconf module does not contain list stream
155      * catching <code>RestconfDocumentedException</code>. Error type, error tag and error status code are validated
156      * against expected values.
157      */
158     @Test
159     public void getAvailableStreamsMissingListStreamNegativeTest() {
160         // prepare conditions - get Restconf module with missing list stream
161         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
162         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
163                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
164                 .thenReturn(getTestingRestconfModule("restconf-module-with-missing-list-stream"));
165
166         // make test and verify
167         try {
168             this.streamsService.getAvailableStreams(null);
169             fail("Test is expected to fail due to missing list stream");
170         } catch (final RestconfDocumentedException e) {
171             assertEquals("Error type is not correct",
172                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
173             assertEquals("Error tag is not correct",
174                     RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
175             assertEquals("Error status code is not correct",
176                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
177         }
178     }
179
180     /**
181      * Try to get all available streams supported by the server when Restconf module does not contain container streams
182      * catching <code>RestconfDocumentedException</code>. Error type, error tag and error status code are validated
183      * against expected values.
184      */
185     @Test
186     public void getAvailableStreamsMissingContainerStreamsNegativeTest() {
187         // prepare conditions - get Restconf module with missing container streams
188         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
189         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
190                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
191                 .thenReturn(getTestingRestconfModule("restconf-module-with-missing-container-streams"));
192
193         // make test and verify
194         try {
195             this.streamsService.getAvailableStreams(null);
196             fail("Test is expected to fail due to missing container streams");
197         } catch (final RestconfDocumentedException e) {
198             assertEquals("Error type is not correct",
199                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
200             assertEquals("Error tag is not correct",
201                     RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
202             assertEquals("Error status code is not correct",
203                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
204         }
205     }
206
207     /**
208      * Try to get all available streams supported by the server when Restconf module contains node with name 'stream'
209      * but it is not of type list. Test is expected to fail with <code>IllegalStateException</code>.
210      */
211     @Test
212     public void getAvailableStreamsIllegalListStreamNegativeTest() {
213         // prepare conditions - get Restconf module with illegal list stream
214         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
215         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
216                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
217                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-list-stream"));
218
219         // make test
220         this.thrown.expect(IllegalStateException.class);
221         this.streamsService.getAvailableStreams(null);
222     }
223
224     /**
225      * Try to get all available streams supported by the server when Restconf module contains node with name 'streams'
226      * but it is not of type container. Test is expected to fail with <code>IllegalStateException</code>.
227      */
228     @Test
229     public void getAvailableStreamsIllegalContainerStreamsNegativeTest() {
230         // prepare conditions - get Restconf module with illegal container streams
231         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
232         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
233                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
234                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-container-streams"));
235
236         // make test
237         this.thrown.expect(IllegalStateException.class);
238         this.streamsService.getAvailableStreams(null);
239     }
240
241     /**
242      * Try to get all available streams supported by the server when node 'description' in list stream in Restconf
243      * module is not of type leaf. Test is expected to fail with <code>IllegalStateException</code>.
244      */
245     @Test
246     public void getAvailableStreamsIllegalLeafDescriptionNegativeTest() {
247         // prepare conditions - get Restconf module with illegal leaf description in list stream
248         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
249         when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME
250                 .getNamespace(), Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
251                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-leaf-description"));
252
253         // make test
254         this.thrown.expect(IllegalStateException.class);
255         this.streamsService.getAvailableStreams(null);
256     }
257
258     /**
259      * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
260      * name or by namespace. This method is looking for Restconf test module by its name.
261      * @param s Testing Restconf module name
262      * @return Restconf module
263      */
264     private Module getTestingRestconfModule(final String s) {
265         return this.schemaContext.findModuleByName(s, Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
266     }
267
268     /**
269      * Verify loaded streams
270      * @param streams Streams to be verified
271      */
272     private void verifyStreams(final Collection<DataContainerChild<? extends PathArgument, ?>> streams) {
273         assertNotNull("Collection of streams should not be empty", streams);
274         assertFalse("Collection of streams should not be empty", Iterables.isEmpty(streams));
275         final Iterator<DataContainerChild<? extends PathArgument, ?>> iterator = streams.iterator();
276
277         final List<String> loadedStreams = new ArrayList<>();
278         for (final Object stream : (Collection<?>) iterator.next().getValue()) {
279             final Iterator mapEntries = ((AbstractImmutableDataContainerAttrNode) stream)
280                     .getChildren().entrySet().iterator();
281
282             final List<String> allowedKeys = Lists.newArrayList(
283                     RestconfMappingNodeConstants.NAME,
284                     RestconfMappingNodeConstants.DESCRIPTION,
285                     RestconfMappingNodeConstants.REPLAY_SUPPORT,
286                     RestconfMappingNodeConstants.REPLAY_LOG,
287                     RestconfMappingNodeConstants.EVENTS);
288
289             while (mapEntries.hasNext()) {
290                 final Map.Entry e = ((AbstractMap.SimpleImmutableEntry) mapEntries.next());
291                 final String key = ((NodeIdentifier) e.getKey()).getNodeType().getLocalName();
292
293                 assertTrue("Not allowed key", allowedKeys.contains(key));
294
295                 switch (key) {
296                     case RestconfMappingNodeConstants.NAME :
297                         loadedStreams.add((String) ((LeafNode) e.getValue()).getValue());
298                         break;
299                     case RestconfMappingNodeConstants.DESCRIPTION :
300                         assertEquals("Stream description value is not as expected",
301                                 RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
302                         break;
303                     case RestconfMappingNodeConstants.REPLAY_SUPPORT :
304                         assertEquals("Stream replay support value is not as expected",
305                                 RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
306                         break;
307                     case RestconfMappingNodeConstants.REPLAY_LOG :
308                         assertEquals("Stream replay log value is not as expected",
309                                 RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
310                         break;
311                     case RestconfMappingNodeConstants.EVENTS :
312                         assertEquals("Stream events value is not as expected",
313                                 RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
314                         break;
315                 }
316             }
317         }
318
319         // sort and compare
320         loadedStreams.sort((s1, s2) -> s1.compareTo(s2));
321         assertEquals("Returned streams are not as expected", expectedStreams, loadedStreams);
322     }
323 }