BUG-8507: Fix replayed directCommit() on reconnect
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / FrontendReadWriteTransactionTest.java
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/FrontendReadWriteTransactionTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/FrontendReadWriteTransactionTest.java
new file mode 100644 (file)
index 0000000..c3c6206
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.cluster.datastore;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorRef;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.access.commands.ModifyTransactionRequestBuilder;
+import org.opendaylight.controller.cluster.access.commands.ReadTransactionRequest;
+import org.opendaylight.controller.cluster.access.commands.TransactionModification;
+import org.opendaylight.controller.cluster.access.commands.TransactionRequest;
+import org.opendaylight.controller.cluster.access.commands.TransactionSuccess;
+import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendType;
+import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.MemberName;
+import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
+import org.opendaylight.controller.cluster.access.concepts.RequestException;
+import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+
+public class FrontendReadWriteTransactionTest {
+
+    private static final ClientIdentifier CLIENT_ID = ClientIdentifier.create(FrontendIdentifier.create(
+        MemberName.forName("mock"), FrontendType.forName("mock")), 0);
+    private static final LocalHistoryIdentifier HISTORY_ID = new LocalHistoryIdentifier(CLIENT_ID, 0);
+    private static final TransactionIdentifier TX_ID = new TransactionIdentifier(HISTORY_ID, 0);
+
+    private AbstractFrontendHistory mockHistory;
+    private ReadWriteShardDataTreeTransaction shardTransaction;
+    private DataTreeModification mockModification;
+    private ShardDataTreeTransactionParent mockParent;
+    private FrontendReadWriteTransaction openTx;
+    private ShardDataTreeCohort mockCohort;
+
+    @Before
+    public void setup() {
+        mockHistory = mock(AbstractFrontendHistory.class);
+        mockParent = mock(ShardDataTreeTransactionParent.class);
+        mockModification = mock(DataTreeModification.class);
+        mockCohort = mock(ShardDataTreeCohort.class);
+
+        shardTransaction = new ReadWriteShardDataTreeTransaction(mockParent, TX_ID, mockModification);
+        openTx = FrontendReadWriteTransaction.createOpen(mockHistory, shardTransaction);
+
+        when(mockParent.finishTransaction(same(shardTransaction))).thenReturn(mockCohort);
+    }
+
+    private TransactionSuccess<?> handleRequest(final TransactionRequest<?> request) throws RequestException {
+        return openTx.doHandleRequest(request, new RequestEnvelope(request, 0, 0), 0);
+    }
+
+    @Test
+    public void testDuplicateModifyAbort() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setAbort();
+        final TransactionRequest<?> abortReq = b.build();
+        assertNull(handleRequest(abortReq));
+        verify(mockParent).abortTransaction(same(shardTransaction), any(Runnable.class));
+
+        assertNull(handleRequest(abortReq));
+        verifyNoMoreInteractions(mockParent);
+    }
+
+    @Test
+    public void testDuplicateReady() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setReady();
+        final TransactionRequest<?> readyReq = b.build();
+
+        assertNotNull(handleRequest(readyReq));
+        verify(mockParent).finishTransaction(same(shardTransaction));
+
+        assertNotNull(handleRequest(readyReq));
+        verifyNoMoreInteractions(mockParent);
+    }
+
+    @Test
+    public void testDuplicateDirect() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setCommit(false);
+        final TransactionRequest<?> readyReq = b.build();
+
+        assertNull(handleRequest(readyReq));
+        verify(mockParent).finishTransaction(same(shardTransaction));
+
+        assertNull(handleRequest(readyReq));
+        verifyNoMoreInteractions(mockParent);
+    }
+
+    @Test
+    public void testDuplicateCoordinated() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setCommit(true);
+        final TransactionRequest<?> readyReq = b.build();
+
+        assertNull(handleRequest(readyReq));
+        verify(mockParent).finishTransaction(same(shardTransaction));
+
+        assertNull(handleRequest(readyReq));
+        verifyNoMoreInteractions(mockParent);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReadAfterReady() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setReady();
+        final TransactionRequest<?> readyReq = b.build();
+
+        assertNotNull(handleRequest(readyReq));
+        verify(mockParent).finishTransaction(same(shardTransaction));
+
+        handleRequest(new ReadTransactionRequest(TX_ID, 0, mock(ActorRef.class), YangInstanceIdentifier.EMPTY, true));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testModifyAfterReady() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setReady();
+        final TransactionRequest<?> readyReq = b.build();
+
+        assertNotNull(handleRequest(readyReq));
+        verify(mockParent).finishTransaction(same(shardTransaction));
+
+        b.setSequence(1);
+        b.addModification(mock(TransactionModification.class));
+        handleRequest(b.build());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReadAfterAbort() throws RequestException {
+        final ModifyTransactionRequestBuilder b = new ModifyTransactionRequestBuilder(TX_ID, mock(ActorRef.class));
+        b.setSequence(0);
+        b.setAbort();
+        final TransactionRequest<?> abortReq = b.build();
+        assertNull(handleRequest(abortReq));
+        verify(mockParent).abortTransaction(same(shardTransaction), any(Runnable.class));
+
+        handleRequest(new ReadTransactionRequest(TX_ID, 0, mock(ActorRef.class), YangInstanceIdentifier.EMPTY, true));
+    }
+}