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