Netconf-topology module unit tests 43/34343/2
authorJakub Morvay <jmorvay@cisco.com>
Tue, 9 Feb 2016 10:32:26 +0000 (11:32 +0100)
committerTomas Cere <tcere@cisco.com>
Tue, 29 Mar 2016 14:28:29 +0000 (14:28 +0000)
Change-Id: I8258ce4973758ef20d503712147e34ee78a1cd95
Signed-off-by: Jakub Morvay <jmorvay@cisco.com>
(cherry picked from commit 32e6bedfee7fa221f28aea8694ed36965c9b905b)

opendaylight/netconf/abstract-topology/src/main/java/org/opendaylight/netconf/topology/util/TopologyUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/OnlySuccessStateAggregator.java [deleted file]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfNodeOperationalDataAggregatorTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/ClusteredNetconfDeviceCommunicatorTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/TopologyMountPointFacadeTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyReadOnlyTransactionTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyWriteOnlyTransactionTest.java [new file with mode: 0644]

diff --git a/opendaylight/netconf/abstract-topology/src/main/java/org/opendaylight/netconf/topology/util/TopologyUtil.java b/opendaylight/netconf/abstract-topology/src/main/java/org/opendaylight/netconf/topology/util/TopologyUtil.java
new file mode 100644 (file)
index 0000000..273d062
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.netconf.topology.util;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class TopologyUtil {
+
+    private TopologyUtil() {
+        throw new AssertionError("Instantiating utility class.");
+    }
+
+    /**
+     * Determines the Netconf Node Node ID, given the node's instance
+     * identifier.
+     *
+     * @param pathArgument Node's path argument
+     * @return     NodeId for the node
+     */
+    public static NodeId getNodeId(final InstanceIdentifier.PathArgument pathArgument) {
+        if (pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>) {
+
+            final Identifier key = ((InstanceIdentifier.IdentifiableItem) pathArgument).getKey();
+            if(key instanceof NodeKey) {
+                return ((NodeKey) key).getNodeId();
+            }
+        }
+        throw new IllegalStateException("Unable to create NodeId from: " + pathArgument);
+    }
+
+    public static InstanceIdentifier<Topology> createTopologyId(final String topologyId) {
+        final InstanceIdentifier<NetworkTopology> networkTopology = InstanceIdentifier.create(NetworkTopology.class);
+        return networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId)));
+    }
+}
diff --git a/opendaylight/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/OnlySuccessStateAggregator.java b/opendaylight/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/OnlySuccessStateAggregator.java
deleted file mode 100644 (file)
index 7a24587..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2015 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.netconf.topology.impl;
-
-import akka.actor.TypedActor;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import java.util.List;
-import org.opendaylight.netconf.topology.StateAggregator;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class OnlySuccessStateAggregator implements StateAggregator{
-
-    private static final Logger LOG = LoggerFactory.getLogger(OnlySuccessStateAggregator.class);
-
-    @Override
-    public ListenableFuture<Node> combineCreateAttempts(List<ListenableFuture<Node>> stateFutures) {
-        final SettableFuture<Node> future = SettableFuture.create();
-        final ListenableFuture<List<Node>> allAsList = Futures.allAsList(stateFutures);
-        Futures.addCallback(allAsList, new FutureCallback<List<Node>>() {
-            @Override
-            public void onSuccess(List<Node> result) {
-                for (int i = 0; i < result.size() - 1; i++) {
-                    if (!result.get(i).equals(result.get(i + 1))) {
-                        future.setException(new IllegalStateException("Create futures have different result"));
-                    }
-                }
-                future.set(result.get(0));
-            }
-
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.error("One of the combined create attempts failed {}", t);
-                future.setException(t);
-            }
-        }, TypedActor.context().dispatcher());
-
-        return future;
-    }
-
-    @Override
-    public ListenableFuture<Node> combineUpdateAttempts(List<ListenableFuture<Node>> stateFutures) {
-        final SettableFuture<Node> future = SettableFuture.create();
-        final ListenableFuture<List<Node>> allAsList = Futures.allAsList(stateFutures);
-        Futures.addCallback(allAsList, new FutureCallback<List<Node>>() {
-            @Override
-            public void onSuccess(List<Node> result) {
-                for (int i = 0; i < result.size() - 1; i++) {
-                    if (!result.get(i).equals(result.get(i + 1))) {
-                        future.setException(new IllegalStateException("Update futures have different result"));
-                    }
-                }
-                future.set(result.get(0));
-            }
-
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.error("One of the combined update attempts failed {}", t);
-                future.setException(t);
-            }
-        });
-        return future;
-    }
-
-    @Override
-    public ListenableFuture<Void> combineDeleteAttempts(List<ListenableFuture<Void>> stateFutures) {
-        final SettableFuture<Void> future = SettableFuture.create();
-        final ListenableFuture<List<Void>> allAsList = Futures.allAsList(stateFutures);
-        Futures.addCallback(allAsList, new FutureCallback<List<Void>>() {
-            @Override
-            public void onSuccess(List<Void> result) {
-                future.set(null);
-            }
-
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.error("One of the combined delete attempts failed {}", t);
-                future.setException(t);
-            }
-        });
-        return future;
-    }
-}
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfNodeOperationalDataAggregatorTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfNodeOperationalDataAggregatorTest.java
new file mode 100644 (file)
index 0000000..d0976b4
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.netconf.topology.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.clustered.connection.status.NodeStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.clustered.connection.status.NodeStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+
+public class NetconfNodeOperationalDataAggregatorTest {
+
+    private List<ListenableFuture<Node>> stateFutures;
+
+    private NetconfNodeOperationalDataAggregator aggregator;
+
+    @Before
+    public void setUp() {
+        aggregator = new NetconfNodeOperationalDataAggregator();
+        stateFutures = Lists.newArrayList();
+    }
+
+    @Test
+    public void testCombineCreateAttempts() throws ExecutionException, InterruptedException {
+        NetconfNode testingNode = new NetconfNodeBuilder().setAvailableCapabilities(
+                new AvailableCapabilitiesBuilder().setAvailableCapability(Lists.<String>newArrayList()).build())
+                .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder().setNodeStatus(Lists.newArrayList(
+                        new NodeStatusBuilder().setStatus(NodeStatus.Status.Connected).build())).build())
+                .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected).build();
+        stateFutures.add(Futures.immediateFuture(new NodeBuilder().addAugmentation(NetconfNode.class, testingNode).build()));
+
+        ListenableFuture<Node> aggregatedCreateFuture = aggregator.combineCreateAttempts(stateFutures);
+        assertTrue(aggregatedCreateFuture.isDone());
+
+        NetconfNode aggregatedNode = aggregatedCreateFuture.get().getAugmentation(NetconfNode.class);
+        assertEquals(aggregatedNode.getClusteredConnectionStatus().getNodeStatus().get(0).getStatus(),
+                NodeStatus.Status.Connected);
+    }
+
+    @Test
+    public void testSuccessfulCombineUpdateAttempts() throws ExecutionException, InterruptedException {
+        NetconfNode testingNode = new NetconfNodeBuilder().setAvailableCapabilities(
+                new AvailableCapabilitiesBuilder().setAvailableCapability(Lists.<String>newArrayList()).build())
+                .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder().setNodeStatus(Lists.newArrayList(
+                        new NodeStatusBuilder().setStatus(NodeStatus.Status.Connected).build())).build())
+                .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected).build();
+        stateFutures.add(Futures.immediateFuture(new NodeBuilder().addAugmentation(NetconfNode.class, testingNode).build()));
+
+        ListenableFuture<Node> aggregatedUpdateFuture = aggregator.combineUpdateAttempts(stateFutures);
+        assertTrue(aggregatedUpdateFuture.isDone());
+
+        NetconfNode aggregatedNode = aggregatedUpdateFuture.get().getAugmentation(NetconfNode.class);
+        assertEquals(aggregatedNode.getClusteredConnectionStatus().getNodeStatus().get(0).getStatus(),
+                NodeStatus.Status.Connected);
+    }
+
+    @Test
+    public void testSuccessfulCombineDeleteAttempts() throws ExecutionException, InterruptedException {
+        List deleteStateFutures = Lists.newArrayList(Futures.immediateFuture(null), Futures.immediateFuture(null));
+
+        ListenableFuture<Void> deleteFuture = aggregator.combineDeleteAttempts(deleteStateFutures);
+        assertTrue(deleteFuture.isDone());
+        assertEquals(deleteFuture.get(), null);
+    }
+
+    @Test
+    public void testFailedCombineDeleteAttempts() {
+        Exception cause = new Exception("Fail");
+        List deleteStateFutures = Lists.newArrayList(Futures.immediateFuture(null), Futures.immediateFuture(null),
+                Futures.immediateFailedFuture(cause));
+
+        ListenableFuture<Void> deleteFuture = aggregator.combineDeleteAttempts(deleteStateFutures);
+        assertTrue(deleteFuture.isDone());
+
+        try {
+            deleteFuture.get();
+            fail("Exception expected");
+        } catch(Exception e) {
+            assertSame(e.getCause(), cause);
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java
new file mode 100644 (file)
index 0000000..1c84668
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * 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.netconf.topology.impl;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorContext;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.ImmediateEventExecutor;
+import io.netty.util.concurrent.SucceededFuture;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.util.TopologyUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public class NetconfTopologyImplTest {
+
+    private static final NodeId NODE_ID = new NodeId("testing-node");
+    private static final String TOPOLOGY_ID = "testing-topology";
+
+    @Mock
+    private Broker mockedDataBroker;
+
+    @Mock
+    private NetconfClientDispatcher mockedClientDispatcher;
+
+    @Mock
+    private BindingAwareBroker mockedBindingAwareBroker;
+
+    @Mock
+    private EventExecutor mockedEventExecutor;
+
+    @Mock
+    private ScheduledThreadPool mockedKeepaliveExecutor;
+
+    @Mock
+    private ThreadPool mockedProcessingExecutor;
+
+    @Mock
+    private SchemaRepositoryProvider mockedSchemaRepositoryProvider;
+
+
+    private TestingNetconfTopologyImpl topology;
+    private TestingNetconfTopologyImpl spyTopology;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mockedSchemaRepositoryProvider.getSharedSchemaRepository()).thenReturn(new SharedSchemaRepository("testingSharedSchemaRepo"));
+        when(mockedProcessingExecutor.getExecutor()).thenReturn(MoreExecutors.newDirectExecutorService());
+        Future future = new SucceededFuture(ImmediateEventExecutor.INSTANCE, new NetconfDeviceCapabilities());
+        when(mockedClientDispatcher.createReconnectingClient(any(NetconfReconnectingClientConfiguration.class))).thenReturn(future);
+
+        topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedBindingAwareBroker,
+                mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider);
+
+        spyTopology = spy(topology);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRegisterMountPointNotSupported() {
+        ActorContext context = mock(ActorContext.class);
+        topology.registerMountPoint(context, NODE_ID);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testUnregisterMountPointNotSupported() {
+        topology.unregisterMountPoint(NODE_ID);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRegisterConnectionStatusListener() {
+        RemoteDeviceHandler<NetconfSessionPreferences> listener = mock(RemoteDeviceHandler.class);
+        topology.registerConnectionStatusListener(NODE_ID, listener);
+    }
+
+    @Test
+    public void testOnSessionInitiated() {
+        BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class);
+        DataBroker dataBroker = mock(DataBroker.class);
+        when(session.getSALService(DataBroker.class)).thenReturn(dataBroker);
+        WriteTransaction wtx = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+        doNothing().when(wtx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
+        when(wtx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
+        topology.onSessionInitiated(session);
+
+        //verify initialization of topology
+        final InstanceIdentifier<NetworkTopology> networkTopologyId = InstanceIdentifier.builder(NetworkTopology.class).build();
+        final Topology topo = new TopologyBuilder().setTopologyId(new TopologyId(TOPOLOGY_ID)).build();
+        final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
+        verify(wtx).merge(LogicalDatastoreType.CONFIGURATION, networkTopologyId, networkTopology);
+        verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, networkTopologyId, networkTopology);
+        verify(wtx).merge(LogicalDatastoreType.CONFIGURATION, networkTopologyId.child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))), topo);
+        verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, networkTopologyId.child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))), topo);
+    }
+
+    @Test
+    public void testOnDataTreeChange() {
+
+        DataObjectModification<Node> newNode = mock(DataObjectModification.class);
+        when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
+
+        InstanceIdentifier.PathArgument pa = null;
+        for (InstanceIdentifier.PathArgument p : TopologyUtil.createTopologyId(TOPOLOGY_ID).child(Node.class, new NodeKey(NODE_ID)).getPathArguments()) {
+            pa = p;
+        }
+
+        when(newNode.getIdentifier()).thenReturn(pa);
+
+
+        NetconfNode testingNode = new NetconfNodeBuilder()
+                .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
+                .setPort(new PortNumber(9999))
+                .setReconnectOnChangedSchema(true)
+                .setDefaultRequestTimeoutMillis(1000L)
+                .setBetweenAttemptsTimeoutMillis(100)
+                .setKeepaliveDelay(1000L)
+                .setTcpOnly(true)
+                .setCredentials(new LoginPasswordBuilder().setUsername("testuser").setPassword("testpassword").build())
+                .build();
+
+        NodeBuilder nn = new NodeBuilder().addAugmentation(NetconfNode.class, testingNode);
+
+        when(newNode.getDataAfter()).thenReturn(nn.build());
+
+
+
+        Collection<DataTreeModification<Node>> changes = Sets.newHashSet();
+        DataTreeModification<Node> ch = mock(DataTreeModification.class);
+        when(ch.getRootNode()).thenReturn(newNode);
+        changes.add(ch);
+        spyTopology.onDataTreeChanged(changes);
+        verify(spyTopology).connectNode(TopologyUtil.getNodeId(pa), nn.build());
+
+        when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
+        spyTopology.onDataTreeChanged(changes);
+        verify(spyTopology).disconnectNode(TopologyUtil.getNodeId(pa));
+
+        when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED);
+        spyTopology.onDataTreeChanged(changes);
+
+        //one in previous creating and deleting node and one in updating
+        verify(spyTopology, times(2)).disconnectNode(TopologyUtil.getNodeId(pa));
+        verify(spyTopology, times(2)).connectNode(TopologyUtil.getNodeId(pa), nn.build());
+
+
+    }
+
+    public static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
+
+        public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher, BindingAwareBroker bindingAwareBroker, Broker domBroker, EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor, ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider) {
+            super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
+        }
+
+        @Override
+        public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
+            return Futures.immediateFuture(new NetconfDeviceCapabilities());
+        }
+
+        @Override
+        public ListenableFuture<Void> disconnectNode(NodeId nodeId) {
+            return Futures.immediateFuture(null);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/ClusteredNetconfDeviceCommunicatorTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/ClusteredNetconfDeviceCommunicatorTest.java
new file mode 100644 (file)
index 0000000..64fe5fa
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.netconf.topology.pipeline;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.net.InetSocketAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.netconf.api.NetconfTerminationReason;
+import org.opendaylight.netconf.client.NetconfClientSession;
+import org.opendaylight.netconf.client.NetconfClientSessionListener;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+
+public class ClusteredNetconfDeviceCommunicatorTest {
+
+    private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+
+    @Mock
+    private ClusteredNetconfDevice remoteDevice;
+
+    @Mock
+    private EntityOwnershipService ownershipService;
+
+    @Mock
+    private NetconfClientSession session;
+
+    @Mock
+    private NetconfClientSessionListener listener1;
+
+    @Mock
+    private NetconfClientSessionListener listener2;
+
+    @Mock
+    private EntityOwnershipListenerRegistration ownershipListenerRegistration;
+
+    private ClusteredNetconfDeviceCommunicator communicator;
+
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(ownershipListenerRegistration).when(ownershipService).registerListener(
+                "netconf-node/" + REMOTE_DEVICE_ID.getName(), remoteDevice);
+
+        communicator = new ClusteredNetconfDeviceCommunicator(REMOTE_DEVICE_ID, remoteDevice, ownershipService);
+        communicator.registerNetconfClientSessionListener(listener1);
+        communicator.registerNetconfClientSessionListener(listener2);
+    }
+
+    @Test
+    public void testOnSessionDown() {
+        communicator.onSessionUp(session);
+
+        Exception exception = mock(Exception.class);
+        communicator.onSessionDown(session, exception);
+
+        verify(ownershipListenerRegistration).close();
+
+        verify(listener1).onSessionDown(eq(session), eq(exception));
+        verify(listener2).onSessionDown(eq(session), eq(exception));
+    }
+
+    @Test
+    public void testOnSessionUp() {
+        communicator.onSessionUp(session);
+
+        verify(ownershipService).registerListener("netconf-node/" + REMOTE_DEVICE_ID.getName(), remoteDevice);
+
+        verify(listener1).onSessionUp(eq(session));
+        verify(listener2).onSessionUp(eq(session));
+    }
+
+    @Test
+    public void testOnSessionTerminated() {
+        communicator.onSessionUp(session);
+
+        NetconfTerminationReason reason = mock(NetconfTerminationReason.class);
+        communicator.onSessionTerminated(session, reason);
+
+        verify(ownershipListenerRegistration).close();
+
+        verify(listener1).onSessionTerminated(eq(session), eq(reason));
+        verify(listener2).onSessionTerminated(eq(session), eq(reason));
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/TopologyMountPointFacadeTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/TopologyMountPointFacadeTest.java
new file mode 100644 (file)
index 0000000..2b4b374
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.netconf.topology.pipeline;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class TopologyMountPointFacadeTest {
+
+    private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+    private static final String TOPOLOGY_ID = "testing-topology";
+
+    @Mock
+    Broker domBroker;
+
+    @Mock
+    BindingAwareBroker bindingBroker;
+
+    @Mock
+    RemoteDeviceHandler<NetconfSessionPreferences> connectionStatusListener1;
+
+    @Mock
+    RemoteDeviceHandler<NetconfSessionPreferences> connectionStatusListener2;
+
+
+    private TopologyMountPointFacade mountPointFacade;
+
+    @Before
+    public void setUp() {
+
+        MockitoAnnotations.initMocks(this);
+
+        mountPointFacade = new TopologyMountPointFacade(TOPOLOGY_ID, REMOTE_DEVICE_ID, domBroker, bindingBroker, 1L);
+
+        mountPointFacade.registerConnectionStatusListener(connectionStatusListener1);
+        mountPointFacade.registerConnectionStatusListener(connectionStatusListener2);
+
+    }
+
+    @Test
+    public void testOnDeviceConnected() {
+        SchemaContext mockedContext = Mockito.mock(SchemaContext.class);
+        NetconfSessionPreferences mockedPreferences = NetconfSessionPreferences.fromStrings(Collections.<String>emptyList());
+        DOMRpcService mockedRpcService = Mockito.mock(DOMRpcService.class);
+        mountPointFacade.onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+
+        Mockito.verify(connectionStatusListener1).onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+        Mockito.verify(connectionStatusListener2).onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+    }
+
+    @Test
+    public void testOnDeviceDisconnected() {
+        mountPointFacade.onDeviceDisconnected();
+
+        Mockito.verify(connectionStatusListener1).onDeviceDisconnected();
+        Mockito.verify(connectionStatusListener2).onDeviceDisconnected();
+    }
+
+    @Test
+    public void testOnDeviceFailed() {
+        Throwable mockedException = Mockito.mock(Throwable.class);
+        mountPointFacade.onDeviceFailed(mockedException);
+
+        Mockito.verify(connectionStatusListener1).onDeviceFailed(mockedException);
+        Mockito.verify(connectionStatusListener2).onDeviceFailed(mockedException);
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyReadOnlyTransactionTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyReadOnlyTransactionTest.java
new file mode 100644 (file)
index 0000000..d7d7c94
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.netconf.topology.pipeline.tx;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorSystem;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.net.InetSocketAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.netconf.topology.pipeline.ProxyNetconfDeviceDataBroker;
+import org.opendaylight.netconf.topology.util.messages.NormalizedNodeMessage;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ProxyReadOnlyTransactionTest {
+    private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+    private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
+
+    @Mock
+    private ProxyNetconfDeviceDataBroker mockedProxyDataBroker;
+
+    @Mock
+    private ActorSystem mockedActorSystem;
+
+    @Mock
+    private NormalizedNodeMessage mockedNodeMessage;
+
+    @Mock
+    private NormalizedNode mockedNode;
+
+    private ProxyReadOnlyTransaction proxyReadOnlyTx;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mockedActorSystem.dispatcher()).thenReturn(ExecutionContexts.fromExecutorService(MoreExecutors.newDirectExecutorService()));
+        when(mockedNodeMessage.getNode()).thenReturn(mockedNode);
+
+        proxyReadOnlyTx = new ProxyReadOnlyTransaction(mockedActorSystem, REMOTE_DEVICE_ID, mockedProxyDataBroker);
+    }
+
+    @Test
+    public void testSuccessfulRead() throws ReadFailedException {
+        when(mockedProxyDataBroker.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+                .thenReturn(Futures.successful(Optional.of(mockedNodeMessage)));
+        CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readResultFuture =  proxyReadOnlyTx.read(LogicalDatastoreType.CONFIGURATION, path);
+        verify(mockedProxyDataBroker).read(eq(LogicalDatastoreType.CONFIGURATION), eq(path));
+        assertTrue(readResultFuture.isDone());
+        assertEquals(readResultFuture.checkedGet().get(), mockedNode);
+    }
+
+    @Test
+    public void testFailedRead() {
+        when(mockedProxyDataBroker.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+                .thenReturn(Futures.<Optional<NormalizedNodeMessage>>failed(new ReadFailedException("Test read failed!")));
+        CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readResultFuture =  proxyReadOnlyTx.read(LogicalDatastoreType.CONFIGURATION, path);
+        verify(mockedProxyDataBroker).read(eq(LogicalDatastoreType.CONFIGURATION), eq(path));
+        assertTrue(readResultFuture.isDone());
+        try {
+            readResultFuture.checkedGet();
+            fail("Exception expected");
+        } catch(Exception e) {
+            assertTrue(e instanceof ReadFailedException);
+        }
+    }
+
+
+    @Test
+    public void testFailedExists() {
+        when(mockedProxyDataBroker.exists(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+                .thenReturn(Futures.<Boolean>failed(new ReadFailedException("Test read failed!")));
+        CheckedFuture existsFuture = proxyReadOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path);
+        verify(mockedProxyDataBroker).exists(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+        assertTrue(existsFuture.isDone());
+        try {
+            existsFuture.checkedGet();
+            fail("Exception expected");
+        } catch(Exception e) {
+            assertTrue(e instanceof ReadFailedException);
+        }
+    }
+
+    @Test
+    public void testExists() throws Exception {
+        when(mockedProxyDataBroker.exists(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+            .thenReturn(Futures.successful(true));
+        CheckedFuture<Boolean, ReadFailedException> existsFuture = proxyReadOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path);
+        verify(mockedProxyDataBroker).exists(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+        assertTrue(existsFuture.isDone());
+        assertTrue(existsFuture.checkedGet());
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyWriteOnlyTransactionTest.java b/opendaylight/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/tx/ProxyWriteOnlyTransactionTest.java
new file mode 100644 (file)
index 0000000..83c118f
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.netconf.topology.pipeline.tx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorSystem;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.netconf.topology.pipeline.ProxyNetconfDeviceDataBroker;
+import org.opendaylight.netconf.topology.util.messages.NormalizedNodeMessage;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ProxyWriteOnlyTransactionTest {
+    private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
+    private ArgumentCaptor<NormalizedNodeMessage> nodeMessageArgumentCaptor;
+
+    @Mock
+    private ProxyNetconfDeviceDataBroker mockedDelegate;
+
+    @Mock
+    private ActorSystem mockedActorSystem;
+
+    @Mock
+    private NormalizedNode<?, ?> normalizedNode;
+
+    private ProxyWriteOnlyTransaction tx;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mockedActorSystem.dispatcher()).thenReturn(ExecutionContexts.fromExecutorService(MoreExecutors.newDirectExecutorService()));
+
+        nodeMessageArgumentCaptor = ArgumentCaptor.forClass(NormalizedNodeMessage.class);
+        tx = new ProxyWriteOnlyTransaction(mockedActorSystem, mockedDelegate);
+    }
+
+    @Test
+    public void testPut() {
+        doNothing().when(mockedDelegate).put(any(LogicalDatastoreType.class), any(NormalizedNodeMessage.class));
+        tx.put(LogicalDatastoreType.OPERATIONAL, path, normalizedNode);
+        verify(mockedDelegate).put(eq(LogicalDatastoreType.OPERATIONAL), nodeMessageArgumentCaptor.capture());
+        assertEquals(path, nodeMessageArgumentCaptor.getValue().getIdentifier());
+        assertEquals(normalizedNode, nodeMessageArgumentCaptor.getValue().getNode());
+    }
+
+    @Test
+    public void testMerge() {
+        doNothing().when(mockedDelegate).merge(any(LogicalDatastoreType.class), any(NormalizedNodeMessage.class));
+        tx.merge(LogicalDatastoreType.CONFIGURATION, path, normalizedNode);
+        verify(mockedDelegate).merge(eq(LogicalDatastoreType.CONFIGURATION), nodeMessageArgumentCaptor.capture());
+        assertEquals(path, nodeMessageArgumentCaptor.getValue().getIdentifier());
+        assertEquals(normalizedNode, nodeMessageArgumentCaptor.getValue().getNode());
+    }
+
+    @Test
+    public void testCancel() {
+        when(mockedDelegate.cancel()).thenReturn(true);
+        assertTrue(tx.cancel());
+        verify(mockedDelegate).cancel();
+    }
+
+    @Test
+    public void testDelete() {
+        doNothing().when(mockedDelegate).delete(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
+        tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+        verify(mockedDelegate).delete(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+    }
+
+    @Test
+    public void testSuccessfulSubmit() throws Exception {
+        when(mockedDelegate.submit()).thenReturn(Futures.<Void>successful(null));
+        CheckedFuture submitFuture = tx.submit();
+        verify(mockedDelegate).submit();
+        assertTrue(submitFuture.isDone());
+        assertEquals(submitFuture.checkedGet(), null);
+    }
+
+    @Test
+    public void testFailedSubmit() {
+        when(mockedDelegate.submit()).thenReturn(Futures.<Void>failed(new TransactionCommitFailedException("fail")));
+        CheckedFuture submitFuture = tx.submit();
+        verify(mockedDelegate).submit();
+        assertTrue(submitFuture.isDone());
+        try {
+            submitFuture.checkedGet();
+            fail("Exception expected");
+        } catch(Exception e) {
+            assertTrue(e instanceof TransactionCommitFailedException);
+        }
+    }
+
+    @Test
+    public void testSuccessfulCommit() throws ExecutionException, InterruptedException {
+        RpcResult<TransactionStatus> rpcResult = mock(RpcResult.class);
+        when(mockedDelegate.commit()).thenReturn(Futures.successful(rpcResult));
+        ListenableFuture<RpcResult<TransactionStatus>> submitFuture = tx.commit();
+        verify(mockedDelegate).commit();
+        assertTrue(submitFuture.isDone());
+        assertEquals(submitFuture.get(), rpcResult);
+    }
+
+    @Test
+    public void testFailedCommit() {
+        when(mockedDelegate.commit()).thenReturn(Futures.<RpcResult<TransactionStatus>>failed(new TransactionCommitFailedException("faile")));
+        ListenableFuture<RpcResult<TransactionStatus>> submitFuture = tx.commit();
+        verify(mockedDelegate).commit();
+        assertTrue(submitFuture.isDone());
+        try {
+            submitFuture.get();
+            fail("Exception expected");
+        } catch(Exception e) {
+            assertTrue(e.getCause() instanceof TransactionCommitFailedException);
+        }
+    }
+}
\ No newline at end of file