Apply style rules on whole sal-rest-connector
[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.common.util.RpcErrors;
40 import org.opendaylight.controller.sal.common.util.Rpcs;
41 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
42 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
43 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
44 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
45 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
46 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
47 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
48 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
49 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
50 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
51 import org.opendaylight.yangtools.yang.common.QName;
52 import org.opendaylight.yangtools.yang.common.RpcError;
53 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
54 import org.opendaylight.yangtools.yang.common.RpcResult;
55 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
56 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
57 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
58 import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
59 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
60 import org.opendaylight.yangtools.yang.model.api.Module;
61 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
62 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
63
64 public class InvokeRpcMethodTest {
65
66     private RestconfImpl restconfImpl = null;
67     private static ControllerContext controllerContext = null;
68     private static UriInfo uriInfo;
69
70     @BeforeClass
71     public static void init() throws FileNotFoundException {
72         Set<Module> allModules = new HashSet<Module>(TestUtils.loadModulesFrom("/full-versions/yangs"));
73         allModules.addAll(TestUtils.loadModulesFrom("/invoke-rpc"));
74         assertNotNull(allModules);
75         Module module = TestUtils.resolveModule("invoke-rpc-module", allModules);
76         assertNotNull(module);
77         SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
78         controllerContext = spy(ControllerContext.getInstance());
79         controllerContext.setSchemas(schemaContext);
80         uriInfo = mock(UriInfo.class);
81         MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
82         map.put("prettyPrint", Collections.singletonList("true"));
83         when(uriInfo.getQueryParameters(any(Boolean.class))).thenReturn(map);
84     }
85
86     @Before
87     public void initMethod() {
88         restconfImpl = RestconfImpl.getInstance();
89         restconfImpl.setControllerContext(controllerContext);
90     }
91
92     /**
93      * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc
94      * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from
95      * string - first argument).
96      */
97     @Test
98     public void invokeRpcMtethodTest() {
99         ControllerContext contContext = controllerContext;
100         try {
101             contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
102         } catch (URISyntaxException e) {
103             assertTrue("Uri wasn't created sucessfuly", false);
104         }
105
106         BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
107
108         RestconfImpl restconf = RestconfImpl.getInstance();
109         restconf.setBroker(mockedBrokerFacade);
110         restconf.setControllerContext(contContext);
111
112         CompositeNode payload = preparePayload();
113
114         when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
115                 Futures.<RpcResult<CompositeNode>> immediateFuture(Rpcs.<CompositeNode> getRpcResult(true)));
116
117         StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
118         assertTrue(structData == null);
119
120     }
121
122     private CompositeNode preparePayload() {
123         MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(
124                 TestUtils.buildQName("cont", "nmspc", "2013-12-04"), null, null, ModifyAction.CREATE, null);
125         MutableSimpleNode<?> lf = NodeFactory.createMutableSimpleNode(
126                 TestUtils.buildQName("lf", "nmspc", "2013-12-04"), cont, "any value", ModifyAction.CREATE, null);
127         cont.getValue().add(lf);
128         cont.init();
129
130         return cont;
131     }
132
133     @Test
134     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
135         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false);
136
137         BrokerFacade brokerFacade = mock(BrokerFacade.class);
138         when(
139                 brokerFacade.invokeRpc(
140                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
141                         any(CompositeNode.class))).thenReturn(
142                 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
143
144         restconfImpl.setBroker(brokerFacade);
145
146         try {
147             restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
148             fail("Expected an exception to be thrown.");
149         } catch (RestconfDocumentedException e) {
150             verifyRestconfDocumentedException(e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
151                     Optional.<String> absent(), Optional.<String> absent());
152         }
153     }
154
155     void verifyRestconfDocumentedException(final RestconfDocumentedException e, final int index,
156             final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg,
157             final Optional<String> expAppTag) {
158         RestconfError actual = null;
159         try {
160             actual = e.getErrors().get(index);
161         } catch (ArrayIndexOutOfBoundsException ex) {
162             fail("RestconfError not found at index " + index);
163         }
164
165         assertEquals("getErrorType", expErrorType, actual.getErrorType());
166         assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
167         assertNotNull("getErrorMessage is null", actual.getErrorMessage());
168
169         if (expErrorMsg.isPresent()) {
170             assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
171         }
172
173         if (expAppTag.isPresent()) {
174             assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
175         }
176     }
177
178     @Test
179     public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
180         List<RpcError> rpcErrors = Arrays.asList(RpcErrors.getRpcError(null, "bogusTag", null, ErrorSeverity.ERROR,
181                 "foo", RpcError.ErrorType.TRANSPORT, null), RpcErrors.getRpcError("app-tag", "in-use", null,
182                 ErrorSeverity.WARNING, "bar", RpcError.ErrorType.RPC, null));
183
184         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false, rpcErrors);
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 = Rpcs.<CompositeNode> getRpcResult(true);
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 = Rpcs.<CompositeNode> getRpcResult(true);
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 = Rpcs.<CompositeNode> getRpcResult(true, compositeNode,
283                 Collections.<RpcError> emptyList());
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     @Test
301     public void testMountedRpcCallNoPayload_Success() throws Exception {
302         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true);
303
304         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
305         when(mockListener.get()).thenReturn(rpcResult);
306
307         QName cancelToastQName = QName.create("namespace", "2014-05-28", "cancelToast");
308
309         RpcDefinition mockRpc = mock(RpcDefinition.class);
310         when(mockRpc.getQName()).thenReturn(cancelToastQName);
311
312         MountInstance mockMountPoint = mock(MountInstance.class);
313         when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
314
315         InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class);
316         when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
317
318         ControllerContext mockedContext = mock(ControllerContext.class);
319         String cancelToastStr = "toaster:cancel-toast";
320         when(mockedContext.urlPathArgDecode(cancelToastStr)).thenReturn(cancelToastStr);
321         when(mockedContext.getRpcDefinition(cancelToastStr)).thenReturn(mockRpc);
322         when(
323                 mockedContext.toMountPointIdentifier("opendaylight-inventory:nodes/node/"
324                         + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast")).thenReturn(mockedInstanceId);
325
326         restconfImpl.setControllerContext(mockedContext);
327         StructuredData output = restconfImpl.invokeRpc(
328                 "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/toaster:cancel-toast", "", uriInfo);
329         assertEquals(null, output);
330
331         // additional validation in the fact that the restconfImpl does not
332         // throw an exception.
333     }
334 }