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.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;
64 public class InvokeRpcMethodTest {
66 private RestconfImpl restconfImpl = null;
67 private static ControllerContext controllerContext = null;
68 private static UriInfo uriInfo;
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);
87 public void initMethod() {
88 restconfImpl = RestconfImpl.getInstance();
89 restconfImpl.setControllerContext(controllerContext);
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).
98 public void invokeRpcMtethodTest() {
99 ControllerContext contContext = controllerContext;
101 contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
102 } catch (URISyntaxException e) {
103 assertTrue("Uri wasn't created sucessfuly", false);
106 BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
108 RestconfImpl restconf = RestconfImpl.getInstance();
109 restconf.setBroker(mockedBrokerFacade);
110 restconf.setControllerContext(contContext);
112 CompositeNode payload = preparePayload();
114 when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
115 Futures.<RpcResult<CompositeNode>> immediateFuture(Rpcs.<CompositeNode> getRpcResult(true)));
117 StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
118 assertTrue(structData == null);
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);
134 public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
135 RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false);
137 BrokerFacade brokerFacade = mock(BrokerFacade.class);
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));
144 restconfImpl.setBroker(brokerFacade);
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());
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;
160 actual = e.getErrors().get(index);
161 } catch (ArrayIndexOutOfBoundsException ex) {
162 fail("RestconfError not found at index " + index);
165 assertEquals("getErrorType", expErrorType, actual.getErrorType());
166 assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
167 assertNotNull("getErrorMessage is null", actual.getErrorMessage());
169 if (expErrorMsg.isPresent()) {
170 assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
173 if (expAppTag.isPresent()) {
174 assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
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));
184 RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false, rpcErrors);
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 = Rpcs.<CompositeNode> getRpcResult(true);
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 = Rpcs.<CompositeNode> getRpcResult(true);
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 = Rpcs.<CompositeNode> getRpcResult(true, compositeNode,
283 Collections.<RpcError> emptyList());
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());
301 public void testMountedRpcCallNoPayload_Success() throws Exception {
302 RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true);
304 ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
305 when(mockListener.get()).thenReturn(rpcResult);
307 QName cancelToastQName = QName.create("namespace", "2014-05-28", "cancelToast");
309 RpcDefinition mockRpc = mock(RpcDefinition.class);
310 when(mockRpc.getQName()).thenReturn(cancelToastQName);
312 MountInstance mockMountPoint = mock(MountInstance.class);
313 when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
315 InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class);
316 when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
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);
323 mockedContext.toMountPointIdentifier("opendaylight-inventory:nodes/node/"
324 + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast")).thenReturn(mockedInstanceId);
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);
331 // additional validation in the fact that the restconfImpl does not
332 // throw an exception.