2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.restconf.impl.test;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.fail;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Matchers.eq;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.when;
20 import com.google.common.collect.Lists;
21 import com.google.common.collect.Maps;
22 import java.io.FileNotFoundException;
23 import java.io.InputStream;
24 import java.io.UnsupportedEncodingException;
26 import java.net.URISyntaxException;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.List;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import javax.ws.rs.core.Application;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.MultivaluedHashMap;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.UriInfo;
41 import org.glassfish.jersey.server.ResourceConfig;
42 import org.glassfish.jersey.test.JerseyTest;
43 import org.junit.BeforeClass;
44 import org.junit.Test;
45 import org.mockito.invocation.InvocationOnMock;
46 import org.mockito.stubbing.Answer;
47 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
48 import org.opendaylight.controller.sal.core.api.mount.MountService;
49 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
50 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
51 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
52 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
53 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
54 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
55 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
56 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
59 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
60 import org.opendaylight.yangtools.yang.common.QName;
61 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
62 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
64 import org.opendaylight.yangtools.yang.data.api.Node;
65 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
66 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
67 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
68 import org.w3c.dom.Document;
69 import org.w3c.dom.Element;
70 import org.w3c.dom.NodeList;
72 public class RestGetOperationTest extends JerseyTest {
74 static class NodeData {
76 Object data; // List for a CompositeNode, value Object for a SimpleNode
78 NodeData( final Object key, final Object data ) {
84 private static BrokerFacade brokerFacade;
85 private static RestconfImpl restconfImpl;
86 private static SchemaContext schemaContextYangsIetf;
87 private static SchemaContext schemaContextTestModule;
88 private static CompositeNode answerFromGet;
90 private static SchemaContext schemaContextModules;
91 private static SchemaContext schemaContextBehindMountPoint;
94 public static void init() throws FileNotFoundException {
95 schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs");
96 schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module");
97 ControllerContext controllerContext = ControllerContext.getInstance();
98 controllerContext.setSchemas(schemaContextYangsIetf);
99 brokerFacade = mock(BrokerFacade.class);
100 restconfImpl = RestconfImpl.getInstance();
101 restconfImpl.setBroker(brokerFacade);
102 restconfImpl.setControllerContext(controllerContext);
103 answerFromGet = prepareCompositeNodeWithIetfInterfacesInterfacesData();
105 schemaContextModules = TestUtils.loadSchemaContext("/modules");
106 schemaContextBehindMountPoint = TestUtils.loadSchemaContext("/modules/modules-behind-mount-point");
110 protected Application configure() {
111 /* enable/disable Jersey logs to console */
112 // enable(TestProperties.LOG_TRAFFIC);
113 // enable(TestProperties.DUMP_ENTITY);
114 // enable(TestProperties.RECORD_LOG_LEVEL);
115 // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue());
116 ResourceConfig resourceConfig = new ResourceConfig();
117 resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
118 StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
119 JsonToCompositeNodeProvider.INSTANCE);
120 resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
121 return resourceConfig;
125 * Tests of status codes for "/operational/{identifier}".
128 public void getOperationalStatusCodes() throws UnsupportedEncodingException {
129 mockReadOperationalDataMethod();
130 String uri = "/operational/ietf-interfaces:interfaces/interface/eth0";
131 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
133 uri = "/operational/wrong-module:interfaces/interface/eth0";
134 assertEquals(400, get(uri, MediaType.APPLICATION_XML));
138 * Tests of status codes for "/config/{identifier}".
141 public void getConfigStatusCodes() throws UnsupportedEncodingException {
142 mockReadConfigurationDataMethod();
143 String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
144 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
146 uri = "/config/wrong-module:interfaces/interface/eth0";
147 assertEquals(400, get(uri, MediaType.APPLICATION_XML));
151 * MountPoint test. URI represents mount point.
154 public void getDataWithUrlMountPoint() throws UnsupportedEncodingException, URISyntaxException {
156 brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
157 any(InstanceIdentifier.class))).thenReturn(prepareCnDataForMountPointTest());
158 MountInstance mountInstance = mock(MountInstance.class);
159 when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
160 MountService mockMountService = mock(MountService.class);
161 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
163 ControllerContext.getInstance().setMountService(mockMountService);
165 String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/cont1";
166 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
168 uri = "/config/ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
169 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
173 * MountPoint test. URI represents mount point.
175 * Slashes in URI behind mount point. lst1 element with key
176 * GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is requested via
177 * GET HTTP operation. It is tested whether %2F character is replaced with
178 * simple / in InstanceIdentifier parameter in method
179 * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, InstanceIdentifier)}
180 * which is called in method {@link RestconfImpl#readConfigurationData}
183 * @throws ParseException
186 public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
188 InstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
190 brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
191 eq(awaitedInstanceIdentifier))).thenReturn(prepareCnDataForMountPointTest());
192 MountInstance mountInstance = mock(MountInstance.class);
193 when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
194 MountService mockMountService = mock(MountService.class);
195 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
197 ControllerContext.getInstance().setMountService(mockMountService);
199 String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0";
200 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
203 private InstanceIdentifier prepareInstanceIdentifierForList() throws URISyntaxException, ParseException {
204 List<PathArgument> parameters = new ArrayList<>();
206 Date revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-09");
207 URI uri = new URI("test:module");
208 QName qNameCont = QName.create(uri, revision, "cont");
209 QName qNameList = QName.create(uri, revision, "lst1");
210 QName qNameKeyList = QName.create(uri, revision, "lf11");
212 parameters.add(new InstanceIdentifier.NodeIdentifier(qNameCont));
213 parameters.add(new InstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
214 "GigabitEthernet0/0/0/0"));
215 return InstanceIdentifier.create(parameters);
219 public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException {
221 brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
222 any(InstanceIdentifier.class))).thenReturn(prepareCnDataForMountPointTest());
223 MountInstance mountInstance = mock(MountInstance.class);
224 when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
225 MountService mockMountService = mock(MountService.class);
226 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
228 ControllerContext.getInstance().setMountService(mockMountService);
230 String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/";
231 assertEquals(200, get(uri, MediaType.APPLICATION_XML));
236 public void getModulesTest() throws UnsupportedEncodingException, FileNotFoundException {
237 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
239 String uri = "/modules";
241 Response response = target(uri).request("application/yang.api+json").get();
242 validateModulesResponseJson(response);
244 response = target(uri).request("application/yang.api+xml").get();
245 validateModulesResponseXml(response);
250 public void getStreamsTest() throws UnsupportedEncodingException, FileNotFoundException {
251 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
253 String uri = "/streams";
255 Response response = target(uri).request("application/yang.api+json").get();
256 String responseBody = response.readEntity(String.class);
257 assertNotNull(responseBody);
258 assertTrue(responseBody.contains("streams"));
260 response = target(uri).request("application/yang.api+xml").get();
261 responseBody = response.readEntity(String.class);
262 assertNotNull(responseBody);
263 assertTrue(responseBody.contains("<streams xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\""));
268 public void getModuleTest() throws FileNotFoundException, UnsupportedEncodingException {
269 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
271 String uri = "/modules/module/module2/2014-01-02";
273 Response response = target(uri).request("application/yang.api+xml").get();
274 assertEquals(200, response.getStatus());
275 String responseBody = response.readEntity(String.class);
276 assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
278 String[] split = responseBody.split("<module");
279 assertEquals("<module element is returned more then once", 2, split.length);
281 response = target(uri).request("application/yang.api+json").get();
282 assertEquals(200, response.getStatus());
283 responseBody = response.readEntity(String.class);
284 assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
286 split = responseBody.split("\"module\"");
287 assertEquals("\"module\" element is returned more then once", 2, split.length);
293 public void getOperationsTest() throws FileNotFoundException, UnsupportedEncodingException {
294 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
296 String uri = "/operations";
298 Response response = target(uri).request("application/yang.api+xml").get();
299 assertEquals(200, response.getStatus());
300 String responseBody = response.readEntity(String.class);
301 assertTrue("Xml response for /operations dummy-rpc1-module1 is incorrect",
302 validateOperationsResponseXml(responseBody, "dummy-rpc1-module1", "module:1").find());
303 assertTrue("Xml response for /operations dummy-rpc2-module1 is incorrect",
304 validateOperationsResponseXml(responseBody, "dummy-rpc2-module1", "module:1").find());
305 assertTrue("Xml response for /operations dummy-rpc1-module2 is incorrect",
306 validateOperationsResponseXml(responseBody, "dummy-rpc1-module2", "module:2").find());
307 assertTrue("Xml response for /operations dummy-rpc2-module2 is incorrect",
308 validateOperationsResponseXml(responseBody, "dummy-rpc2-module2", "module:2").find());
310 response = target(uri).request("application/yang.api+json").get();
311 assertEquals(200, response.getStatus());
312 responseBody = response.readEntity(String.class);
313 assertTrue("Json response for /operations dummy-rpc1-module1 is incorrect",
314 validateOperationsResponseJson(responseBody, "dummy-rpc1-module1", "module1").find());
315 assertTrue("Json response for /operations dummy-rpc2-module1 is incorrect",
316 validateOperationsResponseJson(responseBody, "dummy-rpc2-module1", "module1").find());
317 assertTrue("Json response for /operations dummy-rpc1-module2 is incorrect",
318 validateOperationsResponseJson(responseBody, "dummy-rpc1-module2", "module2").find());
319 assertTrue("Json response for /operations dummy-rpc2-module2 is incorrect",
320 validateOperationsResponseJson(responseBody, "dummy-rpc2-module2", "module2").find());
324 // /operations/pathToMountPoint/yang-ext:mount
326 public void getOperationsBehindMountPointTest() throws FileNotFoundException, UnsupportedEncodingException {
327 ControllerContext controllerContext = ControllerContext.getInstance();
328 controllerContext.setGlobalSchema(schemaContextModules);
330 MountInstance mountInstance = mock(MountInstance.class);
331 when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
332 MountService mockMountService = mock(MountService.class);
333 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
335 controllerContext.setMountService(mockMountService);
337 String uri = "/operations/ietf-interfaces:interfaces/interface/0/yang-ext:mount/";
339 Response response = target(uri).request("application/yang.api+xml").get();
340 assertEquals(200, response.getStatus());
341 String responseBody = response.readEntity(String.class);
342 assertTrue("Xml response for /operations/mount_point rpc-behind-module1 is incorrect",
343 validateOperationsResponseXml(responseBody, "rpc-behind-module1", "module:1:behind:mount:point").find());
344 assertTrue("Xml response for /operations/mount_point rpc-behind-module2 is incorrect",
345 validateOperationsResponseXml(responseBody, "rpc-behind-module2", "module:2:behind:mount:point").find());
347 response = target(uri).request("application/yang.api+json").get();
348 assertEquals(200, response.getStatus());
349 responseBody = response.readEntity(String.class);
350 assertTrue("Json response for /operations/mount_point rpc-behind-module1 is incorrect",
351 validateOperationsResponseJson(responseBody, "rpc-behind-module1", "module1-behind-mount-point").find());
352 assertTrue("Json response for /operations/mount_point rpc-behind-module2 is incorrect",
353 validateOperationsResponseJson(responseBody, "rpc-behind-module2", "module2-behind-mount-point").find());
357 private Matcher validateOperationsResponseJson(final String searchIn, final String rpcName, final String moduleName) {
358 StringBuilder regex = new StringBuilder();
361 regex.append(".*\\{");
362 regex.append(".*\"");
364 // operations prefix optional
366 regex.append("ietf-restconf:");
368 // :operations prefix optional
370 regex.append("operations\"");
372 regex.append(".*\\{");
374 regex.append(".*\"" + moduleName);
376 regex.append(rpcName + "\"");
377 regex.append(".*\\[");
378 regex.append(".*null");
379 regex.append(".*\\]");
381 regex.append(".*\\}");
382 regex.append(".*\\}");
386 Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
387 return ptrn.matcher(searchIn);
391 private Matcher validateOperationsResponseXml(final String searchIn, final String rpcName, final String namespace) {
392 StringBuilder regex = new StringBuilder();
396 regex.append(".*<operations");
397 regex.append(".*xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"");
401 regex.append(".*" + rpcName);
402 regex.append(".*" + namespace);
406 regex.append(".*</operations.*");
411 Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
412 return ptrn.matcher(searchIn);
415 // /restconf/modules/pathToMountPoint/yang-ext:mount
417 public void getModulesBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
418 ControllerContext controllerContext = ControllerContext.getInstance();
419 controllerContext.setGlobalSchema(schemaContextModules);
421 MountInstance mountInstance = mock(MountInstance.class);
422 when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
423 MountService mockMountService = mock(MountService.class);
424 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
426 controllerContext.setMountService(mockMountService);
428 String uri = "/modules/ietf-interfaces:interfaces/interface/0/yang-ext:mount/";
430 Response response = target(uri).request("application/yang.api+json").get();
431 assertEquals(200, response.getStatus());
432 String responseBody = response.readEntity(String.class);
435 "module1-behind-mount-point in json wasn't found",
436 prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
437 responseBody).find());
439 "module2-behind-mount-point in json wasn't found",
440 prepareJsonRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point",
441 responseBody).find());
443 response = target(uri).request("application/yang.api+xml").get();
444 assertEquals(200, response.getStatus());
445 responseBody = response.readEntity(String.class);
447 "module1-behind-mount-point in json wasn't found",
448 prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
451 "module2-behind-mount-point in json wasn't found",
452 prepareXmlRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", responseBody)
457 // /restconf/modules/module/pathToMountPoint/yang-ext:mount/moduleName/revision
459 public void getModuleBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
460 ControllerContext controllerContext = ControllerContext.getInstance();
461 controllerContext.setGlobalSchema(schemaContextModules);
463 MountInstance mountInstance = mock(MountInstance.class);
464 when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
465 MountService mockMountService = mock(MountService.class);
466 when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
468 controllerContext.setMountService(mockMountService);
470 String uri = "/modules/module/ietf-interfaces:interfaces/interface/0/yang-ext:mount/module1-behind-mount-point/2014-02-03";
472 Response response = target(uri).request("application/yang.api+json").get();
473 assertEquals(200, response.getStatus());
474 String responseBody = response.readEntity(String.class);
477 "module1-behind-mount-point in json wasn't found",
478 prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
479 responseBody).find());
480 String[] split = responseBody.split("\"module\"");
481 assertEquals("\"module\" element is returned more then once", 2, split.length);
483 response = target(uri).request("application/yang.api+xml").get();
484 assertEquals(200, response.getStatus());
485 responseBody = response.readEntity(String.class);
487 "module1-behind-mount-point in json wasn't found",
488 prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
490 split = responseBody.split("<module");
491 assertEquals("<module element is returned more then once", 2, split.length);
495 private void validateModulesResponseXml(final Response response) {
496 assertEquals(200, response.getStatus());
497 String responseBody = response.readEntity(String.class);
499 assertTrue("Module1 in xml wasn't found", prepareXmlRegex("module1", "2014-01-01", "module:1", responseBody)
501 assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
503 assertTrue("Module3 in xml wasn't found", prepareXmlRegex("module3", "2014-01-03", "module:3", responseBody)
507 private void validateModulesResponseJson(final Response response) {
508 assertEquals(200, response.getStatus());
509 String responseBody = response.readEntity(String.class);
511 assertTrue("Module1 in json wasn't found", prepareJsonRegex("module1", "2014-01-01", "module:1", responseBody)
513 assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
515 assertTrue("Module3 in json wasn't found", prepareJsonRegex("module3", "2014-01-03", "module:3", responseBody)
519 private Matcher prepareJsonRegex(final String module, final String revision, final String namespace, final String searchIn) {
520 StringBuilder regex = new StringBuilder();
523 regex.append(".*\\{");
524 regex.append(".*\"name\"");
526 regex.append(".*\"" + module + "\",");
528 regex.append(".*\"revision\"");
530 regex.append(".*\"" + revision + "\",");
532 regex.append(".*\"namespace\"");
534 regex.append(".*\"" + namespace + "\"");
536 regex.append(".*\\}");
540 Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
541 return ptrn.matcher(searchIn);
545 private Matcher prepareXmlRegex(final String module, final String revision, final String namespace, final String searchIn) {
546 StringBuilder regex = new StringBuilder();
549 regex.append(".*<module.*");
552 regex.append(".*<name>");
553 regex.append(".*" + module);
554 regex.append(".*<\\/name>");
556 regex.append(".*<revision>");
557 regex.append(".*" + revision);
558 regex.append(".*<\\/revision>");
560 regex.append(".*<namespace>");
561 regex.append(".*" + namespace);
562 regex.append(".*<\\/namespace>");
564 regex.append(".*<\\/module.*>");
569 Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
570 return ptrn.matcher(searchIn);
573 private void prepareMockForModulesTest(final ControllerContext mockedControllerContext) throws FileNotFoundException {
574 SchemaContext schemaContext = TestUtils.loadSchemaContext("/modules");
575 mockedControllerContext.setGlobalSchema(schemaContext);
576 // when(mockedControllerContext.getGlobalSchema()).thenReturn(schemaContext);
579 private int get(final String uri, final String mediaType) {
580 return target(uri).request(mediaType).get().getStatus();
583 private CompositeNode prepareCnDataForMountPointTest() throws URISyntaxException {
584 CompositeNodeWrapper cont1 = new CompositeNodeWrapper(new URI("test:module"), "cont1");
585 SimpleNodeWrapper lf11 = new SimpleNodeWrapper(new URI("test:module"), "lf11", "lf11 value");
586 cont1.addValue(lf11);
587 return cont1.unwrap();
590 private void mockReadOperationalDataMethod() {
591 when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(answerFromGet);
594 private void mockReadConfigurationDataMethod() {
595 when(brokerFacade.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(answerFromGet);
598 private static CompositeNode prepareCompositeNodeWithIetfInterfacesInterfacesData() {
599 CompositeNode intface;
601 intface = new CompositeNodeWrapper(new URI("interface"), "interface");
602 List<Node<?>> childs = new ArrayList<>();
604 childs.add(new SimpleNodeWrapper(new URI("name"), "name", "eth0"));
605 childs.add(new SimpleNodeWrapper(new URI("type"), "type", "ethernetCsmacd"));
606 childs.add(new SimpleNodeWrapper(new URI("enabled"), "enabled", Boolean.FALSE));
607 childs.add(new SimpleNodeWrapper(new URI("description"), "description", "some interface"));
608 intface.setValue(childs);
610 } catch (URISyntaxException e) {
617 * If includeWhiteChars URI parameter is set to false then no white
618 * characters can be included in returned output
619 * @throws UnsupportedEncodingException
622 public void getDataWithUriIncludeWhiteCharsParameterTest() throws UnsupportedEncodingException {
623 getDataWithUriIncludeWhiteCharsParameter("config");
624 getDataWithUriIncludeWhiteCharsParameter("operational");
628 private void getDataWithUriIncludeWhiteCharsParameter(String target) throws UnsupportedEncodingException {
629 mockReadConfigurationDataMethod();
630 String uri = "/"+target+"/ietf-interfaces:interfaces/interface/eth0";
631 Response response = target(uri).queryParam("prettyPrint", "false").request("application/xml").get();
632 String xmlData = response.readEntity(String.class);
634 Pattern pattern = Pattern.compile(".*(>\\s+|\\s+<).*", Pattern.DOTALL);
635 Matcher matcher = pattern.matcher(xmlData);
636 // XML element can't surrounded with white character (e.g "> " or
638 assertFalse(matcher.matches());
640 response = target(uri).queryParam("prettyPrint", "false").request("application/json").get();
641 String jsonData = response.readEntity(String.class);
642 pattern = Pattern.compile(".*(\\}\\s+|\\s+\\{|\\]\\s+|\\s+\\[|\\s+:|:\\s+).*", Pattern.DOTALL);
643 matcher = pattern.matcher(jsonData);
644 // JSON element can't surrounded with white character (e.g "} ", " {",
645 // "] ", " [", " :" or ": ")
646 assertFalse(matcher.matches());
650 public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException {
652 ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
654 CompositeNode depth1Cont = toCompositeNode(
655 toCompositeNodeData( toNestedQName( "depth1-cont" ),
656 toCompositeNodeData( toNestedQName( "depth2-cont1" ),
657 toCompositeNodeData( toNestedQName( "depth3-cont1" ),
658 toCompositeNodeData( toNestedQName( "depth4-cont1" ),
659 toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
661 toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
663 toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
665 toCompositeNodeData( toNestedQName( "depth2-cont2" ),
666 toCompositeNodeData( toNestedQName( "depth3-cont2" ),
667 toCompositeNodeData( toNestedQName( "depth4-cont2" ),
668 toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" )
670 toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" )
672 toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" )
674 toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" )
677 when( brokerFacade.readConfigurationData( any( InstanceIdentifier.class ) ) )
678 .thenReturn( depth1Cont );
680 // Test config with depth 1
682 Response response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "1" )
683 .request( "application/xml" ).get();
685 verifyXMLResponse( response, expectEmptyContainer( "depth1-cont" ) );
687 // Test config with depth 2
689 response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "2" )
690 .request( "application/xml" ).get();
692 // String xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
693 // Response mr=mock(Response.class);
694 // when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) );
696 verifyXMLResponse( response,
697 expectContainer( "depth1-cont",
698 expectEmptyContainer( "depth2-cont1" ),
699 expectEmptyContainer( "depth2-cont2" ),
700 expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
703 // Test config with depth 3
705 response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "3" )
706 .request( "application/xml" ).get();
708 verifyXMLResponse( response,
709 expectContainer( "depth1-cont",
710 expectContainer( "depth2-cont1",
711 expectEmptyContainer( "depth3-cont1" ),
712 expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
714 expectContainer( "depth2-cont2",
715 expectEmptyContainer( "depth3-cont2" ),
716 expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
718 expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
721 // Test config with depth 4
723 response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "4" )
724 .request( "application/xml" ).get();
726 verifyXMLResponse( response,
727 expectContainer( "depth1-cont",
728 expectContainer( "depth2-cont1",
729 expectContainer( "depth3-cont1",
730 expectEmptyContainer( "depth4-cont1" ),
731 expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
733 expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
735 expectContainer( "depth2-cont2",
736 expectContainer( "depth3-cont2",
737 expectEmptyContainer( "depth4-cont2" ),
738 expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
740 expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
742 expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
745 // Test config with depth 5
747 response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "5" )
748 .request( "application/xml" ).get();
750 verifyXMLResponse( response,
751 expectContainer( "depth1-cont",
752 expectContainer( "depth2-cont1",
753 expectContainer( "depth3-cont1",
754 expectContainer( "depth4-cont1",
755 expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
757 expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
759 expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
761 expectContainer( "depth2-cont2",
762 expectContainer( "depth3-cont2",
763 expectContainer( "depth4-cont2",
764 expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
766 expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
768 expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
770 expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
773 // Test config with depth unbounded
775 response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "unbounded" )
776 .request( "application/xml" ).get();
778 verifyXMLResponse( response,
779 expectContainer( "depth1-cont",
780 expectContainer( "depth2-cont1",
781 expectContainer( "depth3-cont1",
782 expectContainer( "depth4-cont1",
783 expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
785 expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
787 expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
789 expectContainer( "depth2-cont2",
790 expectContainer( "depth3-cont2",
791 expectContainer( "depth4-cont2",
792 expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
794 expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
796 expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
798 expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
803 CompositeNode depth2Cont1 = toCompositeNode(
804 toCompositeNodeData( toNestedQName( "depth2-cont1" ),
805 toCompositeNodeData( toNestedQName( "depth3-cont1" ),
806 toCompositeNodeData( toNestedQName( "depth4-cont1" ),
807 toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
809 toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
811 toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
814 when( brokerFacade.readOperationalData( any( InstanceIdentifier.class ) ) )
815 .thenReturn( depth2Cont1 );
817 response = target( "/operational/nested-module:depth1-cont/depth2-cont1" )
818 .queryParam( "depth", "3" ).request( "application/xml" ).get();
820 verifyXMLResponse( response,
821 expectContainer( "depth2-cont1",
822 expectContainer( "depth3-cont1",
823 expectEmptyContainer( "depth4-cont1" ),
824 expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
826 expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
831 public void getDataWithInvalidDepthParameterTest() {
833 ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
835 final MultivaluedMap<String,String> paramMap = new MultivaluedHashMap<>();
836 paramMap.putSingle( "depth", "1o" );
837 UriInfo mockInfo = mock( UriInfo.class );
838 when( mockInfo.getQueryParameters( false ) ).thenAnswer(
839 new Answer<MultivaluedMap<String,String>>() {
841 public MultivaluedMap<String, String> answer( final InvocationOnMock invocation ) {
846 getDataWithInvalidDepthParameterTest( mockInfo );
848 paramMap.putSingle( "depth", "0" );
849 getDataWithInvalidDepthParameterTest( mockInfo );
851 paramMap.putSingle( "depth", "-1" );
852 getDataWithInvalidDepthParameterTest( mockInfo );
855 private void getDataWithInvalidDepthParameterTest( final UriInfo uriInfo ) {
857 restconfImpl.readConfigurationData( "nested-module:depth1-cont", uriInfo );
858 fail( "Expected RestconfDocumentedException" );
860 catch( RestconfDocumentedException e ) {
861 assertTrue( "Unexpected error message: " + e.getErrors().get( 0 ).getErrorMessage(),
862 e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) );
866 private void verifyXMLResponse( final Response response, final NodeData nodeData ) {
868 Document doc = TestUtils.loadDocumentFrom( (InputStream) response.getEntity() );
869 assertNotNull( "Could not parse XML document", doc );
871 //System.out.println(TestUtils.getDocumentInPrintableForm( doc ));
873 verifyContainerElement( doc.getDocumentElement(), nodeData );
876 @SuppressWarnings("unchecked")
877 private void verifyContainerElement( final Element element, final NodeData nodeData ) {
879 assertEquals( "Element local name", nodeData.key, element.getNodeName() );
881 NodeList childNodes = element.getChildNodes();
882 if( nodeData.data == null ) { // empty container
883 assertTrue( "Expected no child elements for \"" + element.getNodeName() + "\"",
884 childNodes.getLength() == 0 );
888 Map<String,NodeData> expChildMap = Maps.newHashMap();
889 for( NodeData expChild: (List<NodeData>)nodeData.data ) {
890 expChildMap.put( expChild.key.toString(), expChild );
893 for( int i = 0; i < childNodes.getLength(); i++ ) {
894 org.w3c.dom.Node actualChild = childNodes.item( i );
895 if( !( actualChild instanceof Element ) ) {
899 Element actualElement = (Element)actualChild;
900 NodeData expChild = expChildMap.remove( actualElement.getNodeName() );
901 assertNotNull( "Unexpected child element for parent \"" + element.getNodeName() +
902 "\": " + actualElement.getNodeName(), expChild );
904 if( expChild.data == null || expChild.data instanceof List ) {
905 verifyContainerElement( actualElement, expChild );
908 assertEquals( "Text content for element: " + actualElement.getNodeName(),
909 expChild.data, actualElement.getTextContent() );
913 if( !expChildMap.isEmpty() ) {
914 fail( "Missing elements for parent \"" + element.getNodeName() +
915 "\": " + expChildMap.keySet() );
919 private NodeData expectContainer( final String name, final NodeData... childData ) {
920 return new NodeData( name, Lists.newArrayList( childData ) );
923 private NodeData expectEmptyContainer( final String name ) {
924 return new NodeData( name, null );
927 private NodeData expectLeaf( final String name, final Object value ) {
928 return new NodeData( name, value );
931 private QName toNestedQName( final String localName ) {
932 return QName.create( "urn:nested:module", "2014-06-3", localName );
935 @SuppressWarnings("unchecked")
936 private CompositeNode toCompositeNode( final NodeData nodeData ) {
937 CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
938 builder.setQName( (QName) nodeData.key );
940 for( NodeData child: (List<NodeData>)nodeData.data ) {
941 if( child.data instanceof List ) {
942 builder.add( toCompositeNode( child ) );
945 builder.addLeaf( (QName) child.key, child.data );
949 return builder.toInstance();
952 private NodeData toCompositeNodeData( final QName key, final NodeData... childData ) {
953 return new NodeData( key, Lists.newArrayList( childData ) );
956 private NodeData toSimpleNodeData( final QName key, final Object value ) {
957 return new NodeData( key, value );