2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2014 Brocade Communications Systems, Inc.
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
9 package org.opendaylight.controller.sal.restconf.impl.test;
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;
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;
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;
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;
62 public class InvokeRpcMethodTest {
64 private RestconfImpl restconfImpl = null;
65 private static ControllerContext controllerContext = null;
66 private static UriInfo uriInfo;
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);
85 public void initMethod() {
86 restconfImpl = RestconfImpl.getInstance();
87 restconfImpl.setControllerContext(controllerContext);
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).
96 public void invokeRpcMtethodTest() {
97 ControllerContext contContext = controllerContext;
99 contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
100 } catch (URISyntaxException e) {
101 assertTrue("Uri wasn't created sucessfuly", false);
104 BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
106 RestconfImpl restconf = RestconfImpl.getInstance();
107 restconf.setBroker(mockedBrokerFacade);
108 restconf.setControllerContext(contContext);
110 CompositeNode payload = preparePayload();
112 when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
113 Futures.<RpcResult<CompositeNode>> immediateFuture(RpcResultBuilder.<CompositeNode>success().build()));
115 StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
116 assertTrue(structData == null);
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);
132 public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
133 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed().build();
135 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
142 restconfImpl.setBroker(brokerFacade);
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());
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;
158 actual = e.getErrors().get(index);
159 } catch (ArrayIndexOutOfBoundsException ex) {
160 fail("RestconfError not found at index " + index);
163 assertEquals("getErrorType", expErrorType, actual.getErrorType());
164 assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
165 assertNotNull("getErrorMessage is null", actual.getErrorMessage());
167 if (expErrorMsg.isPresent()) {
168 assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
171 if (expAppTag.isPresent()) {
172 assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
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 ) );
183 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed()
184 .withRpcErrors(rpcErrors).build();
186 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
193 restconfImpl.setBroker(brokerFacade);
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"));
207 public void testInvokeRpcWithNoPayload_Success() {
208 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
210 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
217 restconfImpl.setBroker(brokerFacade);
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.
226 public void testInvokeRpcMethodExpectingNoPayloadButProvidePayload() {
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());
237 public void testInvokeRpcMethodWithBadMethodName() {
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());
248 public void testInvokeRpcMethodWithInput() {
249 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
251 CompositeNode payload = mock(CompositeNode.class);
253 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
260 restconfImpl.setBroker(brokerFacade);
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.
269 public void testThrowExceptionWhenSlashInModuleName() {
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());
280 public void testInvokeRpcWithNoPayloadWithOutput_Success() {
281 CompositeNode compositeNode = mock(CompositeNode.class);
282 RpcResult<CompositeNode> rpcResult =
283 RpcResultBuilder.<CompositeNode>success(compositeNode).build();
285 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
292 restconfImpl.setBroker(brokerFacade);
294 StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "", uriInfo);
295 assertNotNull(output);
296 assertSame(compositeNode, output.getData());
297 assertNotNull(output.getSchema());
302 * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context.
303 * This rpc is then executed.
305 * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is
309 public void testMountedRpcCallNoPayload_Success() throws Exception {
310 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
312 ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
313 when(mockListener.get()).thenReturn(rpcResult);
315 QName cancelToastQName = QName.create("namespace", "2014-05-28", "cancelToast");
317 RpcDefinition mockRpc = mock(RpcDefinition.class);
318 when(mockRpc.getQName()).thenReturn(cancelToastQName);
320 MountInstance mockMountPoint = mock(MountInstance.class);
321 when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
323 when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc"));
325 InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class);
326 when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
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);
333 mockedContext.toMountPointIdentifier(eq("opendaylight-inventory:nodes/node/"
334 + "REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop"))).thenReturn(mockedInstanceId);
336 restconfImpl.setControllerContext(mockedContext);
338 restconfImpl.invokeRpc(
339 "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop", "",
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());
350 // additional validation in the fact that the restconfImpl does not
351 // throw an exception.