Merge "Remove RaftReplicator and move hearbeat logic to the leader"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / InvokeRpcMethodTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2014 Brocade Communications Systems, Inc.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.controller.sal.restconf.impl.test;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertSame;
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.spy;
20 import static org.mockito.Mockito.when;
21
22 import com.google.common.base.Optional;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import java.io.FileNotFoundException;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33 import javax.ws.rs.core.MultivaluedHashMap;
34 import javax.ws.rs.core.MultivaluedMap;
35 import javax.ws.rs.core.UriInfo;
36 import org.junit.Before;
37 import org.junit.BeforeClass;
38 import org.junit.Test;
39 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
40 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
41 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
42 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
43 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
44 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
45 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
46 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
47 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
48 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.common.RpcError;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
53 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
54 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
55 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
56 import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
57 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
58 import org.opendaylight.yangtools.yang.model.api.Module;
59 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
60 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
61
62 public class InvokeRpcMethodTest {
63
64     private RestconfImpl restconfImpl = null;
65     private static ControllerContext controllerContext = null;
66     private static UriInfo uriInfo;
67
68     @BeforeClass
69     public static void init() throws FileNotFoundException {
70         Set<Module> allModules = new HashSet<Module>(TestUtils.loadModulesFrom("/full-versions/yangs"));
71         allModules.addAll(TestUtils.loadModulesFrom("/invoke-rpc"));
72         assertNotNull(allModules);
73         Module module = TestUtils.resolveModule("invoke-rpc-module", allModules);
74         assertNotNull(module);
75         SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
76         controllerContext = spy(ControllerContext.getInstance());
77         controllerContext.setSchemas(schemaContext);
78         uriInfo = mock(UriInfo.class);
79         MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
80         map.put("prettyPrint", Collections.singletonList("true"));
81         when(uriInfo.getQueryParameters(any(Boolean.class))).thenReturn(map);
82     }
83
84     @Before
85     public void initMethod() {
86         restconfImpl = RestconfImpl.getInstance();
87         restconfImpl.setControllerContext(controllerContext);
88     }
89
90     /**
91      * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc
92      * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from
93      * string - first argument).
94      */
95     @Test
96     public void invokeRpcMtethodTest() {
97         ControllerContext contContext = controllerContext;
98         try {
99             contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
100         } catch (URISyntaxException e) {
101             assertTrue("Uri wasn't created sucessfuly", false);
102         }
103
104         BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
105
106         RestconfImpl restconf = RestconfImpl.getInstance();
107         restconf.setBroker(mockedBrokerFacade);
108         restconf.setControllerContext(contContext);
109
110         CompositeNode payload = preparePayload();
111
112         when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
113                 Futures.<RpcResult<CompositeNode>> immediateFuture(RpcResultBuilder.<CompositeNode>success().build()));
114
115         StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
116         assertTrue(structData == null);
117
118     }
119
120     private CompositeNode preparePayload() {
121         MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(
122                 TestUtils.buildQName("cont", "nmspc", "2013-12-04"), null, null, ModifyAction.CREATE, null);
123         MutableSimpleNode<?> lf = NodeFactory.createMutableSimpleNode(
124                 TestUtils.buildQName("lf", "nmspc", "2013-12-04"), cont, "any value", ModifyAction.CREATE, null);
125         cont.getValue().add(lf);
126         cont.init();
127
128         return cont;
129     }
130
131     @Test
132     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
133         RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed().build();
134
135         BrokerFacade brokerFacade = mock(BrokerFacade.class);
136         when(
137                 brokerFacade.invokeRpc(
138                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
139                         any(CompositeNode.class))).thenReturn(
140                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
141
142         restconfImpl.setBroker(brokerFacade);
143
144         try {
145             restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
146             fail("Expected an exception to be thrown.");
147         } catch (RestconfDocumentedException e) {
148             verifyRestconfDocumentedException(e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
149                     Optional.<String> absent(), Optional.<String> absent());
150         }
151     }
152
153     void verifyRestconfDocumentedException(final RestconfDocumentedException e, final int index,
154             final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg,
155             final Optional<String> expAppTag) {
156         RestconfError actual = null;
157         try {
158             actual = e.getErrors().get(index);
159         } catch (ArrayIndexOutOfBoundsException ex) {
160             fail("RestconfError not found at index " + index);
161         }
162
163         assertEquals("getErrorType", expErrorType, actual.getErrorType());
164         assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
165         assertNotNull("getErrorMessage is null", actual.getErrorMessage());
166
167         if (expErrorMsg.isPresent()) {
168             assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
169         }
170
171         if (expAppTag.isPresent()) {
172             assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
173         }
174     }
175
176     @Test
177     public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
178         List<RpcError> rpcErrors = Arrays.asList(
179             RpcResultBuilder.newError( RpcError.ErrorType.TRANSPORT, "bogusTag", "foo" ),
180             RpcResultBuilder.newWarning( RpcError.ErrorType.RPC, "in-use", "bar",
181                                          "app-tag", null, null ) );
182
183         RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed()
184                                                               .withRpcErrors(rpcErrors).build();
185
186         BrokerFacade brokerFacade = mock(BrokerFacade.class);
187         when(
188                 brokerFacade.invokeRpc(
189                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
190                         any(CompositeNode.class))).thenReturn(
191                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
192
193         restconfImpl.setBroker(brokerFacade);
194
195         try {
196             restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
197             fail("Expected an exception to be thrown.");
198         } catch (RestconfDocumentedException e) {
199             verifyRestconfDocumentedException(e, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED, Optional.of("foo"),
200                     Optional.<String> absent());
201             verifyRestconfDocumentedException(e, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"),
202                     Optional.of("app-tag"));
203         }
204     }
205
206     @Test
207     public void testInvokeRpcWithNoPayload_Success() {
208         RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
209
210         BrokerFacade brokerFacade = mock(BrokerFacade.class);
211         when(
212                 brokerFacade.invokeRpc(
213                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
214                         any(CompositeNode.class))).thenReturn(
215                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
216
217         restconfImpl.setBroker(brokerFacade);
218
219         StructuredData output = restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
220         assertEquals(null, output);
221         // additional validation in the fact that the restconfImpl does not
222         // throw an exception.
223     }
224
225     @Test
226     public void testInvokeRpcMethodExpectingNoPayloadButProvidePayload() {
227         try {
228             restconfImpl.invokeRpc("toaster:cancel-toast", " a payload ", uriInfo);
229             fail("Expected an exception");
230         } catch (RestconfDocumentedException e) {
231             verifyRestconfDocumentedException(e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
232                     Optional.<String> absent(), Optional.<String> absent());
233         }
234     }
235
236     @Test
237     public void testInvokeRpcMethodWithBadMethodName() {
238         try {
239             restconfImpl.invokeRpc("toaster:bad-method", "", uriInfo);
240             fail("Expected an exception");
241         } catch (RestconfDocumentedException e) {
242             verifyRestconfDocumentedException(e, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
243                     Optional.<String> absent(), Optional.<String> absent());
244         }
245     }
246
247     @Test
248     public void testInvokeRpcMethodWithInput() {
249         RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
250
251         CompositeNode payload = mock(CompositeNode.class);
252
253         BrokerFacade brokerFacade = mock(BrokerFacade.class);
254         when(
255                 brokerFacade.invokeRpc(
256                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
257                         any(CompositeNode.class))).thenReturn(
258                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
259
260         restconfImpl.setBroker(brokerFacade);
261
262         StructuredData output = restconfImpl.invokeRpc("toaster:make-toast", payload, uriInfo);
263         assertEquals(null, output);
264         // additional validation in the fact that the restconfImpl does not
265         // throw an exception.
266     }
267
268     @Test
269     public void testThrowExceptionWhenSlashInModuleName() {
270         try {
271             restconfImpl.invokeRpc("toaster/slash", "", uriInfo);
272             fail("Expected an exception.");
273         } catch (RestconfDocumentedException e) {
274             verifyRestconfDocumentedException(e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
275                     Optional.<String> absent(), Optional.<String> absent());
276         }
277     }
278
279     @Test
280     public void testInvokeRpcWithNoPayloadWithOutput_Success() {
281         CompositeNode compositeNode = mock(CompositeNode.class);
282         RpcResult<CompositeNode> rpcResult =
283                                   RpcResultBuilder.<CompositeNode>success(compositeNode).build();
284
285         BrokerFacade brokerFacade = mock(BrokerFacade.class);
286         when(
287                 brokerFacade.invokeRpc(
288                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
289                         any(CompositeNode.class))).thenReturn(
290                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
291
292         restconfImpl.setBroker(brokerFacade);
293
294         StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "", uriInfo);
295         assertNotNull(output);
296         assertSame(compositeNode, output.getData());
297         assertNotNull(output.getSchema());
298     }
299
300     /**
301      *
302      * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context.
303      * This rpc is then executed.
304      *
305      * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is
306      * invoked.
307      */
308     @Test
309     public void testMountedRpcCallNoPayload_Success() throws Exception {
310         RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
311
312         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
313         when(mockListener.get()).thenReturn(rpcResult);
314
315         QName cancelToastQName = QName.create("namespace", "2014-05-28", "cancelToast");
316
317         RpcDefinition mockRpc = mock(RpcDefinition.class);
318         when(mockRpc.getQName()).thenReturn(cancelToastQName);
319
320         MountInstance mockMountPoint = mock(MountInstance.class);
321         when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
322
323         when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc"));
324
325         InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class);
326         when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
327
328         ControllerContext mockedContext = mock(ControllerContext.class);
329         String rpcNoop = "invoke-rpc-module:rpc-noop";
330         when(mockedContext.urlPathArgDecode(rpcNoop)).thenReturn(rpcNoop);
331         when(mockedContext.getRpcDefinition(rpcNoop)).thenReturn(mockRpc);
332         when(
333                 mockedContext.toMountPointIdentifier(eq("opendaylight-inventory:nodes/node/"
334                         + "REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop"))).thenReturn(mockedInstanceId);
335
336         restconfImpl.setControllerContext(mockedContext);
337         try {
338             restconfImpl.invokeRpc(
339                     "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop", "",
340                     uriInfo);
341             fail("RestconfDocumentedException wasn't raised");
342         } catch (RestconfDocumentedException e) {
343             List<RestconfError> errors = e.getErrors();
344             assertNotNull(errors);
345             assertEquals(1, errors.size());
346             assertEquals(ErrorType.APPLICATION, errors.iterator().next().getErrorType());
347             assertEquals(ErrorTag.OPERATION_FAILED, errors.iterator().next().getErrorTag());
348         }
349
350         // additional validation in the fact that the restconfImpl does not
351         // throw an exception.
352     }
353 }