8e1121a562c964debf3da74c870a8801a763e6b3
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / RestGetOperationTest.java
1 /*
2  * Copyright (c) 2014 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.controller.sal.restconf.impl.test;
9
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.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Matchers.any;
17 import static org.mockito.Matchers.eq;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.when;
20 import com.google.common.base.Optional;
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps;
23 import java.io.FileNotFoundException;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URI;
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.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 import javax.ws.rs.core.Application;
38 import javax.ws.rs.core.MediaType;
39 import javax.ws.rs.core.MultivaluedHashMap;
40 import javax.ws.rs.core.MultivaluedMap;
41 import javax.ws.rs.core.Response;
42 import javax.ws.rs.core.UriInfo;
43 import org.glassfish.jersey.server.ResourceConfig;
44 import org.glassfish.jersey.test.JerseyTest;
45 import org.junit.BeforeClass;
46 import org.junit.Ignore;
47 import org.junit.Test;
48 import org.mockito.invocation.InvocationOnMock;
49 import org.mockito.stubbing.Answer;
50 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
51 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
52 import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
53 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
54 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
55 import org.opendaylight.controller.sal.rest.impl.RestconfApplication;
56 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
57 import org.opendaylight.controller.sal.rest.impl.XmlNormalizedNodeBodyReader;
58 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
59 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
60 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
61 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
62 import org.opendaylight.yangtools.yang.common.QName;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
66 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
69 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
70 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
71 import org.opendaylight.yangtools.yang.model.api.Module;
72 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
73 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
74 import org.w3c.dom.Document;
75 import org.w3c.dom.Element;
76 import org.w3c.dom.NodeList;
77
78 public class RestGetOperationTest extends JerseyTest {
79
80     static class NodeData {
81         Object key;
82         Object data; // List for a CompositeNode, value Object for a SimpleNode
83
84         NodeData(final Object key, final Object data) {
85             this.key = key;
86             this.data = data;
87         }
88     }
89
90     private static BrokerFacade brokerFacade;
91     private static RestconfImpl restconfImpl;
92     private static SchemaContext schemaContextYangsIetf;
93     private static SchemaContext schemaContextTestModule;
94     private static NormalizedNode answerFromGet;
95
96     private static SchemaContext schemaContextModules;
97     private static SchemaContext schemaContextBehindMountPoint;
98
99     private static final String RESTCONF_NS = "urn:ietf:params:xml:ns:yang:ietf-restconf";
100
101     @BeforeClass
102     public static void init() throws FileNotFoundException, ParseException {
103         schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs");
104         schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module");
105         brokerFacade = mock(BrokerFacade.class);
106         restconfImpl = RestconfImpl.getInstance();
107         restconfImpl.setBroker(brokerFacade);
108         answerFromGet = TestUtils.prepareNormalizedNodeWithIetfInterfacesInterfacesData();
109
110         schemaContextModules = TestUtils.loadSchemaContext("/modules");
111         schemaContextBehindMountPoint = TestUtils.loadSchemaContext("/modules/modules-behind-mount-point");
112     }
113
114     @Override
115     protected Application configure() {
116         /* enable/disable Jersey logs to console */
117         // enable(TestProperties.LOG_TRAFFIC);
118         // enable(TestProperties.DUMP_ENTITY);
119         // enable(TestProperties.RECORD_LOG_LEVEL);
120         // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue());
121         ResourceConfig resourceConfig = new ResourceConfig();
122         resourceConfig = resourceConfig.registerInstances(restconfImpl, new NormalizedNodeJsonBodyWriter(),
123                 new NormalizedNodeXmlBodyWriter(), new XmlNormalizedNodeBodyReader(), new JsonNormalizedNodeBodyReader());
124         resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class);
125         resourceConfig.registerClasses(new RestconfApplication().getClasses());
126         return resourceConfig;
127     }
128
129     private void setControllerContext(final SchemaContext schemaContext) {
130         final ControllerContext controllerContext = ControllerContext.getInstance();
131         controllerContext.setSchemas(schemaContext);
132         restconfImpl.setControllerContext(controllerContext);
133     }
134
135     /**
136      * Tests of status codes for "/operational/{identifier}".
137      */
138     @Test
139     public void getOperationalStatusCodes() throws UnsupportedEncodingException {
140         setControllerContext(schemaContextYangsIetf);
141         mockReadOperationalDataMethod();
142         String uri = "/operational/ietf-interfaces:interfaces/interface/eth0";
143         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
144
145         uri = "/operational/wrong-module:interfaces/interface/eth0";
146         assertEquals(400, get(uri, MediaType.APPLICATION_XML));
147     }
148
149     /**
150      * Tests of status codes for "/config/{identifier}".
151      */
152     @Test
153     public void getConfigStatusCodes() throws UnsupportedEncodingException {
154         setControllerContext(schemaContextYangsIetf);
155         mockReadConfigurationDataMethod();
156         String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
157         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
158
159         uri = "/config/wrong-module:interfaces/interface/eth0";
160         assertEquals(400, get(uri, MediaType.APPLICATION_XML));
161     }
162
163     /**
164      * MountPoint test. URI represents mount point.
165      */
166     @Test
167     public void getDataWithUrlMountPoint() throws UnsupportedEncodingException, URISyntaxException, ParseException {
168         when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
169                 prepareCnDataForMountPointTest(false));
170         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
171         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
172         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
173         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
174
175         ControllerContext.getInstance().setMountService(mockMountService);
176
177         String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/cont1";
178         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
179
180         uri = "/config/ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
181         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
182     }
183
184     /**
185      * MountPoint test. URI represents mount point.
186      *
187      * Slashes in URI behind mount point. lst1 element with key GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is
188      * requested via GET HTTP operation. It is tested whether %2F character is replaced with simple / in
189      * InstanceIdentifier parameter in method
190      * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, YangInstanceIdentifier)} which is called in
191      * method {@link RestconfImpl#readConfigurationData}
192      *
193      * @throws ParseException
194      */
195     @Test
196     public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
197             ParseException {
198         final YangInstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
199         when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier))).thenReturn(
200                 prepareCnDataForSlashesBehindMountPointTest());
201         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
202         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
203         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
204         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
205
206         ControllerContext.getInstance().setMountService(mockMountService);
207
208         final String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0";
209         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
210     }
211
212     private YangInstanceIdentifier prepareInstanceIdentifierForList() throws URISyntaxException, ParseException {
213         final List<PathArgument> parameters = new ArrayList<>();
214
215         final Date revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-09");
216         final URI uri = new URI("test:module");
217         final QName qNameCont = QName.create(uri, revision, "cont");
218         final QName qNameList = QName.create(uri, revision, "lst1");
219         final QName qNameKeyList = QName.create(uri, revision, "lf11");
220
221         parameters.add(new YangInstanceIdentifier.NodeIdentifier(qNameCont));
222         parameters.add(new YangInstanceIdentifier.NodeIdentifier(qNameList));
223         parameters.add(new YangInstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
224                 "GigabitEthernet0/0/0/0"));
225         return YangInstanceIdentifier.create(parameters);
226     }
227
228     @Test
229     public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException,
230             ParseException {
231         when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
232                 prepareCnDataForMountPointTest(true));
233         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
234         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
235         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
236         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
237
238         ControllerContext.getInstance().setMountService(mockMountService);
239
240         final String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont";
241         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
242     }
243
244     // /modules
245     @Test
246     public void getModulesTest() throws UnsupportedEncodingException, FileNotFoundException {
247         final ControllerContext controllerContext = ControllerContext.getInstance();
248         controllerContext.setGlobalSchema(schemaContextModules);
249         restconfImpl.setControllerContext(controllerContext);
250
251         final String uri = "/modules";
252
253         Response response = target(uri).request("application/yang.api+json").get();
254         validateModulesResponseJson(response);
255
256         response = target(uri).request("application/yang.api+xml").get();
257         validateModulesResponseXml(response,schemaContextModules);
258     }
259
260     // /streams/
261     @Test
262     @Ignore // FIXME : find why it is fail by in gerrit build
263     public void getStreamsTest() throws UnsupportedEncodingException, FileNotFoundException {
264         setControllerContext(schemaContextModules);
265
266         final String uri = "/streams";
267
268         Response response = target(uri).request("application/yang.api+json").get();
269         final String responseBody = response.readEntity(String.class);
270         assertEquals(200, response.getStatus());
271         assertNotNull(responseBody);
272         assertTrue(responseBody.contains("streams"));
273
274         response = target(uri).request("application/yang.api+xml").get();
275         assertEquals(200, response.getStatus());
276         final Document responseXmlBody = response.readEntity(Document.class);
277         assertNotNull(responseXmlBody);
278         final Element rootNode = responseXmlBody.getDocumentElement();
279
280         assertEquals("streams", rootNode.getLocalName());
281         assertEquals(RESTCONF_NS, rootNode.getNamespaceURI());
282     }
283
284     // /modules/module
285     @Test
286     public void getModuleTest() throws FileNotFoundException, UnsupportedEncodingException {
287         setControllerContext(schemaContextModules);
288
289         final String uri = "/modules/module/module2/2014-01-02";
290
291         Response response = target(uri).request("application/yang.api+xml").get();
292         assertEquals(200, response.getStatus());
293         final Document responseXml = response.readEntity(Document.class);
294
295         final QName qname = assertedModuleXmlToModuleQName(responseXml.getDocumentElement());
296         assertNotNull(qname);
297
298         assertEquals("module2", qname.getLocalName());
299         assertEquals("module:2", qname.getNamespace().toString());
300         assertEquals("2014-01-02", qname.getFormattedRevision());
301
302         response = target(uri).request("application/yang.api+json").get();
303         assertEquals(200, response.getStatus());
304         final String responseBody = response.readEntity(String.class);
305         assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
306                 .find());
307         final String[] split = responseBody.split("\"module\"");
308         assertEquals("\"module\" element is returned more then once", 2, split.length);
309
310     }
311
312     // /operations
313     @Test
314     @Ignore // FIXME restconf-netconf yang schema has to be updated for operations container
315     public void getOperationsTest() throws FileNotFoundException, UnsupportedEncodingException {
316         setControllerContext(schemaContextModules);
317
318         final String uri = "/operations";
319
320         Response response = target(uri).request("application/yang.api+xml").get();
321         assertEquals(200, response.getStatus());
322         final Document responseDoc = response.readEntity(Document.class);
323         validateOperationsResponseXml(responseDoc, schemaContextModules);
324
325         response = target(uri).request("application/yang.api+json").get();
326         assertEquals(200, response.getStatus());
327         final String responseBody = response.readEntity(String.class);
328         assertTrue("Json response for /operations dummy-rpc1-module1 is incorrect",
329                 validateOperationsResponseJson(responseBody, "dummy-rpc1-module1", "module1").find());
330         assertTrue("Json response for /operations dummy-rpc2-module1 is incorrect",
331                 validateOperationsResponseJson(responseBody, "dummy-rpc2-module1", "module1").find());
332         assertTrue("Json response for /operations dummy-rpc1-module2 is incorrect",
333                 validateOperationsResponseJson(responseBody, "dummy-rpc1-module2", "module2").find());
334         assertTrue("Json response for /operations dummy-rpc2-module2 is incorrect",
335                 validateOperationsResponseJson(responseBody, "dummy-rpc2-module2", "module2").find());
336
337     }
338
339     private void validateOperationsResponseXml(final Document responseDoc, final SchemaContext schemaContext) {
340         final Element operationsElem = responseDoc.getDocumentElement();
341         assertEquals(RESTCONF_NS, operationsElem.getNamespaceURI());
342         assertEquals("operations", operationsElem.getLocalName());
343
344
345         final HashSet<QName> foundOperations = new HashSet<>();
346
347         final NodeList operationsList = operationsElem.getChildNodes();
348         for(int i = 0;i < operationsList.getLength();i++) {
349             final org.w3c.dom.Node operation = operationsList.item(i);
350
351             final String namespace = operation.getNamespaceURI();
352             final String name = operation.getLocalName();
353             final QName opQName = QName.create(URI.create(namespace), null, name);
354             foundOperations.add(opQName);
355         }
356
357         for(final RpcDefinition schemaOp : schemaContext.getOperations()) {
358             assertTrue(foundOperations.contains(schemaOp.getQName().withoutRevision()));
359         }
360
361     }
362
363     // /operations/pathToMountPoint/yang-ext:mount
364     @Test
365     @Ignore // FIXME fix the way to provide operations overview functionality asap
366     public void getOperationsBehindMountPointTest() throws FileNotFoundException, UnsupportedEncodingException {
367         setControllerContext(schemaContextModules);
368
369         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
370         when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
371         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
372         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
373
374         ControllerContext.getInstance().setMountService(mockMountService);
375
376         final String uri = "/operations/ietf-interfaces:interfaces/interface/0/yang-ext:mount/";
377
378         Response response = target(uri).request("application/yang.api+xml").get();
379         assertEquals(200, response.getStatus());
380
381         final Document responseDoc = response.readEntity(Document.class);
382         validateOperationsResponseXml(responseDoc, schemaContextBehindMountPoint);
383
384         response = target(uri).request("application/yang.api+json").get();
385         assertEquals(200, response.getStatus());
386         final String responseBody = response.readEntity(String.class);
387         assertTrue("Json response for /operations/mount_point rpc-behind-module1 is incorrect",
388                 validateOperationsResponseJson(responseBody, "rpc-behind-module1", "module1-behind-mount-point").find());
389         assertTrue("Json response for /operations/mount_point rpc-behind-module2 is incorrect",
390                 validateOperationsResponseJson(responseBody, "rpc-behind-module2", "module2-behind-mount-point").find());
391
392     }
393
394     private Matcher validateOperationsResponseJson(final String searchIn, final String rpcName, final String moduleName) {
395         final StringBuilder regex = new StringBuilder();
396         regex.append("^");
397
398         regex.append(".*\\{");
399         regex.append(".*\"");
400
401         // operations prefix optional
402         regex.append("(");
403         regex.append("ietf-restconf:");
404         regex.append("|)");
405         // :operations prefix optional
406
407         regex.append("operations\"");
408         regex.append(".*:");
409         regex.append(".*\\{");
410
411         regex.append(".*\"" + moduleName);
412         regex.append(":");
413         regex.append(rpcName + "\"");
414         regex.append(".*\\[");
415         regex.append(".*null");
416         regex.append(".*\\]");
417
418         regex.append(".*\\}");
419         regex.append(".*\\}");
420
421         regex.append(".*");
422         regex.append("$");
423         final Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
424         return ptrn.matcher(searchIn);
425
426     }
427
428     private Matcher validateOperationsResponseXml(final String searchIn, final String rpcName, final String namespace) {
429         final StringBuilder regex = new StringBuilder();
430
431         regex.append("^");
432
433         regex.append(".*<operations");
434         regex.append(".*xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"");
435         regex.append(".*>");
436
437         regex.append(".*<");
438         regex.append(".*" + rpcName);
439         regex.append(".*" + namespace);
440         regex.append(".*/");
441         regex.append(".*>");
442
443         regex.append(".*</operations.*");
444         regex.append(".*>");
445
446         regex.append(".*");
447         regex.append("$");
448         final Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
449         return ptrn.matcher(searchIn);
450     }
451
452     // /restconf/modules/pathToMountPoint/yang-ext:mount
453     @Test
454     public void getModulesBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
455         setControllerContext(schemaContextModules);
456
457         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
458         when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
459         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
460         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
461
462         ControllerContext.getInstance().setMountService(mockMountService);
463
464         final String uri = "/modules/ietf-interfaces:interfaces/interface/0/yang-ext:mount/";
465
466         Response response = target(uri).request("application/yang.api+json").get();
467         assertEquals(200, response.getStatus());
468         final String responseBody = response.readEntity(String.class);
469
470         assertTrue(
471                 "module1-behind-mount-point in json wasn't found",
472                 prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
473                         responseBody).find());
474         assertTrue(
475                 "module2-behind-mount-point in json wasn't found",
476                 prepareJsonRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point",
477                         responseBody).find());
478
479         response = target(uri).request("application/yang.api+xml").get();
480         assertEquals(200, response.getStatus());
481         validateModulesResponseXml(response, schemaContextBehindMountPoint);
482
483     }
484
485     // /restconf/modules/module/pathToMountPoint/yang-ext:mount/moduleName/revision
486     @Test
487     public void getModuleBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
488         setControllerContext(schemaContextModules);
489
490         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
491         when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
492         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
493         when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
494
495         ControllerContext.getInstance().setMountService(mockMountService);
496
497         final String uri = "/modules/module/ietf-interfaces:interfaces/interface/0/yang-ext:mount/module1-behind-mount-point/2014-02-03";
498
499         Response response = target(uri).request("application/yang.api+json").get();
500         assertEquals(200, response.getStatus());
501         final String responseBody = response.readEntity(String.class);
502
503         assertTrue(
504                 "module1-behind-mount-point in json wasn't found",
505                 prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
506                         responseBody).find());
507         final String[] split = responseBody.split("\"module\"");
508         assertEquals("\"module\" element is returned more then once", 2, split.length);
509
510         response = target(uri).request("application/yang.api+xml").get();
511         assertEquals(200, response.getStatus());
512         final Document responseXml = response.readEntity(Document.class);
513
514         final QName module = assertedModuleXmlToModuleQName(responseXml.getDocumentElement());
515
516         assertEquals("module1-behind-mount-point", module.getLocalName());
517         assertEquals("2014-02-03", module.getFormattedRevision());
518         assertEquals("module:1:behind:mount:point", module.getNamespace().toString());
519
520
521     }
522
523     private void validateModulesResponseXml(final Response response, final SchemaContext schemaContext) {
524         assertEquals(200, response.getStatus());
525         final Document responseBody = response.readEntity(Document.class);
526         final NodeList moduleNodes = responseBody.getDocumentElement().getElementsByTagNameNS(RESTCONF_NS, "module");
527
528         assertTrue(moduleNodes.getLength() > 0);
529
530         final HashSet<QName> foundModules = new HashSet<>();
531
532         for(int i=0;i < moduleNodes.getLength();i++) {
533             final org.w3c.dom.Node module = moduleNodes.item(i);
534
535             final QName name = assertedModuleXmlToModuleQName(module);
536             foundModules.add(name);
537         }
538
539         assertAllModules(foundModules,schemaContext);
540     }
541
542     private void assertAllModules(final Set<QName> foundModules, final SchemaContext schemaContext) {
543         for(final Module module : schemaContext.getModules()) {
544             final QName current = QName.create(module.getQNameModule(),module.getName());
545             assertTrue("Module not found in response.",foundModules.contains(current));
546         }
547
548     }
549
550     private QName assertedModuleXmlToModuleQName(final org.w3c.dom.Node module) {
551         assertEquals("module", module.getLocalName());
552         assertEquals(RESTCONF_NS, module.getNamespaceURI());
553         String revision = null;
554         String namespace = null;
555         String name = null;
556
557
558         final NodeList childNodes = module.getChildNodes();
559
560         for(int i =0;i < childNodes.getLength(); i++) {
561             final org.w3c.dom.Node child = childNodes.item(i);
562             assertEquals(RESTCONF_NS, child.getNamespaceURI());
563
564             switch(child.getLocalName()) {
565                 case "name":
566                     assertNull("Name element appeared multiple times",name);
567                     name = child.getTextContent().trim();
568                     break;
569                 case "revision":
570                     assertNull("Revision element appeared multiple times",revision);
571                     revision = child.getTextContent().trim();
572                     break;
573
574                 case "namespace":
575                     assertNull("Namespace element appeared multiple times",namespace);
576                     namespace = child.getTextContent().trim();
577                     break;
578             }
579         }
580
581         assertNotNull("Revision was not part of xml",revision);
582         assertNotNull("Module namespace was not part of xml",namespace);
583         assertNotNull("Module identiffier was not part of xml",name);
584
585
586         // TODO Auto-generated method stub
587
588         return QName.create(namespace,revision,name);
589     }
590
591     private void validateModulesResponseJson(final Response response) {
592         assertEquals(200, response.getStatus());
593         final String responseBody = response.readEntity(String.class);
594
595         assertTrue("Module1 in json wasn't found", prepareJsonRegex("module1", "2014-01-01", "module:1", responseBody)
596                 .find());
597         assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
598                 .find());
599         assertTrue("Module3 in json wasn't found", prepareJsonRegex("module3", "2014-01-03", "module:3", responseBody)
600                 .find());
601     }
602
603     private Matcher prepareJsonRegex(final String module, final String revision, final String namespace,
604             final String searchIn) {
605         final StringBuilder regex = new StringBuilder();
606         regex.append("^");
607
608         regex.append(".*\\{");
609         regex.append(".*\"name\"");
610         regex.append(".*:");
611         regex.append(".*\"" + module + "\",");
612
613         regex.append(".*\"revision\"");
614         regex.append(".*:");
615         regex.append(".*\"" + revision + "\",");
616
617         regex.append(".*\"namespace\"");
618         regex.append(".*:");
619         regex.append(".*\"" + namespace + "\"");
620
621         regex.append(".*\\}");
622
623         regex.append(".*");
624         regex.append("$");
625         final Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
626         return ptrn.matcher(searchIn);
627
628     }
629
630
631     private void prepareMockForModulesTest(final ControllerContext mockedControllerContext)
632             throws FileNotFoundException {
633         final SchemaContext schemaContext = TestUtils.loadSchemaContext("/modules");
634         mockedControllerContext.setGlobalSchema(schemaContext);
635         // when(mockedControllerContext.getGlobalSchema()).thenReturn(schemaContext);
636     }
637
638     private int get(final String uri, final String mediaType) {
639         return target(uri).request(mediaType).get().getStatus();
640     }
641
642     /**
643     container cont {
644         container cont1 {
645             leaf lf11 {
646                 type string;
647             }
648     */
649     private NormalizedNode prepareCnDataForMountPointTest(final boolean wrapToCont) throws URISyntaxException, ParseException {
650         final String testModuleDate = "2014-01-09";
651         final ContainerNode contChild = Builders
652                 .containerBuilder()
653                 .withNodeIdentifier(TestUtils.getNodeIdentifier("cont1", "test:module", testModuleDate))
654                 .withChild(
655                         Builders.leafBuilder()
656                                 .withNodeIdentifier(TestUtils.getNodeIdentifier("lf11", "test:module", testModuleDate))
657                                 .withValue("lf11 value").build()).build();
658
659         if (wrapToCont) {
660             return Builders.containerBuilder()
661                     .withNodeIdentifier(TestUtils.getNodeIdentifier("cont", "test:module", testModuleDate))
662                     .withChild(contChild).build();
663         }
664         return contChild;
665
666     }
667
668     private void mockReadOperationalDataMethod() {
669         when(brokerFacade.readOperationalData(any(YangInstanceIdentifier.class))).thenReturn(answerFromGet);
670     }
671
672     private void mockReadConfigurationDataMethod() {
673         when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class))).thenReturn(answerFromGet);
674     }
675
676     private NormalizedNode prepareCnDataForSlashesBehindMountPointTest() throws ParseException {
677         return ImmutableMapEntryNodeBuilder
678                 .create()
679                 .withNodeIdentifier(
680                         TestUtils.getNodeIdentifierPredicate("lst1", "test:module", "2014-01-09", "lf11",
681                                 "GigabitEthernet0/0/0/0"))
682                 .withChild(
683                         ImmutableLeafNodeBuilder.create()
684                                 .withNodeIdentifier(TestUtils.getNodeIdentifier("lf11", "test:module", "2014-01-09"))
685                                 .withValue("GigabitEthernet0/0/0/0").build()).build();
686
687     }
688
689     /**
690      * If includeWhiteChars URI parameter is set to false then no white characters can be included in returned output
691      *
692      * @throws UnsupportedEncodingException
693      */
694     @Test
695     public void getDataWithUriIncludeWhiteCharsParameterTest() throws UnsupportedEncodingException {
696         getDataWithUriIncludeWhiteCharsParameter("config");
697         getDataWithUriIncludeWhiteCharsParameter("operational");
698     }
699
700     private void getDataWithUriIncludeWhiteCharsParameter(final String target) throws UnsupportedEncodingException {
701         mockReadConfigurationDataMethod();
702         mockReadOperationalDataMethod();
703         final String uri = "/" + target + "/ietf-interfaces:interfaces/interface/eth0";
704         Response response = target(uri).queryParam("prettyPrint", "false").request("application/xml").get();
705         final String xmlData = response.readEntity(String.class);
706
707         Pattern pattern = Pattern.compile(".*(>\\s+|\\s+<).*", Pattern.DOTALL);
708         Matcher matcher = pattern.matcher(xmlData);
709         // XML element can't surrounded with white character (e.g ">    " or
710         // "    <")
711         assertFalse(matcher.matches());
712
713         response = target(uri).queryParam("prettyPrint", "false").request("application/json").get();
714         final String jsonData = response.readEntity(String.class);
715         pattern = Pattern.compile(".*(\\}\\s+|\\s+\\{|\\]\\s+|\\s+\\[|\\s+:|:\\s+).*", Pattern.DOTALL);
716         matcher = pattern.matcher(jsonData);
717         // JSON element can't surrounded with white character (e.g "} ", " {",
718         // "] ", " [", " :" or ": ")
719         assertFalse(matcher.matches());
720     }
721
722     /**
723      * Tests behavior when invalid value of depth URI parameter
724      */
725     @Test
726     @Ignore
727     public void getDataWithInvalidDepthParameterTest() {
728         setControllerContext(schemaContextModules);
729
730         final MultivaluedMap<String, String> paramMap = new MultivaluedHashMap<>();
731         paramMap.putSingle("depth", "1o");
732         final UriInfo mockInfo = mock(UriInfo.class);
733         when(mockInfo.getQueryParameters(false)).thenAnswer(new Answer<MultivaluedMap<String, String>>() {
734             @Override
735             public MultivaluedMap<String, String> answer(final InvocationOnMock invocation) {
736                 return paramMap;
737             }
738         });
739
740         getDataWithInvalidDepthParameterTest(mockInfo);
741
742         paramMap.putSingle("depth", "0");
743         getDataWithInvalidDepthParameterTest(mockInfo);
744
745         paramMap.putSingle("depth", "-1");
746         getDataWithInvalidDepthParameterTest(mockInfo);
747     }
748
749     private void getDataWithInvalidDepthParameterTest(final UriInfo uriInfo) {
750         try {
751             final QName qNameDepth1Cont = QName.create("urn:nested:module", "2014-06-3", "depth1-cont");
752             final YangInstanceIdentifier ii = YangInstanceIdentifier.builder().node(qNameDepth1Cont).build();
753             final NormalizedNode value = (Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(qNameDepth1Cont)).build());
754             when(brokerFacade.readConfigurationData(eq(ii))).thenReturn(value);
755             restconfImpl.readConfigurationData("nested-module:depth1-cont", uriInfo);
756             fail("Expected RestconfDocumentedException");
757         } catch (final RestconfDocumentedException e) {
758             assertTrue("Unexpected error message: " + e.getErrors().get(0).getErrorMessage(), e.getErrors().get(0)
759                     .getErrorMessage().contains("depth"));
760         }
761     }
762
763     private void verifyXMLResponse(final Response response, final NodeData nodeData) {
764         final Document doc = response.readEntity(Document.class);
765 //        Document doc = TestUtils.loadDocumentFrom((InputStream) response.getEntity());
766 //        System.out.println();
767         assertNotNull("Could not parse XML document", doc);
768
769         // System.out.println(TestUtils.getDocumentInPrintableForm( doc ));
770
771         verifyContainerElement(doc.getDocumentElement(), nodeData);
772     }
773
774     @SuppressWarnings("unchecked")
775     private void verifyContainerElement(final Element element, final NodeData nodeData) {
776
777         assertEquals("Element local name", nodeData.key, element.getLocalName());
778
779         final NodeList childNodes = element.getChildNodes();
780         if (nodeData.data == null) { // empty container
781             assertTrue("Expected no child elements for \"" + element.getLocalName() + "\"", childNodes.getLength() == 0);
782             return;
783         }
784
785         final Map<String, NodeData> expChildMap = Maps.newHashMap();
786         for (final NodeData expChild : (List<NodeData>) nodeData.data) {
787             expChildMap.put(expChild.key.toString(), expChild);
788         }
789
790         for (int i = 0; i < childNodes.getLength(); i++) {
791             final org.w3c.dom.Node actualChild = childNodes.item(i);
792             if (!(actualChild instanceof Element)) {
793                 continue;
794             }
795
796             final Element actualElement = (Element) actualChild;
797             final NodeData expChild = expChildMap.remove(actualElement.getLocalName());
798             assertNotNull(
799                     "Unexpected child element for parent \"" + element.getLocalName() + "\": "
800                             + actualElement.getLocalName(), expChild);
801
802             if (expChild.data == null || expChild.data instanceof List) {
803                 verifyContainerElement(actualElement, expChild);
804             } else {
805                 assertEquals("Text content for element: " + actualElement.getLocalName(), expChild.data,
806                         actualElement.getTextContent());
807             }
808         }
809
810         if (!expChildMap.isEmpty()) {
811             fail("Missing elements for parent \"" + element.getLocalName() + "\": " + expChildMap.keySet());
812         }
813     }
814
815     private NodeData expectContainer(final String name, final NodeData... childData) {
816         return new NodeData(name, Lists.newArrayList(childData));
817     }
818
819     private NodeData expectEmptyContainer(final String name) {
820         return new NodeData(name, null);
821     }
822
823     private NodeData expectLeaf(final String name, final Object value) {
824         return new NodeData(name, value);
825     }
826
827     private QName toNestedQName(final String localName) {
828         return QName.create("urn:nested:module", "2014-06-3", localName);
829     }
830
831     private NodeData toCompositeNodeData(final QName key, final NodeData... childData) {
832         return new NodeData(key, Lists.newArrayList(childData));
833     }
834
835     private NodeData toSimpleNodeData(final QName key, final Object value) {
836         return new NodeData(key, value);
837     }
838
839 }