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