2d9d4a33d076fc3edc228d8faab69d76dc2dfa7c
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / restconf / rest / services / impl / RestconfModulesServiceTestUtils.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.rest.services.impl;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.when;
15
16 import com.google.common.collect.ImmutableClassToInstanceMap;
17 import com.google.common.collect.Maps;
18 import java.io.File;
19 import java.net.URI;
20 import java.util.AbstractMap;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
30 import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
31 import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
32 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
33 import org.opendaylight.restconf.Draft15;
34 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
35 import org.opendaylight.restconf.handlers.SchemaContextHandler;
36 import org.opendaylight.restconf.utils.RestconfConstants;
37 import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
46 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
47 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
48 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
49
50 class RestconfModulesServiceTestUtils {
51     static final String MOUNT_POINT = "mount-point-1:cont/" + RestconfConstants.MOUNT + "/";
52     static final String NOT_REGISTERED_MOUNT_POINT = "mount-point-1:listA/" + RestconfConstants.MOUNT + "/";
53
54     static final String TEST_MODULE = "module1/2014-01-01";
55     static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
56
57     static final String TEST_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
58             + "module1-behind-mount-point/2014-02-03";
59
60     static final String NOT_EXISTING_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
61             + NOT_EXISTING_MODULE;
62
63     static final String TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT = NOT_REGISTERED_MOUNT_POINT
64             + "module1-behind-mount-point/2014-02-03";
65
66     // allowed leafs of list of modules
67     static final List<String> ALLOWED_KEYWORDS = Arrays.asList(
68             RestconfMappingNodeConstants.NAME, RestconfMappingNodeConstants.REVISION,
69             RestconfMappingNodeConstants.NAMESPACE, RestconfMappingNodeConstants.FEATURE);
70
71     static final String MODULES_PATH = "/modules";
72     static final String MODULES_WITHOUT_RESTCONF_MODULE_PATH = "/modules/modules-without-restconf-module";
73     static final String MOUNT_POINTS_PATH = "/modules/mount-points";
74     static final String MODULES_BEHIND_MOUNT_POINT_PATH = "/modules/modules-behind-mount-point";
75
76     static final String CUSTOM_RESTCONF_MODULES_PATH =
77             "/modules/restconf-module-testing/";
78     static final String CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH =
79             "/modules/restconf-module-testing-mount-point/";
80
81     private RestconfModulesServiceTestUtils() { throw new UnsupportedOperationException("Util class"); }
82
83     /**
84      * Get all expected modules supported by the server
85      * @return <code>Set</code> of expected modules
86      * @throws Exception
87      */
88     static final Set<Module> getExpectedModules() throws Exception {
89         return TestRestconfUtils.loadSchemaContext(MODULES_PATH).getModules();
90     }
91
92     /**
93      * Get all expected modules behind mount point
94      * @return <code>Set</code> of expected modules
95      * @throws Exception
96      */
97     static final Set<Module> getExpectedModulesBehindMountPoint() throws Exception {
98         return TestRestconfUtils.loadSchemaContext(MODULES_BEHIND_MOUNT_POINT_PATH).getModules();
99     }
100
101     /**
102      * Verify if correct modules were loaded into Restconf module by comparison with expected modules.
103      * @param expectedModules Expected modules
104      * @param loadedModules Loaded modules into Restconf module
105      */
106     static final void verifyModules(final Set<Module> expectedModules, final Set<TestModule> loadedModules) {
107         final Set<TestModule> expectedModulesTransformed = new HashSet<>();
108         expectedModules.forEach((x) -> expectedModulesTransformed.add(
109                 new TestModule(x.getName(), x.getNamespace(), x.getRevision())));
110         assertEquals("Loaded modules are not as expected", expectedModulesTransformed, loadedModules);
111     }
112
113     /**
114      * Verify id correct module was loaded into Restconf module by comparison of loaded and expected values of name,
115      * namespace, revision and features.
116      * @param expectedName Expected name
117      * @param expectedNamespace Expected namespace
118      * @param expectedRevision Expected revision
119      * @param expectedFeatures Expected features
120      * @param loadedModuleEntries Loaded values
121      */
122     static final void verifyModule(final String expectedName, final String expectedNamespace,
123                              final String expectedRevision, final Set<Object> expectedFeatures,
124                              final Iterator loadedModuleEntries) {
125         while (loadedModuleEntries.hasNext()) {
126             final Entry e = ((AbstractMap.SimpleImmutableEntry) loadedModuleEntries.next());
127             final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
128
129             assertTrue("Not allowed keyword", ALLOWED_KEYWORDS.contains(key));
130
131             switch (key) {
132                 case RestconfMappingNodeConstants.NAME:
133                     assertEquals("Not correct module was found",
134                             expectedName, ((LeafNode) e.getValue()).getValue());
135                     break;
136                 case RestconfMappingNodeConstants.NAMESPACE:
137                     assertEquals("Not correct module was found",
138                             expectedNamespace, ((LeafNode) e.getValue()).getValue());
139                     break;
140                 case RestconfMappingNodeConstants.REVISION:
141                     assertEquals("Not correct module was found",
142                             expectedRevision, ((LeafNode) e.getValue()).getValue());
143                 break;
144                 case RestconfMappingNodeConstants.FEATURE:
145                     assertEquals("Not correct module was found",
146                             expectedFeatures, ((LeafSetNode) e.getValue()).getValue());
147                     break;
148             }
149         }
150     }
151
152     /**
153      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
154      * module and testing modules supported by the server.
155      * @return <code>RestconfModulesServiceImpl</code>
156      * @throws Exception
157      */
158     static final RestconfModulesServiceImpl setupNormal() throws Exception {
159         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
160         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(MODULES_PATH));
161         return new RestconfModulesServiceImpl(schemaContextHandler, null);
162     }
163
164     /**
165      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
166      * module and testing modules behind mount point.
167      * @return <code>RestconfModulesServiceImpl</code>
168      * @throws Exception
169      */
170     static final RestconfModulesServiceImpl setupNormalMountPoint() throws Exception {
171         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
172
173         final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
174         yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
175         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
176
177         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
178         when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
179
180         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
181     }
182
183     /**
184      * Mock <code>SchemaContext</code> to load custom (modified) Restconf module.
185      * @param restconfModuleName Path of custom Restconf module
186      * @return <code>RestconfModulesServiceImpl</code>
187      * @throws Exception
188      */
189     static final RestconfModulesServiceImpl setupCustomRestconfModule(final String restconfModuleName)
190             throws Exception {
191         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
192         final SchemaContext schemaContext = mock(SchemaContext.class);
193         when(schemaContextHandler.get()).thenReturn(schemaContext);
194
195         when(schemaContext.findModuleByNamespaceAndRevision(any(URI.class), any(Date.class))).thenAnswer(invocation -> {
196             final Object[] args = invocation.getArguments();
197             if (args[0] == Draft15.RestconfModule.IETF_RESTCONF_QNAME.getNamespace()
198                     && args[1] == Draft15.RestconfModule.IETF_RESTCONF_QNAME.getRevision()) {
199                 return parseCustomRestconfSource(restconfModuleName).findModuleByName(
200                         restconfModuleName, (Date) args[1]);
201             } else {
202                 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByNamespaceAndRevision(
203                         (URI) args[0], (Date) args[1]);
204             }
205         });
206
207         when(schemaContext.findModuleByName(any(String.class), any(Date.class))).thenAnswer(invocation -> {
208             final Object[] args = invocation.getArguments();
209             return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByName(
210                     (String) args[0], (Date) args[1]);
211         });
212
213         return new RestconfModulesServiceImpl(schemaContextHandler, null);
214     }
215
216     /**
217      * Mock <code>SchemaContext</code> to load custom (modified) Restconf module and prepare mount point.
218      * @param restconfModuleName
219      * @return <code>RestconfModulesServiceImpl</code>
220      * @throws Exception
221      */
222     static final RestconfModulesServiceImpl setupCustomRestconfModuleMountPoint(
223             final String restconfModuleName)throws Exception {
224         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
225         when(schemaContextHandler.get()).thenReturn(
226                 parseCustomRestconfSourceMountPoint(restconfModuleName));
227
228         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
229         final DOMMountPointService mountPointService = getMountPointService();
230         when(mountPointServiceHandler.get()).thenReturn(mountPointService);
231
232         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
233     }
234
235     /**
236      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module.
237      * @return <code>RestconfModulesServiceImpl</code>
238      * @throws Exception
239      */
240     static final RestconfModulesServiceImpl setupMissingRestconfModule() throws Exception {
241         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
242         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
243                 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
244
245         return new RestconfModulesServiceImpl(schemaContextHandler, null);
246     }
247
248     /**
249      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module and
250      * mount point.
251      * @return <code>RestconfModulesServiceImpl</code>
252      * @throws Exception
253      */
254     static final RestconfModulesServiceImpl setupMissingRestconfModuleMountPoint() throws Exception {
255         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
256         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
257                 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
258
259         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
260         when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
261
262         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
263     }
264
265     /**
266      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> with testing modules and mount
267      * points but <code>DOMMountPointServiceHandler</code> will contain <code>null</code> reference to
268      * <code>DOMMountPointService</code>.
269      * @return <code>RestconfModulesServiceImpl</code>
270      * @throws Exception
271      */
272     static final RestconfModulesServiceImpl setupNullMountPointService() throws Exception {
273         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
274
275         final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
276         yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
277
278         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
279
280         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
281         when(mountPointServiceHandler.get()).thenReturn(null);
282
283         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
284     }
285
286     /**
287      * Create <code>DOMMountPointService</code> with one registered <code>SimpleDOMMountPoint</code>.
288      * @return <code>DOMMountPointService</code>
289      * @throws Exception
290      */
291     private static DOMMountPointService getMountPointService() throws Exception {
292         final DOMMountPointService mountPointService = new DOMMountPointServiceImpl();
293         ((DOMMountPointServiceImpl) mountPointService).registerMountPoint(
294                 SimpleDOMMountPoint.create(
295                         YangInstanceIdentifier.builder().node(
296                                 QName.create("mount:point:1", "2016-01-01", "cont")).build(),
297                         ImmutableClassToInstanceMap.copyOf(Maps.newHashMap()),
298                         TestRestconfUtils.loadSchemaContext(
299                                 MODULES_BEHIND_MOUNT_POINT_PATH)));
300
301         return mountPointService;
302     }
303
304     /**
305      * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
306      * <code>restconfName</code> its dependencies and one testing module.
307      * @param restconfName File name of custom Restconf module
308      * @return <code>SchemaContext</code> containing custom Restconf module
309      */
310     private static SchemaContext parseCustomRestconfSource(final String restconfName) throws Exception {
311         final String restconf = TestRestconfUtils.class.getResource(
312                 CUSTOM_RESTCONF_MODULES_PATH + restconfName + ".yang").getPath();
313         final String yangTypes = TestRestconfUtils.class.getResource(
314                 CUSTOM_RESTCONF_MODULES_PATH + "ietf-yang-types.yang").getPath();
315         final String inetTypes = TestRestconfUtils.class.getResource(
316                 CUSTOM_RESTCONF_MODULES_PATH + "ietf-inet-types.yang").getPath();
317         final String testModule = TestRestconfUtils.class.getResource(
318                 MODULES_PATH + "/module1.yang").getPath();
319
320         final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
321         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
322         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
323         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
324         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(testModule), testModule)));
325
326         return reactor.buildEffective();
327     }
328
329     /**
330      * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
331      * <code>restconfName</code> its dependencies and one mount point.
332      * @param restconfName File name of custom Restconf module
333      * @return <code>SchemaContext</code> containing custom Restconf module with one mount point
334      */
335     private static SchemaContext parseCustomRestconfSourceMountPoint(String restconfName) throws Exception {
336         final String restconf = TestRestconfUtils.class.getResource(
337                 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH
338                         + restconfName + "/" + restconfName + ".yang").getPath();
339         final String yangTypes = TestRestconfUtils.class.getResource(
340                 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-yang-types.yang").getPath();
341         final String inetTypes = TestRestconfUtils.class.getResource(
342                 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-inet-types.yang").getPath();
343         final String mountPoint = TestRestconfUtils.class.getResource(
344                 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "mount-point-1.yang").getPath();
345
346         final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
347         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
348         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
349         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
350         reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(mountPoint), mountPoint)));
351
352         return reactor.buildEffective();
353     }
354
355
356     /**
357      * Module representation containing name, namespace and revision for easier comparison of modules.
358      */
359     static final class TestModule {
360         private String name;
361         private String namespace;
362         private String revision;
363
364         TestModule() {}
365
366         TestModule(String name, URI namespace, Date revision) {
367             this.name = name;
368             this.namespace = namespace.toString();
369             this.revision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
370         }
371
372         String getName() {
373             return name;
374         }
375
376         void setName(String name) {
377             this.name = name;
378         }
379
380         String getNamespace() {
381             return namespace;
382         }
383
384         void setNamespace(String namespace) {
385             this.namespace = namespace;
386         }
387
388         String getRevision() {
389             return revision;
390         }
391
392         void setRevision(String revision) {
393             this.revision = revision;
394         }
395
396         @Override
397         public boolean equals(Object o) {
398             if (this == o) return true;
399             if (o == null || getClass() != o.getClass()) return false;
400
401             TestModule that = (TestModule) o;
402
403             if (name != null ? !name.equals(that.name) : that.name != null) return false;
404             if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
405             return revision != null ? revision.equals(that.revision) : that.revision == null;
406
407         }
408
409         @Override
410         public int hashCode() {
411             int result = name != null ? name.hashCode() : 0;
412             result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
413             result = 31 * result + (revision != null ? revision.hashCode() : 0);
414             return result;
415         }
416     }
417 }