From c389acda66a92a03d358f1fa1340ec784c76e2b6 Mon Sep 17 00:00:00 2001 From: Ivan Hrasko Date: Wed, 19 Oct 2016 15:15:42 +0200 Subject: [PATCH] Unit test for RestconfInvokeOperationsUtil class Fixed so it add rpcError to the result when there is one. Change-Id: I5e3e924a0e80970f2f754e2ec39d58e4b0a06984 Signed-off-by: miroslav.kovac Signed-off-by: Ivan Hrasko --- .../restful/utils/FutureCallbackTx.java | 16 +- .../utils/RestconfInvokeOperationsUtil.java | 2 +- .../RestconfInvokeOperationsUtilTest.java | 103 +++++++++++++ .../restconf/restful/utils/TestData.java | 140 ++++++++++++++++++ 4 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtilTest.java create mode 100644 restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/TestData.java diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java index a4dbe6fa23..01760376b9 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java @@ -10,9 +10,15 @@ package org.opendaylight.restconf.restful.utils; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +52,7 @@ final class FutureCallbackTx { @Override public void onFailure(final Throwable t) { responseWaiter.countDown(); - handlingLoggerAndValues(t, txType, null, null); + handlingLoggerAndValues(t, txType, null, dataFactory); } @Override @@ -85,7 +91,13 @@ final class FutureCallbackTx { final T result, final FutureDataFactory dataFactory) { if (t != null) { LOG.warn("Transaction({}) FAILED!", txType, t); - throw new RestconfDocumentedException(" Transaction(" + txType + ") not committed correctly", t); + if (t instanceof DOMRpcException) { + final List rpcErrorList = new ArrayList<>(); + rpcErrorList.add(RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", t.getMessage())); + dataFactory.setResult((T) new DefaultDOMRpcResult(rpcErrorList)); + } else { + throw new RestconfDocumentedException(" Transaction(" + txType + ") not committed correctly", t); + } } else { LOG.trace("Transaction({}) SUCCESSFUL!", txType); dataFactory.setResult(result); diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtil.java index 39f5890043..023e70dd53 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtil.java @@ -94,7 +94,7 @@ public class RestconfInvokeOperationsUtil { return null; } try { - if ((response.getErrors() == null) || response.getErrors().isEmpty()) { + if (response.getErrors().isEmpty()) { return response; } LOG.debug("RpcError message", response.getErrors()); diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtilTest.java new file mode 100644 index 0000000000..7935895166 --- /dev/null +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtilTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 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.restconf.restful.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doReturn; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.Futures; +import java.util.Collection; +import java.util.Collections; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.restconf.handlers.RpcServiceHandler; +import org.opendaylight.yangtools.yang.common.RpcError; + +public class RestconfInvokeOperationsUtilTest { + + private static final TestData data = new TestData(); + + private RpcServiceHandler serviceHandler; + @Mock + private DOMRpcService rpcService; + @Mock + private DOMMountPoint moutPoint; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + serviceHandler = new RpcServiceHandler(rpcService); + } + + @Test + public void invokeRpcTest() { + final DOMRpcResult mockResult = new DefaultDOMRpcResult(data.output, Collections.emptyList()); + doReturn(Futures.immediateCheckedFuture(mockResult)).when(rpcService).invokeRpc(data.rpc, data.input); + final DOMRpcResult rpcResult = RestconfInvokeOperationsUtil.invokeRpc(data.input, data.rpc, serviceHandler); + Assert.assertTrue(rpcResult.getErrors().isEmpty()); + assertEquals(data.output, rpcResult.getResult()); + } + + @Test(expected = RestconfDocumentedException.class) + public void invokeRpcErrorsAndCheckTestTest() { + final DOMRpcException exception = new DOMRpcImplementationNotAvailableException("No implementation of RPC " + data.errorRpc.toString() + " availible"); + doReturn(Futures.immediateFailedCheckedFuture(exception)).when(rpcService).invokeRpc(data.errorRpc, data.input); + final DOMRpcResult rpcResult = RestconfInvokeOperationsUtil.invokeRpc(data.input, data.errorRpc, serviceHandler); + assertNull(rpcResult.getResult()); + final Collection errorList = rpcResult.getErrors(); + assertEquals(1, errorList.size()); + final RpcError actual = errorList.iterator().next(); + assertEquals("No implementation of RPC " + data.errorRpc.toString() + " availible", actual.getMessage()); + assertEquals("operation-failed", actual.getTag()); + assertEquals(RpcError.ErrorType.RPC, actual.getErrorType()); + RestconfInvokeOperationsUtil.checkResponse(rpcResult); + } + + @Test + public void invokeRpcViaMountPointTest() { + doReturn(Optional.fromNullable(rpcService)).when(moutPoint).getService(DOMRpcService.class); + final DOMRpcResult mockResult = new DefaultDOMRpcResult(data.output, Collections.emptyList()); + doReturn(Futures.immediateCheckedFuture(mockResult)).when(rpcService).invokeRpc(data.rpc, data.input); + final DOMRpcResult rpcResult = RestconfInvokeOperationsUtil.invokeRpcViaMountPoint(moutPoint, data.input, data.rpc); + Assert.assertTrue(rpcResult.getErrors().isEmpty()); + assertEquals(data.output, rpcResult.getResult()); + } + + @Test(expected = RestconfDocumentedException.class) + public void invokeRpcMissingMountPointServiceTest() { + doReturn(Optional.absent()).when(moutPoint).getService(DOMRpcService.class); + final DOMRpcResult mockResult = new DefaultDOMRpcResult(data.output, Collections.emptyList()); + doReturn(Futures.immediateCheckedFuture(mockResult)).when(rpcService).invokeRpc(data.rpc, data.input); + final DOMRpcResult rpcResult = RestconfInvokeOperationsUtil.invokeRpcViaMountPoint(moutPoint, data.input, data.rpc); + } + + @Test + public void checkResponseTest() { + final DOMRpcResult mockResult = new DefaultDOMRpcResult(data.output, Collections.emptyList()); + doReturn(Futures.immediateCheckedFuture(mockResult)).when(rpcService).invokeRpc(data.rpc, data.input); + final DOMRpcResult rpcResult = RestconfInvokeOperationsUtil.invokeRpc(data.input, data.rpc, serviceHandler); + Assert.assertTrue(rpcResult.getErrors().isEmpty()); + assertEquals(data.output, rpcResult.getResult()); + assertNotNull(RestconfInvokeOperationsUtil.checkResponse(rpcResult)); + } + +} diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/TestData.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/TestData.java new file mode 100644 index 0000000000..265acea3eb --- /dev/null +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/TestData.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016 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.restconf.restful.utils; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +class TestData { + + final YangInstanceIdentifier path; + final YangInstanceIdentifier path2; + final YangInstanceIdentifier path3; + final MapEntryNode data; + final MapEntryNode data2; + final ContainerNode data3; + final ContainerNode data4; + final MapNode listData; + final MapNode listData2; + final UnkeyedListEntryNode unkeyedListEntryNode; + final LeafNode contentLeaf; + final LeafNode contentLeaf2; + final MapEntryNode checkData; + final SchemaPath rpc; + final SchemaPath errorRpc; + final ContainerNode input; + final ContainerNode output; + + TestData() { + final QName base = QName.create("ns", "2016-02-28", "base"); + final QName listQname = QName.create(base, "list"); + final QName listKeyQName = QName.create(base, "list-key"); + final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey = + new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQName, "keyValue"); + final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey2 = + new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQName, "keyValue2"); + final LeafNode content = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "leaf-content"))) + .withValue("content") + .build(); + final LeafNode content2 = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "leaf-content-different"))) + .withValue("content-different") + .build(); + final DataContainerChild dataContainer = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(listQname, "identifier"))) + .withValue("id") + .build(); + unkeyedListEntryNode = Builders.unkeyedListEntryBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(listQname, "list"))) + .withChild(dataContainer) + .build(); + data = Builders.mapEntryBuilder() + .withNodeIdentifier(nodeWithKey) + .withChild(content) + .build(); + data2 = Builders.mapEntryBuilder() + .withNodeIdentifier(nodeWithKey) + .withChild(content2) + .build(); + checkData = Builders.mapEntryBuilder() + .withNodeIdentifier(nodeWithKey) + .withChild(content2) + .withChild(content) + .build(); + listData = Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(listQname, "list"))) + .withChild(data) + .build(); + listData2 = Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(listQname, "list"))) + .withChild(data) + .withChild(data2) + .build(); + path = YangInstanceIdentifier.builder() + .node(QName.create(base, "cont")) + .node(listQname) + .node(nodeWithKey) + .build(); + path2 = YangInstanceIdentifier.builder() + .node(QName.create(base, "cont")) + .node(listQname) + .node(nodeWithKey2) + .build(); + path3 = YangInstanceIdentifier.builder() + .node(QName.create(base, "cont")) + .node(listQname) + .build(); + contentLeaf = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "content"))) + .withValue("test") + .build(); + contentLeaf2 = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "content2"))) + .withValue("test2") + .build(); + data3 = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "container"))) + .withChild(contentLeaf) + .build(); + data4 = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(base, "container2"))) + .withChild(contentLeaf2) + .build(); + + + final QName rpcQname = QName.create("ns", "2015-02-28", "test-rpc"); + final QName errorRpcQname = QName.create(rpcQname, "error-rpc"); + rpc = SchemaPath.create(true, rpcQname); + errorRpc = SchemaPath.create(true, errorRpcQname); + final LeafNode contentLeafNode = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(rpcQname, "content"))) + .withValue("test") + .build(); + input = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(rpcQname, "input"))) + .withChild(contentLeafNode) + .build(); + final LeafNode resultLeafNode = Builders.leafBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(rpcQname, "content"))) + .withValue("operation result") + .build(); + output = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(rpcQname, "output"))) + .withChild(resultLeafNode) + .build(); + } +} \ No newline at end of file -- 2.36.6