Merge "Bug 1029: Remove dead code: sal-dom-it"
[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 java.io.FileNotFoundException;
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.junit.Before;
32 import org.junit.BeforeClass;
33 import org.junit.Test;
34 import org.opendaylight.controller.sal.common.util.RpcErrors;
35 import org.opendaylight.controller.sal.common.util.Rpcs;
36 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
37 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
38 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
39 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
40 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
41 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
42 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
43 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
44 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
45 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.common.RpcError;
48 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
49 import org.opendaylight.yangtools.yang.common.RpcResult;
50 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
51 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
52 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
53 import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
54 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
55 import org.opendaylight.yangtools.yang.model.api.Module;
56 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58
59 import com.google.common.base.Optional;
60 import com.google.common.util.concurrent.Futures;
61 import com.google.common.util.concurrent.ListenableFuture;
62
63 public class InvokeRpcMethodTest {
64
65     private RestconfImpl restconfImpl = null;
66     private static ControllerContext controllerContext = null;
67
68
69     @BeforeClass
70     public static void init() throws FileNotFoundException {
71         Set<Module> allModules = new HashSet<Module>( TestUtils
72                 .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
81     }
82
83     @Before
84     public void initMethod()
85     {
86         restconfImpl = RestconfImpl.getInstance();
87         restconfImpl.setControllerContext( controllerContext );
88     }
89
90     /**
91      * Test method invokeRpc in RestconfImpl class tests if composite node as
92      * input parameter of method invokeRpc (second argument) is wrapped to
93      * parent composite node which has QName equals to QName of rpc (resolved
94      * from string - first argument).
95      */
96     @Test
97     public void invokeRpcMtethodTest() {
98         ControllerContext contContext = controllerContext;
99         try {
100             contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
101         } catch (URISyntaxException e) {
102             assertTrue("Uri wasn't created sucessfuly", false);
103         }
104
105         BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
106
107         RestconfImpl restconf = RestconfImpl.getInstance();
108         restconf.setBroker(mockedBrokerFacade);
109         restconf.setControllerContext(contContext);
110
111         CompositeNode payload = preparePayload();
112
113         when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class)))
114             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture(
115                                                Rpcs.<CompositeNode>getRpcResult( true ) ) );
116
117         StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload);
118         assertTrue(structData == null);
119
120     }
121
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);
128         cont.init();
129
130         return cont;
131     }
132
133     @Test
134     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
135         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false );
136
137         BrokerFacade brokerFacade = mock(BrokerFacade.class);
138         when( brokerFacade.invokeRpc(
139                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
140                  any(CompositeNode.class)))
141             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
142
143         restconfImpl.setBroker(brokerFacade);
144
145         try {
146             restconfImpl.invokeRpc("toaster:cancel-toast", "");
147             fail("Expected an exception to be thrown.");
148         }
149         catch (RestconfDocumentedException e) {
150             verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
151                                                Optional.<String>absent(), Optional.<String>absent() );
152         }
153     }
154
155     void verifyRestconfDocumentedException( final RestconfDocumentedException e, final int index,
156                                             final ErrorType expErrorType, final ErrorTag expErrorTag,
157                                             final Optional<String> expErrorMsg,
158                                             final Optional<String> expAppTag ) {
159         RestconfError actual = null;
160         try {
161             actual = e.getErrors().get( index );
162         }
163         catch( ArrayIndexOutOfBoundsException ex ) {
164             fail( "RestconfError not found at index " + index );
165         }
166
167         assertEquals( "getErrorType", expErrorType, actual.getErrorType() );
168         assertEquals( "getErrorTag", expErrorTag, actual.getErrorTag() );
169         assertNotNull( "getErrorMessage is null", actual.getErrorMessage() );
170
171         if( expErrorMsg.isPresent() ) {
172             assertEquals( "getErrorMessage", expErrorMsg.get(), actual.getErrorMessage() );
173         }
174
175         if( expAppTag.isPresent() ) {
176             assertEquals( "getErrorAppTag", expAppTag.get(), actual.getErrorAppTag() );
177         }
178     }
179
180     @Test
181     public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
182         List<RpcError> rpcErrors = Arrays.asList(
183             RpcErrors.getRpcError( null, "bogusTag", null, ErrorSeverity.ERROR, "foo",
184                                    RpcError.ErrorType.TRANSPORT, null ),
185             RpcErrors.getRpcError( "app-tag", "in-use", null, ErrorSeverity.WARNING, "bar",
186                                    RpcError.ErrorType.RPC, null ));
187
188         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false, rpcErrors );
189
190         BrokerFacade brokerFacade = mock(BrokerFacade.class);
191         when( brokerFacade.invokeRpc(
192                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
193                  any(CompositeNode.class)))
194             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
195
196         restconfImpl.setBroker(brokerFacade);
197
198         try {
199             restconfImpl.invokeRpc("toaster:cancel-toast", "");
200             fail("Expected an exception to be thrown.");
201         }
202         catch (RestconfDocumentedException e) {
203             verifyRestconfDocumentedException( e, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED,
204                                                Optional.of( "foo" ), Optional.<String>absent() );
205             verifyRestconfDocumentedException( e, 1, ErrorType.RPC, ErrorTag.IN_USE,
206                                                Optional.of( "bar" ), Optional.of( "app-tag" ) );
207         }
208     }
209
210     @Test
211     public void testInvokeRpcWithNoPayload_Success() {
212         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
213
214         BrokerFacade brokerFacade = mock(BrokerFacade.class);
215         when( brokerFacade.invokeRpc(
216                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
217                  any( CompositeNode.class )))
218             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
219
220         restconfImpl.setBroker(brokerFacade);
221
222         StructuredData output = restconfImpl.invokeRpc("toaster:cancel-toast",
223                 "");
224         assertEquals(null, output);
225         //additional validation in the fact that the restconfImpl does not throw an exception.
226     }
227
228     @Test
229     public void testInvokeRpcMethodExpectingNoPayloadButProvidePayload() {
230         try {
231             restconfImpl.invokeRpc("toaster:cancel-toast", " a payload ");
232             fail("Expected an exception");
233         } catch (RestconfDocumentedException e) {
234             verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
235                                                Optional.<String>absent(), Optional.<String>absent() );
236         }
237     }
238
239     @Test
240     public void testInvokeRpcMethodWithBadMethodName() {
241         try {
242             restconfImpl.invokeRpc("toaster:bad-method", "");
243             fail("Expected an exception");
244         }
245         catch (RestconfDocumentedException e) {
246             verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
247                                                Optional.<String>absent(), Optional.<String>absent() );
248         }
249     }
250
251     @Test
252     public void testInvokeRpcMethodWithInput() {
253         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
254
255         CompositeNode payload = mock(CompositeNode.class);
256
257         BrokerFacade brokerFacade = mock(BrokerFacade.class);
258         when( brokerFacade.invokeRpc(
259                  eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
260                  any(CompositeNode.class)))
261             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
262
263         restconfImpl.setBroker(brokerFacade);
264
265         StructuredData output = restconfImpl.invokeRpc("toaster:make-toast",
266                 payload);
267         assertEquals(null, output);
268         //additional validation in the fact that the restconfImpl does not throw an exception.
269     }
270
271     @Test
272     public void testThrowExceptionWhenSlashInModuleName() {
273         try {
274             restconfImpl.invokeRpc("toaster/slash", "");
275             fail("Expected an exception.");
276         }
277         catch (RestconfDocumentedException e) {
278             verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
279                                                Optional.<String>absent(), Optional.<String>absent() );
280         }
281     }
282
283     @Test
284     public void testInvokeRpcWithNoPayloadWithOutput_Success() {
285         CompositeNode compositeNode = mock( CompositeNode.class );
286         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true, compositeNode,
287                                                             Collections.<RpcError>emptyList() );
288
289         BrokerFacade brokerFacade = mock(BrokerFacade.class);
290         when( brokerFacade.invokeRpc(
291                         eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
292                         any( CompositeNode.class )))
293             .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
294
295         restconfImpl.setBroker(brokerFacade);
296
297         StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "");
298         assertNotNull( output );
299         assertSame( compositeNode, output.getData() );
300         assertNotNull( output.getSchema() );
301     }
302
303     @Test
304     public void testMountedRpcCallNoPayload_Success() throws Exception
305     {
306         RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
307
308         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock( ListenableFuture.class );
309         when( mockListener.get() ).thenReturn( rpcResult );
310
311         QName cancelToastQName = QName.create( "cancelToast" );
312
313         RpcDefinition mockRpc = mock( RpcDefinition.class );
314         when( mockRpc.getQName() ).thenReturn( cancelToastQName );
315
316         MountInstance mockMountPoint = mock( MountInstance.class );
317         when( mockMountPoint.rpc( eq( cancelToastQName ), any( CompositeNode.class ) ) )
318         .thenReturn( mockListener );
319
320         InstanceIdWithSchemaNode mockedInstanceId = mock( InstanceIdWithSchemaNode.class );
321         when( mockedInstanceId.getMountPoint() ).thenReturn( mockMountPoint );
322
323         ControllerContext mockedContext = mock( ControllerContext.class );
324         String cancelToastStr = "toaster:cancel-toast";
325         when( mockedContext.urlPathArgDecode( cancelToastStr ) ).thenReturn( cancelToastStr );
326         when( mockedContext.getRpcDefinition( cancelToastStr ) ).thenReturn( mockRpc );
327         when( mockedContext.toMountPointIdentifier(  "opendaylight-inventory:nodes/node/"
328                 + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast" ) ).thenReturn( mockedInstanceId );
329
330         restconfImpl.setControllerContext( mockedContext );
331         StructuredData output = restconfImpl.invokeRpc(
332                 "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/toaster:cancel-toast",
333                 "");
334         assertEquals(null, output);
335
336         //additional validation in the fact that the restconfImpl does not throw an exception.
337     }
338 }