diff options
Diffstat (limited to 'netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java')
-rw-r--r-- | netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java new file mode 100644 index 0000000..19386a1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2013, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; + +import java.io.FileNotFoundException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.ErrorTags; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class InvokeRpcMethodTest { + + private static UriInfo uriInfo; + private static EffectiveModelContext schemaContext; + + private final RestconfImpl restconfImpl; + private final ControllerContext controllerContext; + private final BrokerFacade brokerFacade = mock(BrokerFacade.class); + + public InvokeRpcMethodTest() { + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + } + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs", "/invoke-rpc"); + final Collection<? extends Module> allModules = schemaContext.getModules(); + assertNotNull(allModules); + final Module module = TestUtils.resolveModule("invoke-rpc-module", allModules); + assertNotNull(module); + + uriInfo = mock(UriInfo.class); + final MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); + map.put("prettyPrint", List.of("true")); + doReturn(map).when(uriInfo).getQueryParameters(any(Boolean.class)); + } + + /** + * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc + * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from + * string - first argument). + */ + @Test + public void invokeRpcMethodTest() { + controllerContext.findModuleNameByNamespace(XMLNamespace.of("invoke:rpc:module")); + + final NormalizedNodeContext payload = prepareDomPayload(); + + final NormalizedNodeContext rpcResponse = + restconfImpl.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo); + assertNotNull(rpcResponse); + assertNull(rpcResponse.getData()); + + } + + private NormalizedNodeContext prepareDomPayload() { + final EffectiveModelContext schema = controllerContext.getGlobalSchema(); + final Module rpcModule = schema.findModules("invoke-rpc-module").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "rpc-test"); + RpcDefinition rpcSchemaNode = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcSchemaNode = rpc; + break; + } + } + assertNotNull(rpcSchemaNode); + final InputSchemaNode rpcInputSchemaNode = rpcSchemaNode.getInput(); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> container = + SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode); + + final QName contQName = QName.create(rpcModule.getQNameModule(), "cont"); + final DataSchemaNode contSchemaNode = rpcInputSchemaNode.getDataChildByName(contQName); + assertTrue(contSchemaNode instanceof ContainerSchemaNode); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contNode = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) contSchemaNode); + + final QName lfQName = QName.create(rpcModule.getQNameModule(), "lf"); + final DataSchemaNode lfSchemaNode = ((ContainerSchemaNode) contSchemaNode).getDataChildByName(lfQName); + assertTrue(lfSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> lfNode = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) lfSchemaNode).withValue("any value").build(); + contNode.withChild(lfNode); + container.withChild(contNode.build()); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcSchemaNode, null), + container.build()); + } + + @Test + public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() { + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + + doReturn(immediateFailedFluentFuture(new DOMRpcImplementationNotAvailableException("testExeption"))) + .when(brokerFacade).invokeRpc(eq(qname), any()); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED, + Optional.empty(), Optional.empty()); + } + + void verifyRestconfDocumentedException(final RestconfDocumentedException restDocumentedException, final int index, + final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg, + final Optional<String> expAppTag) { + + final List<RestconfError> errors = restDocumentedException.getErrors(); + assertTrue("RestconfError not found at index " + index, errors.size() > index); + + RestconfError actual = errors.get(index); + + assertEquals("getErrorType", expErrorType, actual.getErrorType()); + assertEquals("getErrorTag", expErrorTag, actual.getErrorTag()); + assertNotNull("getErrorMessage is null", actual.getErrorMessage()); + + if (expErrorMsg.isPresent()) { + assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage()); + } + + if (expAppTag.isPresent()) { + assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag()); + } + } + + @Test + public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() { + final List<RpcError> rpcErrors = List.of( + RpcResultBuilder.newError(ErrorType.TRANSPORT, new ErrorTag("bogusTag"), "foo"), + RpcResultBuilder.newWarning(ErrorType.RPC, ErrorTag.IN_USE, "bar", "app-tag", null, null)); + + final DOMRpcResult result = new DefaultDOMRpcResult(rpcErrors); + final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(path), any()); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + + // We are performing pass-through here of error-tag, hence the tag remains as specified, but we want to make + // sure the HTTP status remains the same as + final ErrorTag bogus = new ErrorTag("bogusTag"); + verifyRestconfDocumentedException(ex, 0, ErrorType.TRANSPORT, bogus, Optional.of("foo"), Optional.empty()); + assertEquals(ErrorTags.statusOf(ErrorTag.OPERATION_FAILED), ErrorTags.statusOf(bogus)); + + verifyRestconfDocumentedException(ex, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"), + Optional.of("app-tag")); + } + + @Test + public void testInvokeRpcWithNoPayload_Success() { + final NormalizedNode resultObj = null; + final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj); + + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any()); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo); + assertNotNull(output); + assertEquals(null, output.getData()); + // additional validation in the fact that the restconfImpl does not + // throw an exception. + } + + @Test + public void testInvokeRpcWithEmptyOutput() { + final ContainerNode resultObj = mock(ContainerNode.class); + doReturn(Set.of()).when(resultObj).body(); + doCallRealMethod().when(resultObj).isEmpty(); + final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj); + + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any()); + + WebApplicationException exceptionToBeThrown = assertThrows(WebApplicationException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), exceptionToBeThrown.getResponse().getStatus()); + } + + @Test + public void testInvokeRpcMethodWithBadMethodName() { + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:bad-method", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT, + Optional.empty(), Optional.empty()); + } + + @Test + @Ignore + public void testInvokeRpcMethodWithInput() { + final DOMRpcResult expResult = mock(DOMRpcResult.class); + final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast"); + + final Module rpcModule = schemaContext.findModules("toaster").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "make-toast"); + + RpcDefinition rpcDef = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcDef = rpc; + break; + } + } + + assertNotNull(rpcDef); + + final NormalizedNodeContext payload = new NormalizedNodeContext( + InstanceIdentifierContext.ofLocalRpcInput(schemaContext, rpcDef), + SchemaAwareBuilders.containerBuilder(rpcDef.getInput()).build()); + + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(path), any(NormalizedNode.class)); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:make-toast", payload, uriInfo); + assertNotNull(output); + assertEquals(null, output.getData()); + // additional validation in the fact that the restconfImpl does not + // throw an exception. + } + + @Test + public void testThrowExceptionWhenSlashInModuleName() { + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster/slash", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + Optional.empty(), Optional.empty()); + } + + @Test + public void testInvokeRpcWithNoPayloadWithOutput_Success() { + final SchemaContext schema = controllerContext.getGlobalSchema(); + final Module rpcModule = schema.findModules("toaster").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "testOutput"); + final QName rpcOutputQName = QName.create(rpcModule.getQNameModule(),"output"); + + RpcDefinition rpcDef = null; + ContainerLike rpcOutputSchemaNode = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcOutputSchemaNode = rpc.getOutput(); + rpcDef = rpc; + break; + } + } + assertNotNull(rpcDef); + assertNotNull(rpcOutputSchemaNode); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder = + SchemaAwareBuilders.containerBuilder(rpcOutputSchemaNode); + final DataSchemaNode leafSchema = rpcOutputSchemaNode + .getDataChildByName(QName.create(rpcModule.getQNameModule(), "textOut")); + assertTrue(leafSchema instanceof LeafSchemaNode); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) leafSchema); + leafBuilder.withValue("brm"); + containerBuilder.withChild(leafBuilder.build()); + final ContainerNode container = containerBuilder.build(); + + final DOMRpcResult result = new DefaultDOMRpcResult(container); + + doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(rpcDef.getQName()), any()); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:testOutput", null, uriInfo); + assertNotNull(output); + assertNotNull(output.getData()); + assertSame(container, output.getData()); + assertNotNull(output.getInstanceIdentifierContext()); + assertNotNull(output.getInstanceIdentifierContext().getSchemaContext()); + } + + /** + * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context. + * This rpc is then executed. + * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is + * invoked. + */ + @Test + public void testMountedRpcCallNoPayload_Success() throws Exception { + // FIXME find how to use mockito for it + } +} |