c8821e8e7b842040e22b2fa051c051791941687d
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / services / impl / JSONRestconfServiceRfc8040ImplTest.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertThat;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Matchers.eq;
17 import static org.mockito.Matchers.isNull;
18 import static org.mockito.Matchers.notNull;
19 import static org.mockito.Mockito.doNothing;
20 import static org.mockito.Mockito.doReturn;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.verify;
23
24 import com.google.common.base.Optional;
25 import com.google.common.io.Resources;
26 import com.google.common.util.concurrent.Futures;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.nio.charset.StandardCharsets;
30 import java.util.List;
31 import java.util.Map;
32 import org.junit.Before;
33 import org.junit.BeforeClass;
34 import org.junit.Test;
35 import org.mockito.ArgumentCaptor;
36 import org.mockito.Mock;
37 import org.mockito.MockitoAnnotations;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
40 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
41 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
42 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
43 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
44 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
45 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
46 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
47 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
48 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
49 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
50 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
51 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
52 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
53 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
54 import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
55 import org.opendaylight.restconf.nb.rfc8040.TestUtils;
56 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
57 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
58 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
59 import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
60 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
61 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
62 import org.opendaylight.restconf.nb.rfc8040.services.wrapper.ServicesWrapperImpl;
63 import org.opendaylight.yangtools.yang.common.OperationFailedException;
64 import org.opendaylight.yangtools.yang.common.QName;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
69 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
71 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
75 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
76 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
77 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
78 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
79 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
80
81 /**
82  * Unit tests for JSONRestconfServiceDraft18.
83  *
84  * @author Thomas Pantelis
85  */
86 public class JSONRestconfServiceRfc8040ImplTest {
87     static final String IETF_INTERFACES_NS = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
88     static final String IETF_INTERFACES_VERSION = "2013-07-04";
89     static final QName INTERFACES_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "interfaces");
90     static final QName INTERFACE_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "interface");
91     static final QName NAME_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "name");
92     static final QName TYPE_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "type");
93     static final QName ENABLED_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "enabled");
94     static final QName DESC_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "description");
95
96     static final String TEST_MODULE_NS = "test:module";
97     static final String TEST_MODULE_VERSION = "2014-01-09";
98     static final QName TEST_CONT_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "cont");
99     static final QName TEST_CONT1_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "cont1");
100     static final QName TEST_LF11_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "lf11");
101     static final QName TEST_LF12_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "lf12");
102
103     static final String TOASTER_MODULE_NS = "http://netconfcentral.org/ns/toaster";
104     static final String TOASTER_MODULE_VERSION = "2009-11-20";
105     static final QName TOASTER_DONENESS_QNAME =
106             QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "toasterDoneness");
107     static final QName TOASTER_TYPE_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "toasterToastType");
108     static final QName WHEAT_BREAD_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "wheat-bread");
109     static final QName MAKE_TOAST_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "make-toast");
110     static final QName CANCEL_TOAST_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "cancel-toast");
111     static final QName TEST_OUTPUT_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "testOutput");
112     static final QName TEXT_OUT_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "textOut");
113
114     private static SchemaContext schemaContext;
115
116     @Mock
117     private DOMTransactionChain mockTxChain;
118
119     @Mock
120     private DOMDataReadWriteTransaction mockReadWriteTx;
121
122     @Mock
123     private DOMDataReadOnlyTransaction mockReadOnlyTx;
124
125     @Mock
126     private DOMDataWriteTransaction mockWriteTx;
127
128     @Mock
129     private DOMMountPointService mockMountPointService;
130
131     @Mock
132     private SchemaContextHandler mockSchemaContextHandler;
133
134     @Mock
135     private DOMDataBroker mockDOMDataBroker;
136
137     @Mock
138     private DOMRpcService mockRpcService;
139
140     @Mock
141     private DOMSchemaService domSchemaService;
142
143     private JSONRestconfServiceRfc8040Impl service;
144
145     @BeforeClass
146     public static void init() throws IOException, ReactorException {
147         schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs");
148         SchemaContextHandler.setSchemaContext(schemaContext);
149     }
150
151     @SuppressWarnings("resource")
152     @Before
153     public void setup() throws Exception {
154         MockitoAnnotations.initMocks(this);
155
156         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockReadOnlyTx).read(
157                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
158
159         doNothing().when(mockWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
160                 any(NormalizedNode.class));
161         doNothing().when(mockWriteTx).merge(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
162                 any(NormalizedNode.class));
163         doNothing().when(mockWriteTx).delete(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
164         doReturn(Futures.immediateCheckedFuture(null)).when(mockWriteTx).submit();
165
166         doNothing().when(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
167                 any(NormalizedNode.class));
168         doReturn(Futures.immediateCheckedFuture(null)).when(mockReadWriteTx).submit();
169         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockReadWriteTx).read(
170                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
171         doReturn(Futures.immediateCheckedFuture(Boolean.FALSE)).when(mockReadWriteTx).exists(
172                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
173
174         doReturn(mockReadOnlyTx).when(mockTxChain).newReadOnlyTransaction();
175         doReturn(mockReadWriteTx).when(mockTxChain).newReadWriteTransaction();
176         doReturn(mockWriteTx).when(mockTxChain).newWriteOnlyTransaction();
177
178         doReturn(mockTxChain).when(mockDOMDataBroker).createTransactionChain(any());
179
180         doReturn(schemaContext).when(mockSchemaContextHandler).get();
181
182         final TransactionChainHandler txChainHandler = new TransactionChainHandler(mockDOMDataBroker);
183
184         final DOMMountPointServiceHandler mountPointServiceHandler =
185                 new DOMMountPointServiceHandler(mockMountPointService);
186
187         final DOMNotificationService mockNotificationService = mock(DOMNotificationService.class);
188         ServicesWrapperImpl.getInstance().setHandlers(mockSchemaContextHandler, mountPointServiceHandler,
189                 txChainHandler, new DOMDataBrokerHandler(mockDOMDataBroker),
190                 new RpcServiceHandler(mockRpcService),
191                 new NotificationServiceHandler(mockNotificationService), domSchemaService);
192
193         service = new JSONRestconfServiceRfc8040Impl(ServicesWrapperImpl.getInstance(), mountPointServiceHandler);
194
195         new RestConnectorProvider<>(mockDOMDataBroker, domSchemaService, mockRpcService, mockNotificationService,
196                 mockMountPointService, txChainHandler, null).start();
197
198         SchemaContextHandler.setSchemaContext(schemaContext);
199     }
200
201     private static String loadData(final String path) throws IOException {
202         return Resources.asCharSource(JSONRestconfServiceRfc8040ImplTest.class.getResource(path),
203                 StandardCharsets.UTF_8).read();
204     }
205
206     @SuppressWarnings("rawtypes")
207     @Test
208     public void testPut() throws Exception {
209         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
210         final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
211
212         this.service.put(uriPath, payload);
213
214         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
215                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
216         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
217
218         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
219                 capturedNode.capture());
220
221         verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
222                 new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
223
224         assertTrue("Expected MapEntryNode. Actual " + capturedNode.getValue().getClass(),
225                 capturedNode.getValue() instanceof MapEntryNode);
226         final MapEntryNode actualNode = (MapEntryNode) capturedNode.getValue();
227         assertEquals("MapEntryNode node type", INTERFACE_QNAME, actualNode.getNodeType());
228         verifyLeafNode(actualNode, NAME_QNAME, "eth0");
229         verifyLeafNode(actualNode, TYPE_QNAME, "ethernetCsmacd");
230         verifyLeafNode(actualNode, ENABLED_QNAME, Boolean.FALSE);
231         verifyLeafNode(actualNode, DESC_QNAME, "some interface");
232     }
233
234     @SuppressWarnings("rawtypes")
235     @Test
236     public void testPutBehindMountPoint() throws Exception {
237         setupTestMountPoint();
238
239         final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
240         final String payload = loadData("/full-versions/testCont1Data.json");
241
242         this.service.put(uriPath, payload);
243
244         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
245                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
246         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
247
248         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
249                 capturedNode.capture());
250
251         verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
252
253         assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode);
254         final ContainerNode actualNode = (ContainerNode) capturedNode.getValue();
255         assertEquals("ContainerNode node type", TEST_CONT1_QNAME, actualNode.getNodeType());
256         verifyLeafNode(actualNode, TEST_LF11_QNAME, "lf11 data");
257         verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data");
258     }
259
260     @Test(expected = OperationFailedException.class)
261     @SuppressWarnings("checkstyle:IllegalThrows")
262     public void testPutFailure() throws Throwable {
263         doReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")))
264                 .when(mockReadWriteTx).submit();
265
266         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
267         final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
268
269         this.service.put(uriPath, payload);
270     }
271
272     @SuppressWarnings("rawtypes")
273     @Test
274     public void testPost() throws Exception {
275         final String uriPath = null;
276         final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
277
278         this.service.post(uriPath, payload);
279
280         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
281                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
282         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
283
284         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
285                 capturedNode.capture());
286
287         verifyPath(capturedPath.getValue(), INTERFACES_QNAME);
288
289         assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode);
290         final ContainerNode actualNode = (ContainerNode) capturedNode.getValue();
291         assertEquals("ContainerNode node type", INTERFACES_QNAME, actualNode.getNodeType());
292
293         final java.util.Optional<DataContainerChild<?, ?>> mapChild = actualNode.getChild(
294             new NodeIdentifier(INTERFACE_QNAME));
295         assertEquals(INTERFACE_QNAME.toString() + " present", true, mapChild.isPresent());
296         assertTrue("Expected MapNode. Actual " + mapChild.get().getClass(), mapChild.get() instanceof MapNode);
297         final MapNode mapNode = (MapNode)mapChild.get();
298
299         final NodeIdentifierWithPredicates entryNodeID = new NodeIdentifierWithPredicates(
300                 INTERFACE_QNAME, NAME_QNAME, "eth0");
301         final java.util.Optional<MapEntryNode> entryChild = mapNode.getChild(entryNodeID);
302         assertEquals(entryNodeID.toString() + " present", true, entryChild.isPresent());
303         final MapEntryNode entryNode = entryChild.get();
304         verifyLeafNode(entryNode, NAME_QNAME, "eth0");
305         verifyLeafNode(entryNode, TYPE_QNAME, "ethernetCsmacd");
306         verifyLeafNode(entryNode, ENABLED_QNAME, Boolean.FALSE);
307         verifyLeafNode(entryNode, DESC_QNAME, "some interface");
308     }
309
310     @SuppressWarnings("rawtypes")
311     @Test
312     public void testPostBehindMountPoint() throws Exception {
313         setupTestMountPoint();
314
315         final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont";
316         final String payload = loadData("/full-versions/testCont1Data.json");
317
318         this.service.post(uriPath, payload);
319
320         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
321                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
322         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
323
324         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
325                 capturedNode.capture());
326
327         verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
328
329         assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode);
330         final ContainerNode actualNode = (ContainerNode) capturedNode.getValue();
331         assertEquals("ContainerNode node type", TEST_CONT1_QNAME, actualNode.getNodeType());
332         verifyLeafNode(actualNode, TEST_LF11_QNAME, "lf11 data");
333         verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data");
334     }
335
336     @Test(expected = TransactionCommitFailedException.class)
337     @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
338     public void testPostFailure() throws Throwable {
339         doReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")))
340                 .when(mockReadWriteTx).submit();
341
342         final String uriPath = null;
343         final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
344
345         try {
346             this.service.post(uriPath, payload);
347         } catch (final OperationFailedException e) {
348             assertNotNull(e.getCause());
349             throw e.getCause();
350         }
351     }
352
353     @SuppressWarnings("rawtypes")
354     @Test
355     public void testPatch() throws Exception {
356         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
357         final String payload = loadData("/parts/ietf-interfaces_interfaces_patch.json");
358
359         final Optional<String> patchResult = this.service.patch(uriPath, payload);
360
361         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
362                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
363         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
364
365         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
366                 capturedNode.capture());
367
368         verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
369                 new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
370
371         assertTrue("Expected MapEntryNode. Actual " + capturedNode.getValue().getClass(),
372                 capturedNode.getValue() instanceof MapEntryNode);
373         final MapEntryNode actualNode = (MapEntryNode) capturedNode.getValue();
374         assertEquals("MapEntryNode node type", INTERFACE_QNAME, actualNode.getNodeType());
375         verifyLeafNode(actualNode, NAME_QNAME, "eth0");
376         verifyLeafNode(actualNode, TYPE_QNAME, "ethernetCsmacd");
377         verifyLeafNode(actualNode, ENABLED_QNAME, Boolean.FALSE);
378         verifyLeafNode(actualNode, DESC_QNAME, "some interface");
379         assertTrue(patchResult.get().contains("\"ok\":[null]"));
380     }
381
382     @SuppressWarnings("rawtypes")
383     @Test
384     public void testPatchBehindMountPoint() throws Exception {
385         setupTestMountPoint();
386
387         final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
388         final String payload = loadData("/full-versions/testCont1DataPatch.json");
389
390         final Optional<String> patchResult = this.service.patch(uriPath, payload);
391
392         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
393                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
394         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
395
396         verify(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture(),
397                 capturedNode.capture());
398
399         verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
400
401         assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode);
402         final ContainerNode actualNode = (ContainerNode) capturedNode.getValue();
403         assertEquals("ContainerNode node type", TEST_CONT1_QNAME, actualNode.getNodeType());
404         verifyLeafNode(actualNode, TEST_LF11_QNAME, "lf11 data");
405         verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data");
406         assertTrue(patchResult.get().contains("\"ok\":[null]"));
407     }
408
409     @Test
410     @SuppressWarnings("checkstyle:IllegalThrows")
411     public void testPatchFailure() throws Throwable {
412         doReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")))
413                 .when(mockReadWriteTx).submit();
414
415         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
416
417         final String payload = loadData("/parts/ietf-interfaces_interfaces_patch.json");
418
419         final Optional<String> patchResult = this.service.patch(uriPath, payload);
420         assertTrue("Patch output is not null", patchResult.isPresent());
421         String patch = patchResult.get();
422         assertTrue(patch.contains("mock"));
423     }
424
425     @Test
426     public void testDelete() throws Exception {
427         doReturn(Futures.immediateCheckedFuture(Boolean.TRUE)).when(mockReadWriteTx).exists(
428                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
429
430         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
431
432         this.service.delete(uriPath);
433
434         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
435                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
436
437         verify(mockReadWriteTx).delete(eq(LogicalDatastoreType.CONFIGURATION), capturedPath.capture());
438
439         verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
440                 new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
441     }
442
443     @Test(expected = OperationFailedException.class)
444     public void testDeleteFailure() throws Exception {
445         final String invalidUriPath = "ietf-interfaces:interfaces/invalid";
446
447         this.service.delete(invalidUriPath);
448     }
449
450     @Test
451     public void testGetConfig() throws Exception {
452         testGet(LogicalDatastoreType.CONFIGURATION);
453     }
454
455     @Test
456     public void testGetOperational() throws Exception {
457         testGet(LogicalDatastoreType.OPERATIONAL);
458     }
459
460     @Test
461     public void testGetWithNoData() throws OperationFailedException {
462         final String uriPath = "ietf-interfaces:interfaces";
463         this.service.get(uriPath, LogicalDatastoreType.CONFIGURATION);
464     }
465
466     @Test(expected = OperationFailedException.class)
467     public void testGetFailure() throws Exception {
468         final String invalidUriPath = "/ietf-interfaces:interfaces/invalid";
469         this.service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION);
470     }
471
472     @SuppressWarnings("rawtypes")
473     @Test
474     public void testInvokeRpcWithInput() throws Exception {
475         final SchemaPath path = SchemaPath.create(true, MAKE_TOAST_QNAME);
476
477         final DOMRpcResult expResult = new DefaultDOMRpcResult((NormalizedNode<?, ?>)null);
478         doReturn(Futures.immediateCheckedFuture(expResult)).when(mockRpcService).invokeRpc(eq(path),
479                 any(NormalizedNode.class));
480
481         final String uriPath = "toaster:make-toast";
482         final String input = loadData("/full-versions/make-toast-rpc-input.json");
483
484         final Optional<String> output = this.service.invokeRpc(uriPath, Optional.of(input));
485
486         assertEquals("Output present", false, output.isPresent());
487
488         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
489         verify(mockRpcService).invokeRpc(eq(path), capturedNode.capture());
490
491         assertTrue("Expected ContainerNode. Actual " + capturedNode.getValue().getClass(),
492                 capturedNode.getValue() instanceof ContainerNode);
493         final ContainerNode actualNode = (ContainerNode) capturedNode.getValue();
494         verifyLeafNode(actualNode, TOASTER_DONENESS_QNAME, Long.valueOf(10));
495         verifyLeafNode(actualNode, TOASTER_TYPE_QNAME, WHEAT_BREAD_QNAME);
496     }
497
498     @Test
499     public void testInvokeRpcWithNoInput() throws Exception {
500         final SchemaPath path = SchemaPath.create(true, CANCEL_TOAST_QNAME);
501
502         final DOMRpcResult expResult = new DefaultDOMRpcResult((NormalizedNode<?, ?>)null);
503         doReturn(Futures.immediateCheckedFuture(expResult)).when(mockRpcService).invokeRpc(eq(path),
504                 any(NormalizedNode.class));
505
506         final String uriPath = "toaster:cancel-toast";
507
508         final Optional<String> output = this.service.invokeRpc(uriPath, Optional.<String>absent());
509
510         assertEquals("Output present", false, output.isPresent());
511
512         verify(mockRpcService).invokeRpc(eq(path), isNull(NormalizedNode.class));
513     }
514
515     @Test
516     public void testInvokeRpcWithOutput() throws Exception {
517         final SchemaPath path = SchemaPath.create(true, TEST_OUTPUT_QNAME);
518
519         final NormalizedNode<?, ?> outputNode = ImmutableContainerNodeBuilder.create()
520                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TEST_OUTPUT_QNAME))
521                 .withChild(ImmutableNodes.leafNode(TEXT_OUT_QNAME, "foo")).build();
522         final DOMRpcResult expResult = new DefaultDOMRpcResult(outputNode);
523         doReturn(Futures.immediateCheckedFuture(expResult)).when(mockRpcService).invokeRpc(eq(path),
524                 any(NormalizedNode.class));
525
526         final String uriPath = "toaster:testOutput";
527
528         final Optional<String> output = this.service.invokeRpc(uriPath, Optional.<String>absent());
529
530         assertEquals("Output present", true, output.isPresent());
531         assertNotNull("Returned null response", output.get());
532         assertThat("Missing \"textOut\"", output.get(), containsString("\"textOut\":\"foo\""));
533
534         verify(mockRpcService).invokeRpc(eq(path), isNull(NormalizedNode.class));
535     }
536
537     @Test(expected = OperationFailedException.class)
538     public void testInvokeRpcFailure() throws Exception {
539         final DOMRpcException exception = new DOMRpcImplementationNotAvailableException("testExeption");
540         doReturn(Futures.immediateFailedCheckedFuture(exception)).when(mockRpcService).invokeRpc(any(SchemaPath.class),
541                 any(NormalizedNode.class));
542
543         final String uriPath = "toaster:cancel-toast";
544
545         this.service.invokeRpc(uriPath, Optional.<String>absent());
546     }
547
548     void testGet(final LogicalDatastoreType datastoreType) throws OperationFailedException {
549         final MapEntryNode entryNode = ImmutableNodes.mapEntryBuilder(INTERFACE_QNAME, NAME_QNAME, "eth0")
550                 .withChild(ImmutableNodes.leafNode(NAME_QNAME, "eth0"))
551                 .withChild(ImmutableNodes.leafNode(TYPE_QNAME, "ethernetCsmacd"))
552                 .withChild(ImmutableNodes.leafNode(ENABLED_QNAME, Boolean.TRUE))
553                 .withChild(ImmutableNodes.leafNode(DESC_QNAME, "eth interface"))
554                 .build();
555
556         doReturn(Futures.immediateCheckedFuture(Optional.of(entryNode))).when(mockReadOnlyTx).read(
557                 eq(datastoreType), any(YangInstanceIdentifier.class));
558
559         final String uriPath = "ietf-interfaces:interfaces/interface=eth0";
560
561         final Optional<String> optionalResp = this.service.get(uriPath, datastoreType);
562         assertEquals("Response present", true, optionalResp.isPresent());
563         final String jsonResp = optionalResp.get();
564
565         assertNotNull("Returned null response", jsonResp);
566         assertThat("Missing \"name\"", jsonResp, containsString("\"name\":\"eth0\""));
567         assertThat("Missing \"type\"", jsonResp, containsString("\"type\":\"ethernetCsmacd\""));
568         assertThat("Missing \"enabled\"", jsonResp, containsString("\"enabled\":true"));
569         assertThat("Missing \"description\"", jsonResp, containsString("\"description\":\"eth interface\""));
570
571         final ArgumentCaptor<YangInstanceIdentifier> capturedPath =
572                 ArgumentCaptor.forClass(YangInstanceIdentifier.class);
573         verify(mockReadOnlyTx).read(eq(datastoreType), capturedPath.capture());
574
575         verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
576                 new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
577     }
578
579     DOMMountPoint setupTestMountPoint() throws FileNotFoundException, ReactorException {
580         final SchemaContext schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module");
581         final DOMMountPoint mockMountPoint = mock(DOMMountPoint.class);
582         doReturn(schemaContextTestModule).when(mockMountPoint).getSchemaContext();
583
584         doReturn(Optional.of(mockDOMDataBroker)).when(mockMountPoint).getService(DOMDataBroker.class);
585
586         doReturn(Optional.of(mockMountPoint))
587                 .when(mockMountPointService).getMountPoint(notNull(YangInstanceIdentifier.class));
588
589         return mockMountPoint;
590     }
591
592     void verifyLeafNode(final DataContainerNode<?> parent, final QName leafType, final Object leafValue) {
593         final java.util.Optional<DataContainerChild<?, ?>> leafChild = parent.getChild(new NodeIdentifier(leafType));
594         assertEquals(leafType.toString() + " present", true, leafChild.isPresent());
595         assertEquals(leafType.toString() + " value", leafValue, leafChild.get().getValue());
596     }
597
598     void verifyPath(final YangInstanceIdentifier path, final Object... expArgs) {
599         final List<PathArgument> pathArgs = path.getPathArguments();
600         assertEquals("Arg count for actual path " + path, expArgs.length, pathArgs.size());
601         int index = 0;
602         for (final PathArgument actual: pathArgs) {
603             QName expNodeType;
604             if (expArgs[index] instanceof Object[]) {
605                 final Object[] listEntry = (Object[]) expArgs[index];
606                 expNodeType = (QName) listEntry[0];
607
608                 assertTrue(actual instanceof NodeIdentifierWithPredicates);
609                 final Map<QName, Object> keyValues = ((NodeIdentifierWithPredicates)actual).getKeyValues();
610                 assertEquals(String.format("Path arg %d keyValues size", index + 1), 1, keyValues.size());
611                 final QName expKey = (QName) listEntry[1];
612                 assertEquals(String.format("Path arg %d keyValue for %s", index + 1, expKey), listEntry[2],
613                         keyValues.get(expKey));
614             } else {
615                 expNodeType = (QName) expArgs[index];
616             }
617
618             assertEquals(String.format("Path arg %d node type", index + 1), expNodeType, actual.getNodeType());
619             index++;
620         }
621
622     }
623 }