Merge "Add netconf-cli temporary files to .gitignore"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / InvokeRpcMethodTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2014 Brocade Communications Systems, Inc.
4  *
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
8  */
9 package org.opendaylight.controller.sal.restconf.impl.test;
10
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;
21
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;
26 import java.net.URI;
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;
32 import java.util.Set;
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;
63
64 public class InvokeRpcMethodTest {
65
66     private RestconfImpl restconfImpl = null;
67     private static ControllerContext controllerContext = null;
68     private static UriInfo uriInfo;
69
70
71     @BeforeClass
72     public static void init() throws FileNotFoundException {
73         Set<Module> allModules = new HashSet<Module>( TestUtils
74                 .loadModulesFrom("/full-versions/yangs") );
75         allModules.addAll( TestUtils.loadModulesFrom("/invoke-rpc") );
76         assertNotNull(allModules);
77         Module module = TestUtils.resolveModule("invoke-rpc-module", allModules);
78         assertNotNull(module);
79         SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
80         controllerContext = spy( ControllerContext.getInstance() );
81         controllerContext.setSchemas(schemaContext);
82         uriInfo = mock(UriInfo.class);
83         MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
84         map.put("prettyPrint", Collections.singletonList("true"));
85         when(uriInfo.getQueryParameters(any(Boolean.class))).thenReturn(map);
86     }
87
88     @Before
89     public void initMethod()
90     {
91         restconfImpl = RestconfImpl.getInstance();
92         restconfImpl.setControllerContext( controllerContext );
93     }
94
95     /**
96      * Test method invokeRpc in RestconfImpl class tests if composite node as
97      * input parameter of method invokeRpc (second argument) is wrapped to
98      * parent composite node which has QName equals to QName of rpc (resolved
99      * from string - first argument).
100      */
101     @Test
102     public void invokeRpcMtethodTest() {
103         ControllerContext contContext = controllerContext;
104         try {
105             contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
106         } catch (URISyntaxException e) {
107             assertTrue("Uri wasn't created sucessfuly", false);
108         }
109
110         BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
111
112         RestconfImpl restconf = RestconfImpl.getInstance();
113         restconf.setBroker(mockedBrokerFacade);
114         restconf.setControllerContext(contContext);
115
116         CompositeNode payload = preparePayload();
117
118         when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class)))
119             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture(
120                                                Rpcs.<CompositeNode>getRpcResult( true ) ) );
121
122         StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload,uriInfo);
123         assertTrue(structData == null);
124
125     }
126
127     private CompositeNode preparePayload() {
128         MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(
129                 TestUtils.buildQName("cont", "nmspc", "2013-12-04"), null, null, ModifyAction.CREATE, null);
130         MutableSimpleNode<?> lf = NodeFactory.createMutableSimpleNode(
131                 TestUtils.buildQName("lf", "nmspc", "2013-12-04"), cont, "any value", ModifyAction.CREATE, null);
132         cont.getValue().add(lf);
133         cont.init();
134
135         return cont;
136     }
137
138     @Test
139     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
140         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false );
141
142         BrokerFacade brokerFacade = mock(BrokerFacade.class);
143         when( brokerFacade.invokeRpc(
144                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
145                  any(CompositeNode.class)))
146             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
147
148         restconfImpl.setBroker(brokerFacade);
149
150         try {
151             restconfImpl.invokeRpc("toaster:cancel-toast", "",uriInfo);
152             fail("Expected an exception to be thrown.");
153         }
154         catch (RestconfDocumentedException e) {
155             verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
156                                                Optional.<String>absent(), Optional.<String>absent() );
157         }
158     }
159
160     void verifyRestconfDocumentedException( final RestconfDocumentedException e, final int index,
161                                             final ErrorType expErrorType, final ErrorTag expErrorTag,
162                                             final Optional<String> expErrorMsg,
163                                             final Optional<String> expAppTag ) {
164         RestconfError actual = null;
165         try {
166             actual = e.getErrors().get( index );
167         }
168         catch( ArrayIndexOutOfBoundsException ex ) {
169             fail( "RestconfError not found at index " + index );
170         }
171
172         assertEquals( "getErrorType", expErrorType, actual.getErrorType() );
173         assertEquals( "getErrorTag", expErrorTag, actual.getErrorTag() );
174         assertNotNull( "getErrorMessage is null", actual.getErrorMessage() );
175
176         if( expErrorMsg.isPresent() ) {
177             assertEquals( "getErrorMessage", expErrorMsg.get(), actual.getErrorMessage() );
178         }
179
180         if( expAppTag.isPresent() ) {
181             assertEquals( "getErrorAppTag", expAppTag.get(), actual.getErrorAppTag() );
182         }
183     }
184
185     @Test
186     public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
187         List<RpcError> rpcErrors = Arrays.asList(
188             RpcErrors.getRpcError( null, "bogusTag", null, ErrorSeverity.ERROR, "foo",
189                                    RpcError.ErrorType.TRANSPORT, null ),
190             RpcErrors.getRpcError( "app-tag", "in-use", null, ErrorSeverity.WARNING, "bar",
191                                    RpcError.ErrorType.RPC, null ));
192
193         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false, rpcErrors );
194
195         BrokerFacade brokerFacade = mock(BrokerFacade.class);
196         when( brokerFacade.invokeRpc(
197                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
198                  any(CompositeNode.class)))
199             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
200
201         restconfImpl.setBroker(brokerFacade);
202
203         try {
204             restconfImpl.invokeRpc("toaster:cancel-toast", "",uriInfo);
205             fail("Expected an exception to be thrown.");
206         }
207         catch (RestconfDocumentedException e) {
208             verifyRestconfDocumentedException( e, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED,
209                                                Optional.of( "foo" ), Optional.<String>absent() );
210             verifyRestconfDocumentedException( e, 1, ErrorType.RPC, ErrorTag.IN_USE,
211                                                Optional.of( "bar" ), Optional.of( "app-tag" ) );
212         }
213     }
214
215     @Test
216     public void testInvokeRpcWithNoPayload_Success() {
217         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
218
219         BrokerFacade brokerFacade = mock(BrokerFacade.class);
220         when( brokerFacade.invokeRpc(
221                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
222                  any( CompositeNode.class )))
223             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
224
225         restconfImpl.setBroker(brokerFacade);
226
227         StructuredData output = restconfImpl.invokeRpc("toaster:cancel-toast",
228                 "",uriInfo);
229         assertEquals(null, output);
230         //additional validation in the fact that the restconfImpl does not throw an exception.
231     }
232
233     @Test
234     public void testInvokeRpcMethodExpectingNoPayloadButProvidePayload() {
235         try {
236             restconfImpl.invokeRpc("toaster:cancel-toast", " a payload ",uriInfo);
237             fail("Expected an exception");
238         } catch (RestconfDocumentedException e) {
239             verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
240                                                Optional.<String>absent(), Optional.<String>absent() );
241         }
242     }
243
244     @Test
245     public void testInvokeRpcMethodWithBadMethodName() {
246         try {
247             restconfImpl.invokeRpc("toaster:bad-method", "",uriInfo);
248             fail("Expected an exception");
249         }
250         catch (RestconfDocumentedException e) {
251             verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
252                                                Optional.<String>absent(), Optional.<String>absent() );
253         }
254     }
255
256     @Test
257     public void testInvokeRpcMethodWithInput() {
258         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
259
260         CompositeNode payload = mock(CompositeNode.class);
261
262         BrokerFacade brokerFacade = mock(BrokerFacade.class);
263         when( brokerFacade.invokeRpc(
264                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
265                  any(CompositeNode.class)))
266             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
267
268         restconfImpl.setBroker(brokerFacade);
269
270         StructuredData output = restconfImpl.invokeRpc("toaster:make-toast",
271                 payload,uriInfo);
272         assertEquals(null, output);
273         //additional validation in the fact that the restconfImpl does not throw an exception.
274     }
275
276     @Test
277     public void testThrowExceptionWhenSlashInModuleName() {
278         try {
279             restconfImpl.invokeRpc("toaster/slash", "",uriInfo);
280             fail("Expected an exception.");
281         }
282         catch (RestconfDocumentedException e) {
283             verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
284                                                Optional.<String>absent(), Optional.<String>absent() );
285         }
286     }
287
288     @Test
289     public void testInvokeRpcWithNoPayloadWithOutput_Success() {
290         CompositeNode compositeNode = mock( CompositeNode.class );
291         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true, compositeNode,
292                                                             Collections.<RpcError>emptyList() );
293
294         BrokerFacade brokerFacade = mock(BrokerFacade.class);
295         when( brokerFacade.invokeRpc(
296                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
297                         any( CompositeNode.class )))
298             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
299
300         restconfImpl.setBroker(brokerFacade);
301
302         StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "",uriInfo);
303         assertNotNull( output );
304         assertSame( compositeNode, output.getData() );
305         assertNotNull( output.getSchema() );
306     }
307
308     @Test
309     public void testMountedRpcCallNoPayload_Success() throws Exception
310     {
311         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
312
313         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock( ListenableFuture.class );
314         when( mockListener.get() ).thenReturn( rpcResult );
315
316         QName cancelToastQName = QName.create( "namespace", "2014-05-28", "cancelToast" );
317
318         RpcDefinition mockRpc = mock( RpcDefinition.class );
319         when( mockRpc.getQName() ).thenReturn( cancelToastQName );
320
321         MountInstance mockMountPoint = mock( MountInstance.class );
322         when( mockMountPoint.rpc( eq( cancelToastQName ), any( CompositeNode.class ) ) )
323         .thenReturn( mockListener );
324
325         InstanceIdWithSchemaNode mockedInstanceId = mock( InstanceIdWithSchemaNode.class );
326         when( mockedInstanceId.getMountPoint() ).thenReturn( mockMountPoint );
327
328         ControllerContext mockedContext = mock( ControllerContext.class );
329         String cancelToastStr = "toaster:cancel-toast";
330         when( mockedContext.urlPathArgDecode( cancelToastStr ) ).thenReturn( cancelToastStr );
331         when( mockedContext.getRpcDefinition( cancelToastStr ) ).thenReturn( mockRpc );
332         when( mockedContext.toMountPointIdentifier(  "opendaylight-inventory:nodes/node/"
333                 + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast" ) ).thenReturn( mockedInstanceId );
334
335         restconfImpl.setControllerContext( mockedContext );
336         StructuredData output = restconfImpl.invokeRpc(
337                 "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/toaster:cancel-toast",
338                 "",uriInfo);
339         assertEquals(null, output);
340
341         //additional validation in the fact that the restconfImpl does not throw an exception.
342     }
343 }