BUG-8538: rework transaction abort paths
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / databroker / actors / dds / LocalProxyTransactionTest.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.databroker.actors.dds;
9
10 import static org.mockito.Matchers.any;
11 import static org.mockito.Mockito.doAnswer;
12 import static org.mockito.Mockito.mock;
13 import static org.mockito.Mockito.verify;
14 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.assertFutureEquals;
15
16 import akka.testkit.TestProbe;
17 import com.google.common.base.Ticker;
18 import java.util.function.Consumer;
19 import org.junit.Assert;
20 import org.junit.Test;
21 import org.mockito.ArgumentCaptor;
22 import org.mockito.invocation.InvocationOnMock;
23 import org.mockito.stubbing.Answer;
24 import org.opendaylight.controller.cluster.access.client.ClientActorBehavior;
25 import org.opendaylight.controller.cluster.access.client.InternalCommand;
26 import org.opendaylight.controller.cluster.access.commands.AbortLocalTransactionRequest;
27 import org.opendaylight.controller.cluster.access.commands.CommitLocalTransactionRequest;
28 import org.opendaylight.controller.cluster.access.commands.ExistsTransactionRequest;
29 import org.opendaylight.controller.cluster.access.commands.ExistsTransactionSuccess;
30 import org.opendaylight.controller.cluster.access.commands.ModifyTransactionRequest;
31 import org.opendaylight.controller.cluster.access.commands.PersistenceProtocol;
32 import org.opendaylight.controller.cluster.access.commands.ReadTransactionRequest;
33 import org.opendaylight.controller.cluster.access.commands.ReadTransactionSuccess;
34 import org.opendaylight.controller.cluster.access.commands.TransactionPurgeRequest;
35 import org.opendaylight.controller.cluster.access.commands.TransactionRequest;
36 import org.opendaylight.controller.cluster.access.concepts.Response;
37 import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeModification;
38 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
39
40 public abstract class LocalProxyTransactionTest<T extends LocalProxyTransaction>
41         extends AbstractProxyTransactionTest<T> {
42
43     @Override
44     @Test
45     public void testExists() throws Exception {
46         assertFutureEquals(true, transaction.exists(PATH_1));
47         assertFutureEquals(false, transaction.exists(PATH_3));
48     }
49
50     @Override
51     @Test
52     public void testRead() throws Exception {
53         assertFutureEquals(com.google.common.base.Optional.of(DATA_1), transaction.read(PATH_1));
54         assertFutureEquals(com.google.common.base.Optional.absent(), transaction.read(PATH_3));
55     }
56
57     @Test
58     public void testAbort() throws Exception {
59         transaction.abort();
60         getTester().expectTransactionRequest(AbortLocalTransactionRequest.class);
61     }
62
63     @SuppressWarnings("unchecked")
64     private void setupExecuteInActor() {
65         doAnswer(inv -> {
66             inv.getArgumentAt(0, InternalCommand.class).execute(mock(ClientActorBehavior.class));
67             return null;
68         }).when(context).executeInActor(any(InternalCommand.class));
69     }
70
71     @Test
72     public void testHandleForwardedRemoteReadRequest() throws Exception {
73         final TestProbe probe = createProbe();
74         final ReadTransactionRequest request =
75                 new ReadTransactionRequest(TRANSACTION_ID, 0L, probe.ref(), PATH_1, true);
76         final Consumer<Response<?, ?>> callback = createCallbackMock();
77         setupExecuteInActor();
78
79         transaction.handleReplayedRemoteRequest(request, callback, Ticker.systemTicker().read());
80         final ArgumentCaptor<Response> captor = ArgumentCaptor.forClass(Response.class);
81         verify(callback).accept(captor.capture());
82         final Response<?, ?> value = captor.getValue();
83         Assert.assertTrue(value instanceof ReadTransactionSuccess);
84         final ReadTransactionSuccess success = (ReadTransactionSuccess) value;
85         Assert.assertTrue(success.getData().isPresent());
86         Assert.assertEquals(DATA_1, success.getData().get());
87     }
88
89     @Test
90     public void testHandleForwardedRemoteExistsRequest() throws Exception {
91         final TestProbe probe = createProbe();
92         final ExistsTransactionRequest request =
93                 new ExistsTransactionRequest(TRANSACTION_ID, 0L, probe.ref(), PATH_1, true);
94         final Consumer<Response<?, ?>> callback = createCallbackMock();
95         setupExecuteInActor();
96
97         transaction.handleReplayedRemoteRequest(request, callback, Ticker.systemTicker().read());
98         final ArgumentCaptor<Response> captor = ArgumentCaptor.forClass(Response.class);
99         verify(callback).accept(captor.capture());
100         final Response<?, ?> value = captor.getValue();
101         Assert.assertTrue(value instanceof ExistsTransactionSuccess);
102         final ExistsTransactionSuccess success = (ExistsTransactionSuccess) value;
103         Assert.assertTrue(success.getExists());
104     }
105
106     @Test
107     public void testHandleForwardedRemotePurgeRequest() throws Exception {
108         final TestProbe probe = createProbe();
109         final TransactionPurgeRequest request =
110                 new TransactionPurgeRequest(TRANSACTION_ID, 0L, probe.ref());
111         testHandleForwardedRemoteRequest(request);
112     }
113
114     @Override
115     @Test
116     public void testForwardToRemoteAbort() throws Exception {
117         final TestProbe probe = createProbe();
118         final AbortLocalTransactionRequest request = new AbortLocalTransactionRequest(TRANSACTION_ID, probe.ref());
119         final ModifyTransactionRequest modifyRequest = testForwardToRemote(request, ModifyTransactionRequest.class);
120         Assert.assertTrue(modifyRequest.getPersistenceProtocol().isPresent());
121         Assert.assertEquals(PersistenceProtocol.ABORT, modifyRequest.getPersistenceProtocol().get());
122     }
123
124     @Override
125     @Test
126     public void testForwardToRemoteCommit() throws Exception {
127         final TestProbe probe = createProbe();
128         final CursorAwareDataTreeModification modification = mock(CursorAwareDataTreeModification.class);
129         final CommitLocalTransactionRequest request =
130                 new CommitLocalTransactionRequest(TRANSACTION_ID, 0L, probe.ref(), modification, null, true);
131         doAnswer(LocalProxyTransactionTest::applyToCursorAnswer).when(modification).applyToCursor(any());
132         final ModifyTransactionRequest modifyRequest = testForwardToRemote(request, ModifyTransactionRequest.class);
133         verify(modification).applyToCursor(any());
134         Assert.assertTrue(modifyRequest.getPersistenceProtocol().isPresent());
135         Assert.assertEquals(PersistenceProtocol.THREE_PHASE, modifyRequest.getPersistenceProtocol().get());
136         checkModifications(modifyRequest);
137     }
138
139     @Test
140     public void testForwardToLocalAbort() throws Exception {
141         final TestProbe probe = createProbe();
142         final AbortLocalTransactionRequest request = new AbortLocalTransactionRequest(TRANSACTION_ID, probe.ref());
143         testForwardToLocal(request, AbortLocalTransactionRequest.class);
144     }
145
146     @Test
147     public void testForwardToLocalPurge() throws Exception {
148         final TestProbe probe = createProbe();
149         final TransactionPurgeRequest request = new TransactionPurgeRequest(TRANSACTION_ID, 0L, probe.ref());
150         testForwardToLocal(request, TransactionPurgeRequest.class);
151     }
152
153     protected <R extends TransactionRequest<R>> R testForwardToLocal(final TransactionRequest<?> toForward,
154                                                                   final Class<R> expectedMessageClass) {
155         final Consumer<Response<?, ?>> callback = createCallbackMock();
156         final TransactionTester<LocalReadWriteProxyTransaction> transactionTester = createLocalProxy();
157         final LocalReadWriteProxyTransaction successor = transactionTester.getTransaction();
158         transaction.forwardToLocal(successor, toForward, callback);
159         return transactionTester.expectTransactionRequest(expectedMessageClass);
160     }
161
162     /**
163      * To emulate side effect of void method.
164      * {@link CursorAwareDataTreeModification#applyToCursor(DataTreeModificationCursor)}
165      *
166      * @param invocation invocation
167      * @return void - always null
168      */
169     protected static final <T> Answer<T> applyToCursorAnswer(final InvocationOnMock invocation) {
170         final DataTreeModificationCursor cursor =
171                 invocation.getArgumentAt(0, DataTreeModificationCursor.class);
172         cursor.write(PATH_1.getLastPathArgument(), DATA_1);
173         cursor.merge(PATH_2.getLastPathArgument(), DATA_2);
174         cursor.delete(PATH_3.getLastPathArgument());
175         return null;
176     }
177
178 }