2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.databroker.actors.dds;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.lenient;
15 import static org.mockito.Mockito.mock;
16 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.CLIENT_ID;
17 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.HISTORY_ID;
18 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.TRANSACTION_ID;
20 import akka.actor.ActorRef;
21 import akka.actor.ActorSelection;
22 import akka.actor.ActorSystem;
23 import akka.testkit.TestProbe;
24 import akka.testkit.javadsl.TestKit;
25 import java.util.List;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.mockito.Mock;
32 import org.mockito.junit.MockitoJUnitRunner;
33 import org.opendaylight.controller.cluster.access.client.AbstractClientConnection;
34 import org.opendaylight.controller.cluster.access.client.AccessClientUtil;
35 import org.opendaylight.controller.cluster.access.client.ClientActorContext;
36 import org.opendaylight.controller.cluster.access.client.InternalCommand;
37 import org.opendaylight.controller.cluster.access.commands.AbortLocalTransactionRequest;
38 import org.opendaylight.controller.cluster.access.commands.ConnectClientRequest;
39 import org.opendaylight.controller.cluster.access.commands.ConnectClientSuccess;
40 import org.opendaylight.controller.cluster.access.concepts.Envelope;
41 import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
42 import org.opendaylight.controller.cluster.access.concepts.Request;
43 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
44 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
45 import org.opendaylight.controller.cluster.access.concepts.RequestSuccess;
46 import org.opendaylight.controller.cluster.access.concepts.Response;
47 import org.opendaylight.controller.cluster.access.concepts.SuccessEnvelope;
48 import org.opendaylight.controller.cluster.datastore.DatastoreContext;
49 import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
50 import org.opendaylight.controller.cluster.datastore.utils.ActorUtils;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
52 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
53 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
54 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
55 import scala.concurrent.Promise;
57 @RunWith(MockitoJUnitRunner.StrictStubs.class)
58 public abstract class AbstractClientHandleTest<T extends AbstractClientHandle<AbstractProxyTransaction>> {
59 private static final String PERSISTENCE_ID = "per-1";
60 private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.of();
63 private DataTree dataTree;
65 private DataTreeSnapshot dataTreeSnapshot;
66 private ActorSystem system;
67 private TestProbe backendProbe;
68 private AbstractClientHistory parent;
69 private AbstractDataStoreClientBehavior client;
73 public void setUp() throws Exception {
74 system = ActorSystem.apply();
75 final TestProbe contextProbe = new TestProbe(system, "context");
76 final TestProbe clientContextProbe = new TestProbe(system, "client-context");
77 backendProbe = new TestProbe(system, "backend");
78 //create handle dependencies
79 final ActorUtils actorUtils = createActorContextMock(system, contextProbe.ref());
80 final ClientActorContext clientContext =
81 AccessClientUtil.createClientActorContext(system, clientContextProbe.ref(), CLIENT_ID, PERSISTENCE_ID);
82 client = new SimpleDataStoreClientBehavior(clientContext, actorUtils, "shard");
83 client.createLocalHistory();
84 parent = new SingleClientHistory(client, HISTORY_ID);
86 client.getConnection(0L);
87 contextProbe.expectMsgClass(ConnectClientRequest.class);
88 final long sequence = 0L;
89 contextProbe.reply(new ConnectClientSuccess(CLIENT_ID, sequence, backendProbe.ref(), List.of(), dataTree, 3));
90 final InternalCommand<ShardBackendInfo> command = clientContextProbe.expectMsgClass(InternalCommand.class);
91 command.execute(client);
93 doReturn(dataTreeSnapshot).when(dataTree).takeSnapshot();
95 handle = createHandle(parent);
98 @SuppressWarnings("checkstyle:hiddenField")
99 protected abstract T createHandle(AbstractClientHistory parent);
102 * Do a operation with handle.
103 * Used for testing, whether closed handle throws exception when the operation is performed.
105 * @param handle handle
107 @SuppressWarnings("checkstyle:hiddenField")
108 protected abstract void doHandleOperation(T handle);
111 public void tearDown() {
112 TestKit.shutdownActorSystem(system);
116 public void testGetIdentifier() {
117 assertEquals(TRANSACTION_ID, handle.getIdentifier());
121 public void testAbort() throws Exception {
122 doHandleOperation(handle);
124 final Envelope<?> envelope = backendProbe.expectMsgClass(Envelope.class);
125 final AbortLocalTransactionRequest request = (AbortLocalTransactionRequest) envelope.getMessage();
126 assertEquals(TRANSACTION_ID, request.getTarget());
131 public void testLocalAbort() throws Exception {
132 doHandleOperation(handle);
133 handle.localAbort(new RuntimeException("fail"));
134 final Envelope<?> envelope = backendProbe.expectMsgClass(Envelope.class);
135 final AbortLocalTransactionRequest request = (AbortLocalTransactionRequest) envelope.getMessage();
136 assertEquals(TRANSACTION_ID, request.getTarget());
141 public void testEnsureClosed() {
142 doHandleOperation(handle);
143 final Map<Long, AbstractProxyTransaction> transactions = handle.ensureClosed();
144 assertNotNull(transactions);
145 assertEquals(1, transactions.size());
149 public void testEnsureProxy() {
150 final var proxy = handle.ensureProxy(PATH);
151 assertEquals(0, proxy.getIdentifier().getTransactionId());
155 public void testParent() {
156 assertEquals(parent, handle.parent());
159 protected void checkClosed() throws Exception {
160 TestUtils.assertOperationThrowsException(() -> doHandleOperation(handle), IllegalStateException.class);
164 * Checks, whether backend actor has received request of expected class wrapped in RequestEnvelope.
165 * Then given response wrapped in ResponseEnvelope is sent.
167 * @param expectedRequestClass expected request class
168 * @param response response
169 * @param <R> expected request type
170 * @return request message
172 protected <R extends Request<?, R>> R backendRespondToRequest(final Class<R> expectedRequestClass,
173 final Response<?, ?> response) {
174 final RequestEnvelope envelope = backendProbe.expectMsgClass(RequestEnvelope.class);
175 assertEquals(expectedRequestClass, envelope.getMessage().getClass());
176 final AbstractClientConnection<ShardBackendInfo> connection = client.getConnection(0L);
177 final long sessionId = envelope.getSessionId();
178 final long txSequence = envelope.getTxSequence();
179 final long executionTime = 0L;
180 if (response instanceof RequestSuccess) {
181 final RequestSuccess<?, ?> success = (RequestSuccess<?, ?>) response;
182 final SuccessEnvelope responseEnvelope = new SuccessEnvelope(success, sessionId, txSequence, executionTime);
183 AccessClientUtil.completeRequest(connection, responseEnvelope);
184 } else if (response instanceof RequestFailure) {
185 final RequestFailure<?, ?> fail = (RequestFailure<?, ?>) response;
186 final FailureEnvelope responseEnvelope = new FailureEnvelope(fail, sessionId, txSequence, executionTime);
187 AccessClientUtil.completeRequest(connection, responseEnvelope);
189 return expectedRequestClass.cast(envelope.getMessage());
192 protected T getHandle() {
196 protected DataTreeSnapshot getDataTreeSnapshot() {
197 return dataTreeSnapshot;
200 private static ActorUtils createActorContextMock(final ActorSystem system, final ActorRef actor) {
201 final ActorUtils mock = mock(ActorUtils.class);
202 final Promise<PrimaryShardInfo> promise = new scala.concurrent.impl.Promise.DefaultPromise<>();
203 final ActorSelection selection = system.actorSelection(actor.path());
204 final PrimaryShardInfo shardInfo = new PrimaryShardInfo(selection, (short) 0);
205 promise.success(shardInfo);
206 doReturn(promise.future()).when(mock).findPrimaryShardAsync(any());
208 final EffectiveModelContext context = mock(EffectiveModelContext.class);
209 lenient().doCallRealMethod().when(context).getQName();
210 lenient().doReturn(context).when(mock).getSchemaContext();
211 lenient().doReturn(DatastoreContext.newBuilder().build()).when(mock).getDatastoreContext();