2 * Copyright (c) 2013, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sal.restconf.impl.test;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertSame;
14 import static org.junit.Assert.assertThrows;
15 import static org.junit.Assert.assertTrue;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.ArgumentMatchers.eq;
18 import static org.mockito.Mockito.doCallRealMethod;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.mock;
21 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
22 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
24 import java.io.FileNotFoundException;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Optional;
30 import javax.ws.rs.WebApplicationException;
31 import javax.ws.rs.core.MultivaluedHashMap;
32 import javax.ws.rs.core.MultivaluedMap;
33 import javax.ws.rs.core.Response;
34 import javax.ws.rs.core.UriInfo;
35 import org.junit.BeforeClass;
36 import org.junit.Ignore;
37 import org.junit.Test;
38 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
39 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
40 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
41 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
42 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
43 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
44 import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
45 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
46 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
47 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
48 import org.opendaylight.restconf.common.errors.RestconfError;
49 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
50 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
51 import org.opendaylight.yangtools.yang.common.QName;
52 import org.opendaylight.yangtools.yang.common.RpcError;
53 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
54 import org.opendaylight.yangtools.yang.common.XMLNamespace;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
56 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
59 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
60 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
61 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
62 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
63 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
66 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.Module;
68 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
69 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
70 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
72 public class InvokeRpcMethodTest {
74 private static UriInfo uriInfo;
75 private static EffectiveModelContext schemaContext;
77 private final RestconfImpl restconfImpl;
78 private final ControllerContext controllerContext;
79 private final BrokerFacade brokerFacade = mock(BrokerFacade.class);
81 public InvokeRpcMethodTest() {
82 controllerContext = TestRestconfUtils.newControllerContext(schemaContext);
83 restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext);
87 public static void init() throws FileNotFoundException, ReactorException {
88 schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs", "/invoke-rpc");
89 final Collection<? extends Module> allModules = schemaContext.getModules();
90 assertNotNull(allModules);
91 final Module module = TestUtils.resolveModule("invoke-rpc-module", allModules);
92 assertNotNull(module);
94 uriInfo = mock(UriInfo.class);
95 final MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
96 map.put("prettyPrint", List.of("true"));
97 doReturn(map).when(uriInfo).getQueryParameters(any(Boolean.class));
101 * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc
102 * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from
103 * string - first argument).
107 public void invokeRpcMethodTest() {
108 controllerContext.findModuleNameByNamespace(XMLNamespace.of("invoke:rpc:module"));
110 final NormalizedNodeContext payload = prepareDomPayload();
112 final NormalizedNodeContext rpcResponse =
113 restconfImpl.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
114 assertNotNull(rpcResponse);
115 assertNull(rpcResponse.getData());
119 private NormalizedNodeContext prepareDomPayload() {
120 final EffectiveModelContext schema = controllerContext.getGlobalSchema();
121 final Module rpcModule = schema.findModules("invoke-rpc-module").iterator().next();
122 assertNotNull(rpcModule);
123 final QName rpcQName = QName.create(rpcModule.getQNameModule(), "rpc-test");
124 ContainerLike rpcInputSchemaNode = null;
125 for (final RpcDefinition rpc : rpcModule.getRpcs()) {
126 if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
127 rpcInputSchemaNode = rpc.getInput();
131 assertNotNull(rpcInputSchemaNode);
133 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> container =
134 SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode);
136 final QName contQName = QName.create(rpcModule.getQNameModule(), "cont");
137 final DataSchemaNode contSchemaNode = rpcInputSchemaNode.getDataChildByName(contQName);
138 assertTrue(contSchemaNode instanceof ContainerSchemaNode);
139 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contNode =
140 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) contSchemaNode);
142 final QName lfQName = QName.create(rpcModule.getQNameModule(), "lf");
143 final DataSchemaNode lfSchemaNode = ((ContainerSchemaNode) contSchemaNode).getDataChildByName(lfQName);
144 assertTrue(lfSchemaNode instanceof LeafSchemaNode);
145 final LeafNode<Object> lfNode =
146 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) lfSchemaNode).withValue("any value").build();
147 contNode.withChild(lfNode);
148 container.withChild(contNode.build());
150 return new NormalizedNodeContext(
151 new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
155 public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
156 final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
158 doReturn(immediateFailedFluentFuture(new DOMRpcImplementationNotAvailableException("testExeption")))
159 .when(brokerFacade).invokeRpc(eq(qname), any());
161 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
162 () -> this.restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo));
163 verifyRestconfDocumentedException(ex, 0, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED,
164 Optional.empty(), Optional.empty());
167 void verifyRestconfDocumentedException(final RestconfDocumentedException restDocumentedException, final int index,
168 final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg,
169 final Optional<String> expAppTag) {
171 final List<RestconfError> errors = restDocumentedException.getErrors();
172 assertTrue("RestconfError not found at index " + index, errors.size() > index);
174 RestconfError actual = errors.get(index);
176 assertEquals("getErrorType", expErrorType, actual.getErrorType());
177 assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
178 assertNotNull("getErrorMessage is null", actual.getErrorMessage());
180 if (expErrorMsg.isPresent()) {
181 assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
184 if (expAppTag.isPresent()) {
185 assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
190 public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
191 final List<RpcError> rpcErrors = Arrays.asList(
192 RpcResultBuilder.newError(RpcError.ErrorType.TRANSPORT, "bogusTag", "foo"),
193 RpcResultBuilder.newWarning(RpcError.ErrorType.RPC, "in-use", "bar",
194 "app-tag", null, null));
196 final DOMRpcResult result = new DefaultDOMRpcResult(rpcErrors);
197 final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
198 doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(path), any());
200 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
201 () -> this.restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo));
202 verifyRestconfDocumentedException(ex, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED, Optional.of("foo"),
204 verifyRestconfDocumentedException(ex, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"),
205 Optional.of("app-tag"));
209 public void testInvokeRpcWithNoPayload_Success() {
210 final NormalizedNode resultObj = null;
211 final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj);
213 final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
215 doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any());
217 final NormalizedNodeContext output = this.restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo);
218 assertNotNull(output);
219 assertEquals(null, output.getData());
220 // additional validation in the fact that the restconfImpl does not
221 // throw an exception.
225 public void testInvokeRpcWithEmptyOutput() {
226 final ContainerNode resultObj = mock(ContainerNode.class);
227 doReturn(Set.of()).when(resultObj).body();
228 doCallRealMethod().when(resultObj).isEmpty();
229 final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj);
231 final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
232 doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any());
234 WebApplicationException exceptionToBeThrown = assertThrows(WebApplicationException.class,
235 () -> this.restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo));
236 assertEquals(Response.Status.NO_CONTENT.getStatusCode(), exceptionToBeThrown.getResponse().getStatus());
240 public void testInvokeRpcMethodWithBadMethodName() {
241 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
242 () -> this.restconfImpl.invokeRpc("toaster:bad-method", null, uriInfo));
243 verifyRestconfDocumentedException(ex, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
244 Optional.empty(), Optional.empty());
249 public void testInvokeRpcMethodWithInput() {
250 final DOMRpcResult expResult = mock(DOMRpcResult.class);
251 final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast");
253 final Module rpcModule = schemaContext.findModules("toaster").iterator().next();
254 assertNotNull(rpcModule);
255 final QName rpcQName = QName.create(rpcModule.getQNameModule(), "make-toast");
257 RpcDefinition rpcDef = null;
258 ContainerLike rpcInputSchemaNode = null;
259 for (final RpcDefinition rpc : rpcModule.getRpcs()) {
260 if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
261 rpcInputSchemaNode = rpc.getInput();
267 assertNotNull(rpcDef);
268 assertNotNull(rpcInputSchemaNode);
269 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
270 SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode);
272 final NormalizedNodeContext payload =
273 new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode,
274 null, schemaContext), containerBuilder.build());
276 doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(path), any(NormalizedNode.class));
278 final NormalizedNodeContext output = this.restconfImpl.invokeRpc("toaster:make-toast", payload, uriInfo);
279 assertNotNull(output);
280 assertEquals(null, output.getData());
281 // additional validation in the fact that the restconfImpl does not
282 // throw an exception.
286 public void testThrowExceptionWhenSlashInModuleName() {
287 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
288 () -> this.restconfImpl.invokeRpc("toaster/slash", null, uriInfo));
289 verifyRestconfDocumentedException(ex, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
290 Optional.empty(), Optional.empty());
294 public void testInvokeRpcWithNoPayloadWithOutput_Success() {
295 final SchemaContext schema = controllerContext.getGlobalSchema();
296 final Module rpcModule = schema.findModules("toaster").iterator().next();
297 assertNotNull(rpcModule);
298 final QName rpcQName = QName.create(rpcModule.getQNameModule(), "testOutput");
299 final QName rpcOutputQName = QName.create(rpcModule.getQNameModule(),"output");
301 RpcDefinition rpcDef = null;
302 ContainerLike rpcOutputSchemaNode = null;
303 for (final RpcDefinition rpc : rpcModule.getRpcs()) {
304 if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
305 rpcOutputSchemaNode = rpc.getOutput();
310 assertNotNull(rpcDef);
311 assertNotNull(rpcOutputSchemaNode);
312 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
313 SchemaAwareBuilders.containerBuilder(rpcOutputSchemaNode);
314 final DataSchemaNode leafSchema = rpcOutputSchemaNode
315 .getDataChildByName(QName.create(rpcModule.getQNameModule(), "textOut"));
316 assertTrue(leafSchema instanceof LeafSchemaNode);
317 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
318 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) leafSchema);
319 leafBuilder.withValue("brm");
320 containerBuilder.withChild(leafBuilder.build());
321 final ContainerNode container = containerBuilder.build();
323 final DOMRpcResult result = new DefaultDOMRpcResult(container);
325 doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(rpcDef.getQName()), any());
327 final NormalizedNodeContext output = this.restconfImpl.invokeRpc("toaster:testOutput", null, uriInfo);
328 assertNotNull(output);
329 assertNotNull(output.getData());
330 assertSame(container, output.getData());
331 assertNotNull(output.getInstanceIdentifierContext());
332 assertNotNull(output.getInstanceIdentifierContext().getSchemaContext());
336 * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context.
337 * This rpc is then executed.
338 * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is
342 public void testMountedRpcCallNoPayload_Success() throws Exception {
343 // FIXME find how to use mockito for it