Remove RestconfError.ErrorType
[netconf.git] / restconf / restconf-nb-bierman02 / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / InvokeRpcMethodTest.java
1 /*
2  * Copyright (c) 2013, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.sal.restconf.impl.test;
9
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;
23
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;
29 import java.util.Set;
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.yangtools.yang.common.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;
71
72 public class InvokeRpcMethodTest {
73
74     private static UriInfo uriInfo;
75     private static EffectiveModelContext schemaContext;
76
77     private final RestconfImpl restconfImpl;
78     private final ControllerContext controllerContext;
79     private final BrokerFacade brokerFacade = mock(BrokerFacade.class);
80
81     public InvokeRpcMethodTest() {
82         controllerContext = TestRestconfUtils.newControllerContext(schemaContext);
83         restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext);
84     }
85
86     @BeforeClass
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);
93
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));
98     }
99
100     /**
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).
104      */
105     @Test
106     @Ignore
107     public void invokeRpcMethodTest() {
108         controllerContext.findModuleNameByNamespace(XMLNamespace.of("invoke:rpc:module"));
109
110         final NormalizedNodeContext payload = prepareDomPayload();
111
112         final NormalizedNodeContext rpcResponse =
113                 restconfImpl.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
114         assertNotNull(rpcResponse);
115         assertNull(rpcResponse.getData());
116
117     }
118
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();
128                 break;
129             }
130         }
131         assertNotNull(rpcInputSchemaNode);
132
133         final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> container =
134                 SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode);
135
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);
141
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());
149
150         return new NormalizedNodeContext(
151                 new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
152     }
153
154     @Test
155     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
156         final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
157
158         doReturn(immediateFailedFluentFuture(new DOMRpcImplementationNotAvailableException("testExeption")))
159             .when(brokerFacade).invokeRpc(eq(qname), any());
160
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());
165     }
166
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) {
170
171         final List<RestconfError> errors = restDocumentedException.getErrors();
172         assertTrue("RestconfError not found at index " + index, errors.size() > index);
173
174         RestconfError actual = errors.get(index);
175
176         assertEquals("getErrorType", expErrorType, actual.getErrorType());
177         assertEquals("getErrorTag", expErrorTag, actual.getErrorTag());
178         assertNotNull("getErrorMessage is null", actual.getErrorMessage());
179
180         if (expErrorMsg.isPresent()) {
181             assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage());
182         }
183
184         if (expAppTag.isPresent()) {
185             assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag());
186         }
187     }
188
189     @Test
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));
195
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());
199
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"),
203             Optional.empty());
204         verifyRestconfDocumentedException(ex, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"),
205             Optional.of("app-tag"));
206     }
207
208     @Test
209     public void testInvokeRpcWithNoPayload_Success() {
210         final NormalizedNode resultObj = null;
211         final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj);
212
213         final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast");
214
215         doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any());
216
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.
222     }
223
224     @Test
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);
230
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());
233
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());
237     }
238
239     @Test
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());
245     }
246
247     @Test
248     @Ignore
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");
252
253         final Module rpcModule = schemaContext.findModules("toaster").iterator().next();
254         assertNotNull(rpcModule);
255         final QName rpcQName = QName.create(rpcModule.getQNameModule(), "make-toast");
256
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();
262                 rpcDef = rpc;
263                 break;
264             }
265         }
266
267         assertNotNull(rpcDef);
268         assertNotNull(rpcInputSchemaNode);
269         final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
270                 SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode);
271
272         final NormalizedNodeContext payload =
273                 new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode,
274                 null, schemaContext), containerBuilder.build());
275
276         doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(path), any(NormalizedNode.class));
277
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.
283     }
284
285     @Test
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());
291     }
292
293     @Test
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");
300
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();
306                 rpcDef = rpc;
307                 break;
308             }
309         }
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();
322
323         final DOMRpcResult result = new DefaultDOMRpcResult(container);
324
325         doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(rpcDef.getQName()), any());
326
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());
333     }
334
335     /**
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
339      * invoked.
340      */
341     @Test
342     public void testMountedRpcCallNoPayload_Success() throws Exception {
343         // FIXME find how to use mockito for it
344     }
345 }