SGF-897 - Write Unit Tests for transaction event handler framework code.

This commit is contained in:
John Blum
2019-11-12 17:02:49 -08:00
parent 1150e34148
commit 584776dd5e
4 changed files with 350 additions and 14 deletions

View File

@@ -22,6 +22,8 @@ import org.apache.geode.cache.TransactionEvent;
import org.apache.geode.cache.TransactionWriter;
import org.apache.geode.cache.TransactionWriterException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -45,8 +47,8 @@ public class ComposableTransactionWriter implements TransactionWriter {
* the composition of both {@link TransactionWriter} one and {@link TransactionWriter} two.
* @see org.apache.geode.cache.TransactionWriter
*/
public static TransactionWriter compose(TransactionWriter transactionWriterOne,
TransactionWriter transactionWriterTwo) {
public static @Nullable TransactionWriter compose(@Nullable TransactionWriter transactionWriterOne,
@Nullable TransactionWriter transactionWriterTwo) {
return transactionWriterOne == null ? transactionWriterTwo
: transactionWriterTwo == null ? transactionWriterOne
@@ -56,7 +58,8 @@ public class ComposableTransactionWriter implements TransactionWriter {
private final TransactionWriter transactionWriterOne;
private final TransactionWriter transactionWriterTwo;
private ComposableTransactionWriter(TransactionWriter transactionWriterOne, TransactionWriter transactionWriterTwo) {
private ComposableTransactionWriter(@NonNull TransactionWriter transactionWriterOne,
@NonNull TransactionWriter transactionWriterTwo) {
Assert.notNull(transactionWriterOne, "TransactionWriter one must not be null");
Assert.notNull(transactionWriterTwo, "TransactionWriter two must not be null");
@@ -65,14 +68,34 @@ public class ComposableTransactionWriter implements TransactionWriter {
this.transactionWriterTwo = transactionWriterTwo;
}
/**
* Returns a reference to the first {@link TransactionWriter} in the composition.
*
* @return a reference to the first {@link TransactionWriter} in the composition.
* @see org.apache.geode.cache.TransactionWriter
*/
protected TransactionWriter getTransactionWriterOne() {
return this.transactionWriterOne;
}
/**
* Returns a reference to the second {@link TransactionWriter} in the composition.
*
* @return a reference to the second {@link TransactionWriter} in the composition.
* @see org.apache.geode.cache.TransactionWriter
*/
protected TransactionWriter getTransactionWriterTwo() {
return this.transactionWriterTwo;
}
/**
* @inheritDoc
*/
@Override
public void beforeCommit(TransactionEvent event) throws TransactionWriterException {
this.transactionWriterOne.beforeCommit(event);
this.transactionWriterTwo.beforeCommit(event);
getTransactionWriterOne().beforeCommit(event);
getTransactionWriterTwo().beforeCommit(event);
}
/**
@@ -81,8 +104,8 @@ public class ComposableTransactionWriter implements TransactionWriter {
@Override
public void close() {
this.transactionWriterOne.close();
this.transactionWriterTwo.close();
getTransactionWriterOne().close();
getTransactionWriterTwo().close();
}
/**
@@ -91,8 +114,8 @@ public class ComposableTransactionWriter implements TransactionWriter {
@Override
public void init(Properties properties) {
this.transactionWriterOne.init(properties);
this.transactionWriterTwo.init(properties);
getTransactionWriterOne().init(properties);
getTransactionWriterTwo().init(properties);
}
/**
@@ -101,7 +124,7 @@ public class ComposableTransactionWriter implements TransactionWriter {
@Override
public void initialize(Cache cache, Properties properties) {
this.transactionWriterOne.initialize(cache, properties);
this.transactionWriterTwo.initialize(cache, properties);
getTransactionWriterOne().initialize(cache, properties);
getTransactionWriterTwo().initialize(cache, properties);
}
}

View File

@@ -21,6 +21,7 @@ import org.apache.geode.cache.TransactionWriter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
/**
@@ -57,6 +58,16 @@ public class TransactionListenerAdapter implements TransactionListener, Transact
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* Returns a reference to the configured {@link ApplicationEventPublisher}.
*
* @return a reference to the configured {@link ApplicationEventPublisher}.
* @see org.springframework.context.ApplicationEventPublisher
*/
protected @NonNull ApplicationEventPublisher getApplicationEventPublisher() {
return this.applicationEventPublisher;
}
/**
* @inheritDoc
*/
@@ -68,7 +79,7 @@ public class TransactionListenerAdapter implements TransactionListener, Transact
// all application @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) annotated transaction
// event handler methods are invoked.
//this.applicationEventPublisher.publishEvent(TransactionApplicationEvent.of(event));
//getApplicationEventPublisher().publishEvent(TransactionApplicationEvent.of(event));
}
/**
@@ -76,7 +87,7 @@ public class TransactionListenerAdapter implements TransactionListener, Transact
*/
@Override
public void afterCommit(TransactionEvent event) {
this.applicationEventPublisher.publishEvent(TransactionApplicationEvent.of(event));
getApplicationEventPublisher().publishEvent(TransactionApplicationEvent.of(event));
}
/**
@@ -90,6 +101,6 @@ public class TransactionListenerAdapter implements TransactionListener, Transact
*/
@Override
public void afterRollback(TransactionEvent event) {
this.applicationEventPublisher.publishEvent(TransactionApplicationEvent.of(event));
getApplicationEventPublisher().publishEvent(TransactionApplicationEvent.of(event));
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.transaction.event;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Properties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.TransactionEvent;
import org.apache.geode.cache.TransactionWriter;
import org.apache.geode.cache.TransactionWriterException;
/**
* Unit Tests for {@link ComposableTransactionWriter}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.TransactionWriter
* @see org.springframework.data.gemfire.transaction.event.ComposableTransactionWriter
* @since 2.3.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ComposableTransactionWriterUnitTests {
@Mock
private TransactionEvent mockTransactionEvent;
@Mock
private TransactionWriter mockTransactionWriterOne;
@Mock
private TransactionWriter mockTransactionWriterTwo;
@Test
public void composeWithNullIsNullSafeAndReturnsNull() {
assertThat(ComposableTransactionWriter.compose(null, null)).isNull();
}
@Test
public void composeWithSingleNonNullTransactionWriterReturnsTransactionWriter() {
assertThat(ComposableTransactionWriter.compose(this.mockTransactionWriterOne, null))
.isEqualTo(this.mockTransactionWriterOne);
assertThat(ComposableTransactionWriter.compose(null, this.mockTransactionWriterTwo))
.isEqualTo(this.mockTransactionWriterTwo);
}
@Test
public void composeWithTwoTransactionWritersReturnsComposite() {
TransactionWriter compositeTransactionWriter =
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo);
assertThat(compositeTransactionWriter).isInstanceOf(ComposableTransactionWriter.class);
assertThat(((ComposableTransactionWriter) compositeTransactionWriter).getTransactionWriterOne())
.isEqualTo(this.mockTransactionWriterOne);
assertThat(((ComposableTransactionWriter) compositeTransactionWriter).getTransactionWriterTwo())
.isEqualTo(this.mockTransactionWriterTwo);
}
@Test
public void beforeCommitInvokesComposedTransactionWriters() throws Exception {
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo)
.beforeCommit(this.mockTransactionEvent);
verify(this.mockTransactionWriterOne, times(1))
.beforeCommit(eq(this.mockTransactionEvent));
verify(this.mockTransactionWriterTwo, times(1))
.beforeCommit(eq(this.mockTransactionEvent));
}
@Test(expected = TransactionWriterException.class)
public void beforeCommitWhenFirstTransactionWriterThrowsException()
throws Exception {
doThrow(new TransactionWriterException("TEST"))
.when(this.mockTransactionWriterOne).beforeCommit(any(TransactionEvent.class));
try {
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo)
.beforeCommit(this.mockTransactionEvent);
}
catch (TransactionWriterException expected) {
assertThat(expected).hasMessage("TEST");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(this.mockTransactionWriterOne, times(1))
.beforeCommit(eq(this.mockTransactionEvent));
verify(this.mockTransactionWriterTwo, never()).beforeCommit(any(TransactionEvent.class));
}
}
@Test
public void closeCallsComposedTransactionWritersCloseMethod() {
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo).close();
verify(this.mockTransactionWriterOne, times(1)).close();
verify(this.mockTransactionWriterTwo, times(1)).close();
}
@Test
public void initCallsComposedTransactionWritersInitMethod() {
Properties testProperties = new Properties();
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo)
.init(testProperties);
verify(this.mockTransactionWriterOne, times(1)).init(eq(testProperties));
verify(this.mockTransactionWriterTwo, times(1)).init(eq(testProperties));
}
@Test
public void initializeCallsComposedTransactionWritersInitializeMethod() {
Cache mockCache = mock(Cache.class);
Properties testProperties = new Properties();
ComposableTransactionWriter.compose(this.mockTransactionWriterOne, this.mockTransactionWriterTwo)
.initialize(mockCache, testProperties);
verify(this.mockTransactionWriterOne, times(1))
.initialize(eq(mockCache), eq(testProperties));
verify(this.mockTransactionWriterTwo, times(1))
.initialize(eq(mockCache), eq(testProperties));
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.transaction.event;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.TransactionEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
/**
* Unit Tests for {@link TransactionListenerAdapter}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.context.ApplicationEventPublisher
* @see org.springframework.data.gemfire.transaction.event.TransactionListenerAdapter
* @since 2.3.0
*/
@RunWith(MockitoJUnitRunner.class)
public class TransactionListenerAdapterUnitTests {
@Mock
private ApplicationEventPublisher mockApplicationEventPublisher;
@Mock
private TransactionEvent mockTransactionEvent;
@Test
public void constructTransactionListenerAdapterIsCorrect() {
TransactionListenerAdapter listener = new TransactionListenerAdapter(this.mockApplicationEventPublisher);
assertThat(listener).isNotNull();
assertThat(listener.getApplicationEventPublisher()).isEqualTo(this.mockApplicationEventPublisher);
}
@Test(expected = IllegalArgumentException.class)
public void constructTransactionListenerAdapterWithNullApplicationEventPublisherThrowsIllegalArgumentException() {
try {
new TransactionListenerAdapter(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("ApplicationEventPublisher must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
private void invokingApplicationEventPublisherTest(Consumer<TransactionListenerAdapter> listenerConsumer) {
doAnswer(invocation -> {
ApplicationEvent applicationEvent = invocation.getArgument(0, ApplicationEvent.class);
assertThat(applicationEvent).isInstanceOf(TransactionApplicationEvent.class);
assertThat(applicationEvent.getSource()).isEqualTo(this.mockTransactionEvent);
return null;
}).when(this.mockApplicationEventPublisher).publishEvent(any(ApplicationEvent.class));
TransactionListenerAdapter listener = new TransactionListenerAdapter(this.mockApplicationEventPublisher);
listenerConsumer.accept(listener);
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(TransactionApplicationEvent.class));
}
private void nonInvokingApplicationEventPublisherTest(Consumer<TransactionListenerAdapter> listenerConsumer) {
TransactionListenerAdapter listener = new TransactionListenerAdapter(this.mockApplicationEventPublisher);
listenerConsumer.accept(listener);
verify(this.mockApplicationEventPublisher, never()).publishEvent(any(ApplicationEvent.class));
}
@Test
public void beforeCommitDoesNotInvokeApplicationEventPublisher() {
nonInvokingApplicationEventPublisherTest(listener -> listener.beforeCommit(this.mockTransactionEvent));
}
@Test
public void afterCommitInvokesApplicationEventPublisher() {
invokingApplicationEventPublisherTest(listener -> listener.afterCommit(this.mockTransactionEvent));
}
@Test
public void afterFailedCommitDoesNotInvokeApplicationEventPublisher() {
nonInvokingApplicationEventPublisherTest(listener -> listener.afterFailedCommit(this.mockTransactionEvent));
}
@Test
public void afterRollbackInvokesApplicationEventPublisher() {
invokingApplicationEventPublisherTest(listener -> listener.afterRollback(this.mockTransactionEvent));
}
}