Fix channel inactive handling for SSH transport
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / jaxrs / RestconfSchemaServiceTest.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 package org.opendaylight.restconf.nb.jaxrs;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertSame;
12 import static org.mockito.Mockito.doReturn;
13 import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertEntity;
14 import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertError;
15
16 import com.google.common.io.CharStreams;
17 import com.google.common.util.concurrent.Futures;
18 import java.io.Reader;
19 import org.junit.Before;
20 import org.junit.Test;
21 import org.junit.runner.RunWith;
22 import org.mockito.Mock;
23 import org.mockito.junit.MockitoJUnitRunner;
24 import org.opendaylight.mdsal.dom.api.DOMActionService;
25 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
26 import org.opendaylight.mdsal.dom.api.DOMRpcService;
27 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
28 import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
29 import org.opendaylight.mdsal.dom.broker.DOMMountPointServiceImpl;
30 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
31 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
32 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
33 import org.opendaylight.restconf.server.mdsal.DOMSourceResolver;
34 import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
35 import org.opendaylight.yangtools.yang.common.ErrorTag;
36 import org.opendaylight.yangtools.yang.common.ErrorType;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.common.Revision;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
41 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
42 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
43 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
44
45 /**
46  * Unit tests for {@code RestconfSchemaService}.
47  */
48 @RunWith(MockitoJUnitRunner.StrictStubs.class)
49 public class RestconfSchemaServiceTest {
50     private static final String MOUNT_POINT = "mount-point-1:cont/yang-ext:mount/";
51     private static final String NULL_MOUNT_POINT = "mount-point-2:cont/yang-ext:mount/";
52     private static final String NOT_EXISTING_MOUNT_POINT = "mount-point-3:cont/yang-ext:mount/";
53
54     private static final String TEST_MODULE = "module1/2014-01-01";
55     private static final String TEST_MODULE_BEHIND_MOUNT_POINT = "module1-behind-mount-point/2014-02-03";
56     private static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
57
58     // schema context with modules
59     private static final EffectiveModelContext SCHEMA_CONTEXT =
60         YangParserTestUtils.parseYangResourceDirectory("/modules");
61     // schema context with modules behind mount point
62     private static final EffectiveModelContext SCHEMA_CONTEXT_BEHIND_MOUNT_POINT =
63         YangParserTestUtils.parseYangResourceDirectory("/modules/modules-behind-mount-point");
64     // schema context with mount points
65     private static final EffectiveModelContext SCHEMA_CONTEXT_WITH_MOUNT_POINTS =
66         YangParserTestUtils.parseYangResourceDirectory("/modules/mount-points");
67
68     // handlers
69     @Mock
70     private DOMSchemaService schemaService;
71     @Mock
72     private DOMYangTextSourceProvider sourceProvider;
73     @Mock
74     private DOMDataBroker dataBroker;
75     @Mock
76     private DOMActionService actionService;
77     @Mock
78     private DOMRpcService rpcService;
79     @Mock
80     private YangTextSchemaSource yangSource;
81     @Mock
82     private Reader yangReader;
83
84     // service under test
85     private JaxRsRestconf restconf;
86
87     @Before
88     public void setup() throws Exception {
89         final var mountPointService = new DOMMountPointServiceImpl();
90         // create and register mount points
91         mountPointService
92                 .createMountPoint(YangInstanceIdentifier.of(QName.create("mount:point:1", "2016-01-01", "cont")))
93                 .addService(DOMSchemaService.class, FixedDOMSchemaService.of(SCHEMA_CONTEXT_BEHIND_MOUNT_POINT))
94                 .register();
95         mountPointService
96                 .createMountPoint(YangInstanceIdentifier.of(QName.create("mount:point:2", "2016-01-01", "cont")))
97                 .register();
98
99         restconf = new JaxRsRestconf(new MdsalRestconfServer(
100             () -> DatabindContext.ofModel(schemaService.getGlobalContext(), new DOMSourceResolver(sourceProvider)),
101             dataBroker, rpcService, actionService, mountPointService));
102     }
103
104     /**
105      * Get schema with identifier of existing module and check if correct module was found.
106      */
107     @Test
108     public void getSchemaTest() throws Exception {
109         // prepare conditions - return not-mount point schema context
110         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
111         doReturn(Futures.immediateFuture(yangSource)).when(sourceProvider)
112             .getSource(new SourceIdentifier("module1", Revision.of("2014-01-01")));
113         doReturn(yangReader).when(yangSource).openStream();
114
115         assertSame(yangReader, assertEntity(Reader.class, 200, ar -> restconf.modulesYangGET(TEST_MODULE, ar)));
116     }
117
118     /**
119      * Get schema with identifier of not-existing module. Trying to create <code>SchemaExportContext</code> with
120      * not-existing module should result in error.
121      */
122     @Test
123     public void getSchemaForNotExistingModuleTest() {
124         // prepare conditions - return not-mount point schema context
125         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
126
127         final var error = assertError(ar -> restconf.modulesYinGET(NOT_EXISTING_MODULE, ar));
128         assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
129         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
130         assertEquals(ErrorType.APPLICATION, error.getErrorType());
131     }
132
133     /**
134      * Get schema with identifier of existing module behind mount point and check if correct module was found.
135      */
136     @Test
137     public void getSchemaMountPointTest() throws Exception {
138         // prepare conditions - return schema context with mount points
139         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
140
141         final var reader = assertEntity(Reader.class, 200,
142             ar -> restconf.modulesYangGET(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
143         assertEquals("""
144             module module1-behind-mount-point {
145               namespace module:1:behind:mount:point;
146               prefix mod1bemopo;
147               revision 2014-02-03;
148               rpc rpc-behind-module1;
149             }
150             """, CharStreams.toString(reader));
151     }
152
153     /**
154      * Get schema with identifier of not-existing module behind mount point. Trying to create
155      * <code>SchemaExportContext</code> with not-existing module behind mount point should result in error.
156      */
157     @Test
158     public void getSchemaForNotExistingModuleMountPointTest() {
159         // prepare conditions - return schema context with mount points
160         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
161
162         final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + NOT_EXISTING_MODULE, ar));
163         assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
164         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
165         assertEquals(ErrorType.APPLICATION, error.getErrorType());
166     }
167
168     /**
169      * Try to get schema with <code>null</code> <code>SchemaContext</code> behind mount point when using
170      * <code>NULL_MOUNT_POINT</code>. Test is expected to fail with <code>NullPointerException</code>.
171      */
172     @Test
173     public void getSchemaNullSchemaContextBehindMountPointTest() {
174         // prepare conditions - return correct schema context for mount points (this is not null)
175         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
176
177         // make test - call service on mount point with null schema context
178         // NULL_MOUNT_POINT contains null schema context
179         final var error = assertError(
180             ar -> restconf.modulesYangGET(NULL_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
181         assertEquals("Mount point mount-point-2:cont does not expose DOMSchemaService", error.getErrorMessage());
182         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
183         assertEquals(ErrorTags.RESOURCE_DENIED_TRANSPORT, error.getErrorTag());
184     }
185
186     /**
187      * Try to get schema with empty (not valid) identifier catching <code>RestconfDocumentedException</code>. Error
188      * type, error tag and error status code are compared to expected values.
189      */
190     @Test
191     public void getSchemaWithEmptyIdentifierTest() {
192         // prepare conditions - return correct schema context
193         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
194
195         final var error = assertError(ar -> restconf.modulesYangGET("", ar));
196         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
197         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
198         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
199     }
200
201     /**
202      * Try to get schema with empty (not valid) identifier behind mount point catching
203      * <code>RestconfDocumentedException</code>. Error type, error tag and error status code are compared to expected
204      * values.
205      */
206     @Test
207     public void getSchemaWithEmptyIdentifierMountPointTest() {
208         // prepare conditions - return correct schema context with mount points
209         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
210
211         // make test and verify
212         final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "", ar));
213         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
214         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
215         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
216     }
217
218     /**
219      * Try to get schema with not-parsable identifier catching <code>RestconfDocumentedException</code>. Error type,
220      * error tag and error status code are compared to expected values.
221      */
222     @Test
223     public void getSchemaWithNotParsableIdentifierTest() {
224         // prepare conditions - return correct schema context without mount points
225         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
226
227         // make test and verify
228         final var error = assertError(ar -> restconf.modulesYangGET("01_module/2016-01-01", ar));
229         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
230         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
231         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
232     }
233
234     /**
235      * Try to get schema behind mount point with not-parsable identifier catching
236      * <code>RestconfDocumentedException</code>. Error type, error tag and error status code are compared to expected
237      * values.
238      */
239     @Test
240     public void getSchemaWithNotParsableIdentifierMountPointTest() {
241         // prepare conditions - return correct schema context with mount points
242         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
243
244         // make test and verify
245         final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "01_module/2016-01-01", ar));
246         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
247         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
248         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
249     }
250
251     /**
252      * Try to get schema with wrong (not valid) identifier catching <code>RestconfDocumentedException</code>. Error
253      * type, error tag and error status code are compared to expected values.
254      *
255      * <p>
256      * Not valid identifier contains only revision without module name.
257      */
258     @Test
259     public void getSchemaWrongIdentifierTest() {
260         // prepare conditions - return correct schema context without mount points
261         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
262
263         // make test and verify
264         final var error = assertError(ar -> restconf.modulesYangGET("2014-01-01", ar));
265         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
266         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
267         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
268     }
269
270     /**
271      * Try to get schema with wrong (not valid) identifier behind mount point catching
272      * <code>RestconfDocumentedException</code>. Error type, error tag and error status code are compared to expected
273      * values.
274      *
275      * <p>
276      * Not valid identifier contains only revision without module name.
277      */
278     @Test
279     public void getSchemaWrongIdentifierMountPointTest() {
280         // prepare conditions - return correct schema context with mount points
281         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
282
283         // make test and verify
284         final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "2014-01-01", ar));
285         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
286         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
287         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
288     }
289
290     /**
291      * Try to get schema with identifier which does not contain revision catching
292      * <code>RestconfDocumentedException</code>. Error type, error tag and error status code are compared to expected
293      * values.
294      */
295     @Test
296     public void getSchemaWithoutRevisionTest() {
297         // prepare conditions - return correct schema context without mount points
298         doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
299
300         // make test and verify
301         final var error = assertError(ar -> restconf.modulesYangGET("module", ar));
302         assertEquals("Revision date must be supplied.", error.getErrorMessage());
303         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
304         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
305     }
306
307     /**
308      * Try to get schema behind mount point with identifier when does not contain revision catching
309      * <code>RestconfDocumentedException</code>. Error type, error tag and error status code are compared to expected
310      * values.
311      */
312     @Test
313     public void getSchemaWithoutRevisionMountPointTest() {
314         // prepare conditions - return correct schema context with mount points
315         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
316
317         // make test and verify
318         final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "module", ar));
319         assertEquals("Revision date must be supplied.", error.getErrorMessage());
320         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
321         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
322     }
323
324     /**
325      * Negative test when mount point module is not found in current <code>SchemaContext</code> for mount points.
326      * <code>IllegalArgumentException</code> exception is expected.
327      */
328     @Test
329     public void getSchemaContextWithNotExistingMountPointTest() {
330         // prepare conditions - return schema context with mount points
331         doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
332
333         final var error = assertError(
334             ar -> restconf.modulesYangGET(NOT_EXISTING_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
335         assertEquals("Failed to lookup for module with name 'mount-point-3'.", error.getErrorMessage());
336         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
337         assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());
338     }
339 }