Bug 1435: CDS: Added support for custom commit cohort.
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / DataTreeCohortIntegrationTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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
9 package org.opendaylight.controller.cluster.datastore;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertSame;
14 import static org.junit.Assert.fail;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Mockito.mock;
17
18 import akka.actor.ActorSystem;
19 import akka.actor.Address;
20 import akka.actor.AddressFromURIString;
21 import akka.cluster.Cluster;
22 import akka.testkit.JavaTestKit;
23 import akka.util.Timeout;
24 import com.google.common.base.Throwables;
25 import com.google.common.util.concurrent.CheckedFuture;
26 import com.google.common.util.concurrent.Futures;
27 import com.typesafe.config.ConfigFactory;
28 import java.io.IOException;
29 import java.util.concurrent.TimeUnit;
30 import org.junit.AfterClass;
31 import org.junit.BeforeClass;
32 import org.junit.Ignore;
33 import org.junit.Test;
34 import org.mockito.ArgumentCaptor;
35 import org.mockito.Mockito;
36 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
37 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
38 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
39 import org.opendaylight.mdsal.common.api.DataValidationFailedException;
40 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
41 import org.opendaylight.mdsal.common.api.PostCanCommitStep;
42 import org.opendaylight.mdsal.common.api.ThreePhaseCommitStep;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohort;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
46 import org.opendaylight.yangtools.concepts.ObjectRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
49 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
50 import scala.concurrent.duration.Duration;
51
52 public class DataTreeCohortIntegrationTest {
53
54     private static final DataValidationFailedException FAILED_CAN_COMMIT =
55             new DataValidationFailedException(YangInstanceIdentifier.class, TestModel.TEST_PATH, "Test failure.");
56     private static final CheckedFuture<PostCanCommitStep, DataValidationFailedException> FAILED_CAN_COMMIT_FUTURE =
57             Futures.immediateFailedCheckedFuture(FAILED_CAN_COMMIT);
58
59     private static final DOMDataTreeIdentifier TEST_ID =
60             new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
61
62     private static final Timeout TIMEOUT = new Timeout(Duration.create(5, TimeUnit.SECONDS));
63
64     private static ActorSystem system;
65
66     private final DatastoreContext.Builder datastoreContextBuilder =
67             DatastoreContext.newBuilder().shardHeartbeatIntervalInMillis(100);
68
69     @BeforeClass
70     public static void setUpClass() throws IOException {
71         system = ActorSystem.create("cluster-test", ConfigFactory.load().getConfig("Member1"));
72         final Address member1Address = AddressFromURIString.parse("akka.tcp://cluster-test@127.0.0.1:2558");
73         Cluster.get(system).join(member1Address);
74     }
75
76     @AfterClass
77     public static void tearDownClass() throws IOException {
78         JavaTestKit.shutdownActorSystem(system);
79         system = null;
80     }
81
82     protected ActorSystem getSystem() {
83         return system;
84     }
85
86     @Test
87     public void registerNoopCohortTest() throws Exception {
88         final DOMDataTreeCommitCohort cohort = mock(DOMDataTreeCommitCohort.class);
89         Mockito.doReturn(PostCanCommitStep.NOOP_SUCCESS_FUTURE).when(cohort).canCommit(any(Object.class),
90                 any(DOMDataTreeCandidate.class), any(SchemaContext.class));
91         ArgumentCaptor<DOMDataTreeCandidate> candidateCapt = ArgumentCaptor.forClass(DOMDataTreeCandidate.class);
92         new IntegrationTestKit(getSystem(), datastoreContextBuilder) {
93             {
94                 final DistributedDataStore dataStore = setupDistributedDataStore("transactionIntegrationTest", "test-1");
95                 final ObjectRegistration<DOMDataTreeCommitCohort> cohortReg = dataStore.registerCommitCohort(TEST_ID, cohort);
96                 Thread.sleep(1000); // Registration is asynchronous
97                 assertNotNull(cohortReg);
98                 testWriteTransaction(dataStore, TestModel.TEST_PATH,
99                         ImmutableNodes.containerNode(TestModel.TEST_QNAME));
100                 Mockito.verify(cohort).canCommit(any(Object.class), candidateCapt.capture(), any(SchemaContext.class));
101                 DOMDataTreeCandidate candidate = candidateCapt.getValue();
102                 assertNotNull(candidate);
103                 assertEquals(TEST_ID, candidate.getRootPath());
104                 testWriteTransaction(dataStore, TestModel.OUTER_LIST_PATH,
105                         ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
106                 Mockito.verify(cohort, Mockito.times(2)).canCommit(any(Object.class), any(DOMDataTreeCandidate.class),
107                         any(SchemaContext.class));
108                 cohortReg.close();
109                 testWriteTransaction(dataStore, TestModel.TEST_PATH,
110                         ImmutableNodes.containerNode(TestModel.TEST_QNAME));
111                 Mockito.verifyNoMoreInteractions(cohort);
112                 cleanup(dataStore);
113             }
114         };
115     }
116
117     @Test
118     public void failCanCommitTest() throws Exception {
119         final DOMDataTreeCommitCohort failedCohort = mock(DOMDataTreeCommitCohort.class);
120
121         Mockito.doReturn(FAILED_CAN_COMMIT_FUTURE).when(failedCohort).canCommit(any(Object.class),
122                 any(DOMDataTreeCandidate.class), any(SchemaContext.class));
123
124         new IntegrationTestKit(getSystem(), datastoreContextBuilder) {
125             {
126                 final DistributedDataStore dataStore =
127                         setupDistributedDataStore("transactionIntegrationTest", "test-1");
128                 dataStore.registerCommitCohort(TEST_ID, failedCohort);
129                 Thread.sleep(1000); // Registration is asynchronous
130
131                 DOMStoreWriteTransaction writeTx = dataStore.newWriteOnlyTransaction();
132                 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
133                 DOMStoreThreePhaseCommitCohort dsCohort = writeTx.ready();
134                 try {
135                     // FIXME: Weird thing is that invoking canCommit on front-end invokes also
136                     // preCommit on backend.
137                     dsCohort.canCommit().get();
138                     fail("Exception should be raised.");
139                 } catch (Exception e) {
140                     assertSame(FAILED_CAN_COMMIT, Throwables.getRootCause(e));
141                 }
142                 cleanup(dataStore);
143             }
144         };
145     }
146
147     /**
148      *
149      * FIXME: Weird thing is that invoking canCommit on front-end invokes also preCommit on backend
150      * so we can not test abort after can commit.
151      *
152      */
153     @Test
154     @Ignore
155     public void canCommitSuccessExternallyAborted() throws Exception {
156         final DOMDataTreeCommitCohort cohortToAbort = mock(DOMDataTreeCommitCohort.class);
157         final PostCanCommitStep stepToAbort = mock(PostCanCommitStep.class);
158         Mockito.doReturn(Futures.immediateCheckedFuture(stepToAbort)).when(cohortToAbort).canCommit(any(Object.class),
159                 any(DOMDataTreeCandidate.class), any(SchemaContext.class));
160         Mockito.doReturn(ThreePhaseCommitStep.NOOP_ABORT_FUTURE).when(stepToAbort).abort();
161         new IntegrationTestKit(getSystem(), datastoreContextBuilder) {
162             {
163                 final DistributedDataStore dataStore =
164                         setupDistributedDataStore("transactionIntegrationTest", "test-1");
165                 dataStore.registerCommitCohort(TEST_ID, cohortToAbort);
166                 Thread.sleep(1000); // Registration is asynchronous
167
168                 DOMStoreWriteTransaction writeTx = dataStore.newWriteOnlyTransaction();
169                 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
170                 DOMStoreThreePhaseCommitCohort dsCohort = writeTx.ready();
171
172                 dsCohort.canCommit().get();
173                 dsCohort.abort().get();
174                 Mockito.verify(stepToAbort, Mockito.times(1)).abort();
175                 cleanup(dataStore);
176             }
177         };
178     }
179 }