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