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.md.sal.dom.api.DOMMountPoint;
40 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
41 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
42 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
43 import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
44 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
45 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
46 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
47 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
48 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
49 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
50 import org.opendaylight.yangtools.yang.common.QName;
51 import org.opendaylight.yangtools.yang.common.RpcError;
52 import org.opendaylight.yangtools.yang.common.RpcResult;
53 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
54 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
55 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
56 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
57 import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
58 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
59 import org.opendaylight.yangtools.yang.model.api.Module;
60 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
61 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
63 public class InvokeRpcMethodTest {
65 private RestconfImpl restconfImpl = null;
66 private static ControllerContext controllerContext = null;
67 private static UriInfo uriInfo;
70 public static void init() throws FileNotFoundException {
71 Set<Module> allModules = new HashSet<Module>(TestUtils.loadModulesFrom("/full-versions/yangs"));
72 allModules.addAll(TestUtils.loadModulesFrom("/invoke-rpc"));
73 assertNotNull(allModules);
74 Module module = TestUtils.resolveModule("invoke-rpc-module", allModules);
75 assertNotNull(module);
76 SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
77 controllerContext = spy(ControllerContext.getInstance());
78 controllerContext.setSchemas(schemaContext);
79 uriInfo = mock(UriInfo.class);
80 MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
81 map.put("prettyPrint", Collections.singletonList("true"));
82 when(uriInfo.getQueryParameters(any(Boolean.class))).thenReturn(map);
86 public void initMethod() {
87 restconfImpl = RestconfImpl.getInstance();
88 restconfImpl.setControllerContext(controllerContext);
92 * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc
93 * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from
94 * string - first argument).
97 public void invokeRpcMethodTest() {
98 ControllerContext contContext = controllerContext;
100 contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
101 } catch (URISyntaxException e) {
102 assertTrue("Uri wasn't created sucessfuly", false);
105 BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
107 RestconfImpl restconf = RestconfImpl.getInstance();
108 restconf.setBroker(mockedBrokerFacade);
109 restconf.setControllerContext(contContext);
111 CompositeNode payload = preparePayload();
113 when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
114 Futures.<RpcResult<CompositeNode>> immediateFuture(RpcResultBuilder.<CompositeNode>success().build()));
116 StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
117 assertTrue(structData == null);
121 private CompositeNode preparePayload() {
122 MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(
123 TestUtils.buildQName("cont", "nmspc", "2013-12-04"), null, null, ModifyAction.CREATE, null);
124 MutableSimpleNode<?> lf = NodeFactory.createMutableSimpleNode(
125 TestUtils.buildQName("lf", "nmspc", "2013-12-04"), cont, "any value", ModifyAction.CREATE, null);
126 cont.getValue().add(lf);
133 public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
134 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed().build();
136 BrokerFacade brokerFacade = mock(BrokerFacade.class);
138 brokerFacade.invokeRpc(
139 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
140 any(CompositeNode.class))).thenReturn(
141 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
143 restconfImpl.setBroker(brokerFacade);
146 restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
147 fail("Expected an exception to be thrown.");
148 } catch (RestconfDocumentedException e) {
149 verifyRestconfDocumentedException(e, 0, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
150 Optional.<String> absent(), Optional.<String> absent());
154 void verifyRestconfDocumentedException(final RestconfDocumentedException e, final int index,
155 final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg,
156 final Optional<String> expAppTag) {
157 RestconfError actual = null;
159 actual = e.getErrors().get(index);
160 } catch (ArrayIndexOutOfBoundsException ex) {
161 fail("RestconfError not found at index " + index);
164 assertEquals("getErrorType", expErrorType, actual.getErrorType());
165 assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
166 assertNotNull("getErrorMessage is null", actual.getErrorMessage());
168 if (expErrorMsg.isPresent()) {
169 assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
172 if (expAppTag.isPresent()) {
173 assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
178 public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
179 List<RpcError> rpcErrors = Arrays.asList(
180 RpcResultBuilder.newError( RpcError.ErrorType.TRANSPORT, "bogusTag", "foo" ),
181 RpcResultBuilder.newWarning( RpcError.ErrorType.RPC, "in-use", "bar",
182 "app-tag", null, null ) );
184 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed()
185 .withRpcErrors(rpcErrors).build();
187 BrokerFacade brokerFacade = mock(BrokerFacade.class);
189 brokerFacade.invokeRpc(
190 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
191 any(CompositeNode.class))).thenReturn(
192 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
194 restconfImpl.setBroker(brokerFacade);
197 restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
198 fail("Expected an exception to be thrown.");
199 } catch (RestconfDocumentedException e) {
200 verifyRestconfDocumentedException(e, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED, Optional.of("foo"),
201 Optional.<String> absent());
202 verifyRestconfDocumentedException(e, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"),
203 Optional.of("app-tag"));
208 public void testInvokeRpcWithNoPayload_Success() {
209 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
211 BrokerFacade brokerFacade = mock(BrokerFacade.class);
213 brokerFacade.invokeRpc(
214 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
215 any(CompositeNode.class))).thenReturn(
216 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
218 restconfImpl.setBroker(brokerFacade);
220 StructuredData output = restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
221 assertEquals(null, output);
222 // additional validation in the fact that the restconfImpl does not
223 // throw an exception.
227 public void testInvokeRpcMethodExpectingNoPayloadButProvidePayload() {
229 restconfImpl.invokeRpc("toaster:cancel-toast", " a payload ", uriInfo);
230 fail("Expected an exception");
231 } catch (RestconfDocumentedException e) {
232 verifyRestconfDocumentedException(e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
233 Optional.<String> absent(), Optional.<String> absent());
238 public void testInvokeRpcMethodWithBadMethodName() {
240 restconfImpl.invokeRpc("toaster:bad-method", "", uriInfo);
241 fail("Expected an exception");
242 } catch (RestconfDocumentedException e) {
243 verifyRestconfDocumentedException(e, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
244 Optional.<String> absent(), Optional.<String> absent());
249 public void testInvokeRpcMethodWithInput() {
250 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
252 CompositeNode payload = mock(CompositeNode.class);
254 BrokerFacade brokerFacade = mock(BrokerFacade.class);
256 brokerFacade.invokeRpc(
257 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
258 any(CompositeNode.class))).thenReturn(
259 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
261 restconfImpl.setBroker(brokerFacade);
263 StructuredData output = restconfImpl.invokeRpc("toaster:make-toast", payload, uriInfo);
264 assertEquals(null, output);
265 // additional validation in the fact that the restconfImpl does not
266 // throw an exception.
270 public void testThrowExceptionWhenSlashInModuleName() {
272 restconfImpl.invokeRpc("toaster/slash", "", uriInfo);
273 fail("Expected an exception.");
274 } catch (RestconfDocumentedException e) {
275 verifyRestconfDocumentedException(e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
276 Optional.<String> absent(), Optional.<String> absent());
281 public void testInvokeRpcWithNoPayloadWithOutput_Success() {
282 CompositeNode compositeNode = mock(CompositeNode.class);
283 RpcResult<CompositeNode> rpcResult =
284 RpcResultBuilder.<CompositeNode>success(compositeNode).build();
286 BrokerFacade brokerFacade = mock(BrokerFacade.class);
288 brokerFacade.invokeRpc(
289 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
290 any(CompositeNode.class))).thenReturn(
291 Futures.<RpcResult<CompositeNode>> immediateFuture(rpcResult));
293 restconfImpl.setBroker(brokerFacade);
295 StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "", uriInfo);
296 assertNotNull(output);
297 assertSame(compositeNode, output.getData());
298 assertNotNull(output.getSchema());
303 * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context.
304 * This rpc is then executed.
306 * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is
310 public void testMountedRpcCallNoPayload_Success() throws Exception {
311 RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
313 ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
314 when(mockListener.get()).thenReturn(rpcResult);
316 QName cancelToastQName = QName.create("namespace", "2014-05-28", "cancelToast");
318 RpcDefinition mockRpc = mock(RpcDefinition.class);
319 when(mockRpc.getQName()).thenReturn(cancelToastQName);
321 DOMMountPoint mockMountPoint = mock(DOMMountPoint.class);
322 RpcProvisionRegistry mockedRpcProvisionRegistry = mock(RpcProvisionRegistry.class);
323 when(mockedRpcProvisionRegistry.invokeRpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
324 when(mockMountPoint.getService(eq(RpcProvisionRegistry.class))).thenReturn(Optional.of(mockedRpcProvisionRegistry));
325 when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc"));
327 InstanceIdentifierContext mockedInstanceId = mock(InstanceIdentifierContext.class);
328 when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
330 ControllerContext mockedContext = mock(ControllerContext.class);
331 String rpcNoop = "invoke-rpc-module:rpc-noop";
332 when(mockedContext.urlPathArgDecode(rpcNoop)).thenReturn(rpcNoop);
333 when(mockedContext.getRpcDefinition(rpcNoop)).thenReturn(mockRpc);
335 mockedContext.toMountPointIdentifier(eq("opendaylight-inventory:nodes/node/"
336 + "REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop"))).thenReturn(mockedInstanceId);
338 restconfImpl.setControllerContext(mockedContext);
340 restconfImpl.invokeRpc(
341 "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop", "",
343 fail("RestconfDocumentedException wasn't raised");
344 } catch (RestconfDocumentedException e) {
345 List<RestconfError> errors = e.getErrors();
346 assertNotNull(errors);
347 assertEquals(1, errors.size());
348 assertEquals(ErrorType.APPLICATION, errors.iterator().next().getErrorType());
349 assertEquals(ErrorTag.OPERATION_FAILED, errors.iterator().next().getErrorTag());
352 // additional validation in the fact that the restconfImpl does not
353 // throw an exception.