Create GemFire/Geode AsyncEventListener implementation using a SD CrudRepository for persistent data access operations.
Resolves gh-58.
This commit is contained in:
@@ -0,0 +1,781 @@
|
||||
/*
|
||||
* Copyright 2020 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.geode.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.apache.geode.cache.Operation;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEvent;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.AbstractAsyncEventOperationRepositoryFunction;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.AsyncEventError;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.AsyncEventErrorHandler;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.AsyncEventOperationRepositoryFunction;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.CreateUpdateAsyncEventRepositoryFunction;
|
||||
import org.springframework.geode.cache.RepositoryAsyncEventListener.RemoveAsyncEventRepositoryFunction;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link RepositoryAsyncEventListener}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.apache.geode.cache.Operation
|
||||
* @see org.apache.geode.cache.asyncqueue.AsyncEvent
|
||||
* @see org.apache.geode.cache.asyncqueue.AsyncEventListener
|
||||
* @see org.springframework.data.repository.CrudRepository
|
||||
* @see org.springframework.geode.cache.RepositoryAsyncEventListener
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked"})
|
||||
public class RepositoryAsyncEventListenerUnitTests {
|
||||
|
||||
@Test
|
||||
public void constructRepositoryAsyncEventListener() {
|
||||
|
||||
CrudRepository<?, ?> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<?, ?> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
|
||||
verifyNoInteractions(mockRepository);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructRepositoryAsyncEventListenerWithNullRepositoryThrowsIllegalArgumentException() {
|
||||
|
||||
try {
|
||||
new RepositoryAsyncEventListener<>(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("CrudRepository must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetAsyncEventErrorHandler() {
|
||||
|
||||
AsyncEventErrorHandler mockAsyncEventErrorHandler = mock(AsyncEventErrorHandler.class);
|
||||
|
||||
CrudRepository<?, ?> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<?, ?> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
assertThat(listener.getAsyncEventErrorHandler()).isEqualTo(RepositoryAsyncEventListener.DEFAULT);
|
||||
|
||||
listener.setAsyncEventErrorHandler(mockAsyncEventErrorHandler);
|
||||
|
||||
assertThat(listener.getAsyncEventErrorHandler()).isEqualTo(mockAsyncEventErrorHandler);
|
||||
|
||||
listener.setAsyncEventErrorHandler(null);
|
||||
|
||||
assertThat(listener.getAsyncEventErrorHandler()).isEqualTo(RepositoryAsyncEventListener.DEFAULT);
|
||||
|
||||
verifyNoInteractions(mockAsyncEventErrorHandler, mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepositoryFunctionsHandlesCreateUpdateAndRemove() {
|
||||
|
||||
CrudRepository<Object, Object> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<Object, Object> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
List<AsyncEventOperationRepositoryFunction<Object, Object>> repositoryFunctions =
|
||||
listener.getRepositoryFunctions();
|
||||
|
||||
assertThat(repositoryFunctions).isNotNull();
|
||||
|
||||
List<Class<?>> repositoryFunctionTypes = repositoryFunctions.stream()
|
||||
.map(Object::getClass)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(repositoryFunctionTypes).containsExactly(CreateUpdateAsyncEventRepositoryFunction.class,
|
||||
RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
verifyNoInteractions(mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerAndUnregisterAsyncEventOperationRepositoryFunction() {
|
||||
|
||||
AsyncEventOperationRepositoryFunction<Object, Object> mockFunction =
|
||||
mock(AsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
CrudRepository<Object, Object> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<Object, Object> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
assertThat(listener.getRepositoryFunctions()).hasSize(2);
|
||||
assertThat(listener.getRepositoryFunctions().get(0).getClass())
|
||||
.isEqualTo(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
assertThat(listener.getRepositoryFunctions().get(1).getClass())
|
||||
.isEqualTo(RemoveAsyncEventRepositoryFunction.class);
|
||||
assertThat(listener.register(mockFunction)).isTrue();
|
||||
assertThat(listener.register(null)).isFalse();
|
||||
assertThat(listener.getRepositoryFunctions()).hasSize(3);
|
||||
assertThat(listener.getRepositoryFunctions()).contains(mockFunction);
|
||||
assertThat(listener.getRepositoryFunctions().get(0)).isEqualTo(mockFunction);
|
||||
assertThat(listener.getRepositoryFunctions().get(1).getClass())
|
||||
.isEqualTo(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
assertThat(listener.getRepositoryFunctions().get(2).getClass())
|
||||
.isEqualTo(RemoveAsyncEventRepositoryFunction.class);
|
||||
assertThat(listener.unregister(null)).isFalse();
|
||||
assertThat(listener.unregister(mockFunction)).isTrue();
|
||||
assertThat(listener.getRepositoryFunctions()).hasSize(2);
|
||||
assertThat(listener.getRepositoryFunctions().get(0).getClass())
|
||||
.isEqualTo(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
assertThat(listener.getRepositoryFunctions().get(1).getClass())
|
||||
.isEqualTo(RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
verifyNoInteractions(mockFunction, mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processEventsIsNullSafe() {
|
||||
|
||||
CrudRepository<?, ?> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<?, ?> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
assertThat(listener.processEvents(null)).isTrue();
|
||||
|
||||
verifyNoInteractions(mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processEventsIsSuccessful() {
|
||||
|
||||
AsyncEvent mockEventOne = mock(AsyncEvent.class, "AsyncEventOne");
|
||||
AsyncEvent mockEventTwo = mock(AsyncEvent.class, "AsyncEventTwo");
|
||||
AsyncEvent mockEventThree = mock(AsyncEvent.class, "AsyncEventThree");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionA =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionA");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionB =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionB");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionC =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionC");
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
|
||||
AsyncEvent event = invocation.getArgument(0);
|
||||
|
||||
return mockEventOne.equals(event) || mockEventThree.equals(event);
|
||||
|
||||
}).when(mockRepositoryFunctionA).canProcess(isA(AsyncEvent.class));
|
||||
|
||||
doAnswer(invocation -> {
|
||||
mockRepository.save(invocation.getArgument(0));
|
||||
return true;
|
||||
}).when(mockRepositoryFunctionA).apply(isA(AsyncEvent.class));
|
||||
|
||||
doAnswer(invocation -> {
|
||||
|
||||
AsyncEvent event = invocation.getArgument(0);
|
||||
|
||||
return mockEventTwo.equals(event) || mockEventThree.equals(event);
|
||||
|
||||
}).when(mockRepositoryFunctionB).canProcess(isA(AsyncEvent.class));
|
||||
|
||||
doAnswer(invocation -> {
|
||||
mockRepository.delete(invocation.getArgument(0));
|
||||
return true;
|
||||
}).when(mockRepositoryFunctionB).apply(isA(AsyncEvent.class));
|
||||
|
||||
doReturn(false).when(mockRepositoryFunctionC).canProcess(any());
|
||||
|
||||
List<AsyncEvent> mockEvents = Arrays.asList(mockEventOne, mockEventTwo, mockEventThree);
|
||||
|
||||
RepositoryAsyncEventListener listener = spy(new RepositoryAsyncEventListener<>(mockRepository));
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
|
||||
doReturn(Arrays.asList(mockRepositoryFunctionC, mockRepositoryFunctionA, mockRepositoryFunctionB))
|
||||
.when(listener).getRepositoryFunctions();
|
||||
|
||||
assertThat(listener.processEvents(mockEvents)).isTrue();
|
||||
|
||||
InOrder order =
|
||||
inOrder(mockRepository, mockRepositoryFunctionA, mockRepositoryFunctionB, mockRepositoryFunctionC);
|
||||
|
||||
order.verify(mockRepositoryFunctionC, times(1)).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionA, times(1)).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionB, never()).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionA, times(1)).apply(eq(mockEventOne));
|
||||
order.verify(mockRepository, times(1)).save(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionC, times(1)).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionA, times(1)).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionB, times(1)).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionB, times(1)).apply(eq(mockEventTwo));
|
||||
order.verify(mockRepository, times(1)).delete(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionC, times(1)).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionA, times(1)).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionB, never()).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionA, times(1)).apply(eq(mockEventThree));
|
||||
order.verify(mockRepository, times(1)).save(eq(mockEventThree));
|
||||
|
||||
verify(mockRepositoryFunctionA, never()).apply(eq(mockEventTwo));
|
||||
verify(mockRepositoryFunctionB, never()).apply(eq(mockEventOne));
|
||||
verify(mockRepositoryFunctionB, never()).apply(eq(mockEventThree));
|
||||
verify(mockRepositoryFunctionC, never()).apply(any());
|
||||
verify(mockRepository, never()).delete(eq(mockEventOne));
|
||||
verify(mockRepository, never()).delete(eq(mockEventThree));
|
||||
verify(mockRepository, never()).save(eq(mockEventTwo));
|
||||
|
||||
verifyNoInteractions(mockEventOne, mockEventTwo, mockEventThree);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processEventsIsUnsuccessfulWhenASingleFunctionApplyReturnsFalse() {
|
||||
|
||||
AsyncEvent mockEventOne = mock(AsyncEvent.class, "AsyncEventOne");
|
||||
AsyncEvent mockEventTwo = mock(AsyncEvent.class, "AsyncEventTwo");
|
||||
AsyncEvent mockEventThree = mock(AsyncEvent.class, "AsyncEventThree");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionOne =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionOne");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionTwo =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionTwo");
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunctionThree =
|
||||
mock(AsyncEventOperationRepositoryFunction.class, "RepositoryFunctionThree");
|
||||
|
||||
doReturn(true).when(mockRepositoryFunctionOne).canProcess(eq(mockEventOne));
|
||||
doReturn(true).when(mockRepositoryFunctionOne).apply(eq(mockEventOne));
|
||||
doReturn(true).when(mockRepositoryFunctionTwo).canProcess(eq(mockEventTwo));
|
||||
doReturn(false).when(mockRepositoryFunctionTwo).apply(eq(mockEventTwo));
|
||||
doReturn(true).when(mockRepositoryFunctionThree).canProcess(eq(mockEventThree));
|
||||
doReturn(true).when(mockRepositoryFunctionThree).apply(eq(mockEventThree));
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
List<AsyncEvent> mockEvents = Arrays.asList(mockEventOne, mockEventTwo, mockEventThree);
|
||||
|
||||
RepositoryAsyncEventListener listener = spy(new RepositoryAsyncEventListener<>(mockRepository));
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
|
||||
doReturn(Arrays.asList(mockRepositoryFunctionOne, mockRepositoryFunctionTwo, mockRepositoryFunctionThree))
|
||||
.when(listener).getRepositoryFunctions();
|
||||
|
||||
assertThat(listener.processEvents(mockEvents)).isFalse();
|
||||
|
||||
InOrder order =
|
||||
inOrder(mockRepository, mockRepositoryFunctionOne, mockRepositoryFunctionTwo, mockRepositoryFunctionThree);
|
||||
|
||||
order.verify(mockRepositoryFunctionOne, times(1)).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionTwo, never()).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionThree, never()).canProcess(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionOne, times(1)).apply(eq(mockEventOne));
|
||||
order.verify(mockRepositoryFunctionOne, times(1)).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionTwo, times(1)).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionThree, never()).canProcess(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionTwo, times(1)).apply(eq(mockEventTwo));
|
||||
order.verify(mockRepositoryFunctionOne, times(1)).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionTwo, times(1)).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionThree, times(1)).canProcess(eq(mockEventThree));
|
||||
order.verify(mockRepositoryFunctionThree, times(1)).apply(eq(mockEventThree));
|
||||
|
||||
verify(mockRepositoryFunctionOne, never()).apply(eq(mockEventTwo));
|
||||
verify(mockRepositoryFunctionOne, never()).apply(eq(mockEventThree));
|
||||
verify(mockRepositoryFunctionTwo, never()).apply(eq(mockEventOne));
|
||||
verify(mockRepositoryFunctionTwo, never()).apply(eq(mockEventThree));
|
||||
verify(mockRepositoryFunctionThree, never()).apply(eq(mockEventOne));
|
||||
verify(mockRepositoryFunctionThree, never()).apply(eq(mockEventTwo));
|
||||
|
||||
verifyNoInteractions(mockRepository, mockEventOne, mockEventTwo, mockEventThree);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processEventsIsUnsuccessfulWhenNoFunctionCanProcessEvent() {
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
AsyncEventOperationRepositoryFunction mockRepositoryFunction =
|
||||
mock(AsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
doReturn(false).when(mockRepositoryFunction).canProcess(any());
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener listener = spy(new RepositoryAsyncEventListener<>(mockRepository));
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
|
||||
doReturn(Collections.singletonList(mockRepositoryFunction)).when(listener).getRepositoryFunctions();
|
||||
|
||||
assertThat(listener.processEvents(Collections.singletonList(mockEvent))).isFalse();
|
||||
|
||||
verify(mockRepositoryFunction, times(1)).canProcess(eq(mockEvent));
|
||||
verify(mockRepositoryFunction, never()).apply(any());
|
||||
|
||||
verifyNoInteractions(mockEvent, mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processNoEventsIsSuccessful() {
|
||||
|
||||
CrudRepository<?, ?> mockRepository = mock(CrudRepository.class);
|
||||
|
||||
RepositoryAsyncEventListener<?, ?> listener = new RepositoryAsyncEventListener<>(mockRepository);
|
||||
|
||||
assertThat(listener).isNotNull();
|
||||
assertThat(listener.getRepository()).isEqualTo(mockRepository);
|
||||
assertThat(listener.processEvents(Collections.emptyList())).isTrue();
|
||||
|
||||
verifyNoInteractions(mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructAsyncEventError() {
|
||||
|
||||
AsyncEvent<?, ?> mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
Throwable cause = new RuntimeException("test");
|
||||
|
||||
AsyncEventError eventError = new AsyncEventError(mockEvent, cause);
|
||||
|
||||
assertThat(eventError).isNotNull();
|
||||
assertThat(eventError.getCause()).isEqualTo(cause);
|
||||
assertThat(eventError.getEvent()).isEqualTo(mockEvent);
|
||||
|
||||
assertThat(eventError.toString())
|
||||
.isEqualTo("Error [test] thrown when processing AsyncEvent [%s]", mockEvent);
|
||||
|
||||
verifyNoInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructAsyncEventErrorWithNullCauseThrowsIllegalArgumentException() {
|
||||
|
||||
try {
|
||||
new AsyncEventError(mock(AsyncEvent.class), null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("The cause must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructAsyncEventErrorWithNullEventThrowsIllegalArgumentException() {
|
||||
|
||||
try {
|
||||
new AsyncEventError(null, new RuntimeException("test"));
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("AsyncEvent must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callAbstractAsyncEventOperationRepositoryFunctionApplyWhenFunctionCanProcessEvent() {
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
mock(AbstractAsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
Object entity = "test";
|
||||
|
||||
doReturn(true).when(repositoryFunction).canProcess(eq(mockEvent));
|
||||
doReturn(entity).when(repositoryFunction).resolveEntity(eq(mockEvent));
|
||||
doCallRealMethod().when(repositoryFunction).apply(any());
|
||||
|
||||
assertThat(repositoryFunction.apply(mockEvent)).isTrue();
|
||||
|
||||
InOrder order = inOrder(repositoryFunction);
|
||||
|
||||
order.verify(repositoryFunction, times(1)).apply(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).canProcess(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).resolveEntity(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).doRepositoryOp(eq(entity));
|
||||
|
||||
verifyNoMoreInteractions(repositoryFunction);
|
||||
verifyNoInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callAbstractAsyncEventOperationRepositoryFunctionApplyWhenFunctionCannotProcessEvent() {
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
mock(AbstractAsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doReturn(false).when(repositoryFunction).canProcess(any());
|
||||
doCallRealMethod().when(repositoryFunction).apply(any());
|
||||
|
||||
assertThat(repositoryFunction.apply(mockEvent)).isFalse();
|
||||
|
||||
verify(repositoryFunction, times(1)).apply(eq(mockEvent));
|
||||
verify(repositoryFunction, times(1)).canProcess(eq(mockEvent));
|
||||
|
||||
verifyNoMoreInteractions(repositoryFunction);
|
||||
verifyNoInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callAbstractAsyncEventOperationRepositoryFunctionApplyWhenRepositoryOperationThrowsException() {
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
TestRepositoryAsyncEventListener listener = spy(new TestRepositoryAsyncEventListener(mockRepository));
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
spy(listener.new TestAsyncEventOperationRepositoryFunction());
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
AsyncEventErrorHandler mockEventErrorHandler = mock(AsyncEventErrorHandler.class);
|
||||
|
||||
Object entity = "mock";
|
||||
|
||||
doReturn(mockEventErrorHandler).when(listener).getAsyncEventErrorHandler();
|
||||
doReturn(true).when(repositoryFunction).canProcess(eq(mockEvent));
|
||||
doReturn(entity).when(repositoryFunction).resolveEntity(eq(mockEvent));
|
||||
|
||||
doAnswer(invocation -> {
|
||||
|
||||
AsyncEventError eventError = invocation.getArgument(0);
|
||||
|
||||
assertThat(eventError).isNotNull();
|
||||
assertThat(eventError.getCause()).isInstanceOf(UnsupportedOperationException.class);
|
||||
assertThat(eventError.getCause().getMessage()).isEqualTo("Not Implemented");
|
||||
assertThat(eventError.getEvent()).isEqualTo(mockEvent);
|
||||
|
||||
return false;
|
||||
|
||||
}).when(mockEventErrorHandler).apply(any());
|
||||
|
||||
assertThat(repositoryFunction.apply(mockEvent)).isFalse();
|
||||
|
||||
InOrder order = inOrder(listener, mockEventErrorHandler, repositoryFunction);
|
||||
|
||||
order.verify(repositoryFunction, times(1)).apply(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).canProcess(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).resolveEntity(eq(mockEvent));
|
||||
order.verify(repositoryFunction, times(1)).doRepositoryOp(eq(entity));
|
||||
order.verify(listener, times(1)).getAsyncEventErrorHandler();
|
||||
order.verify(mockEventErrorHandler, times(1)).apply(isA(AsyncEventError.class));
|
||||
|
||||
verifyNoMoreInteractions(repositoryFunction, listener, mockEventErrorHandler);
|
||||
verifyNoInteractions(mockEvent, mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveEntityIsSuccessful() {
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
mock(AbstractAsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).resolveEntity(any());
|
||||
doReturn("mock").when(mockEvent).getDeserializedValue();
|
||||
|
||||
assertThat(repositoryFunction.resolveEntity(mockEvent)).isEqualTo("mock");
|
||||
|
||||
verify(mockEvent, times(1)).getDeserializedValue();
|
||||
verifyNoMoreInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void resolveEntityFromNullEventThrowsIllegalArgumentException() {
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
mock(AbstractAsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).resolveEntity(any());
|
||||
|
||||
try {
|
||||
repositoryFunction.resolveEntity(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("AsyncEvent must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveNulEntityThrowsIllegalStateException() {
|
||||
|
||||
AbstractAsyncEventOperationRepositoryFunction repositoryFunction =
|
||||
mock(AbstractAsyncEventOperationRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).resolveEntity(any());
|
||||
doReturn(null).when(mockEvent).getDeserializedValue();
|
||||
|
||||
try {
|
||||
repositoryFunction.resolveEntity(mockEvent);
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("The entity (deserialized value) was null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockEvent, times(1)).getDeserializedValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionCanProcessCreateEventReturnsTrue() {
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
mock(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(Operation.CREATE).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isTrue();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
verifyNoMoreInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionCanProcessUpdateEventReturnsTrue() {
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
mock(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(Operation.UPDATE).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isTrue();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
verifyNoMoreInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionCanProcessEventWithNullOperationReturnsFalse() {
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
mock(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(null).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isFalse();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
verifyNoMoreInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionCanProcessNullEventReturnsFalse() {
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
mock(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
assertThat(repositoryFunction.canProcess(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionCanProcessRemoveEventReturnsFalse() {
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
mock(CreateUpdateAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(Operation.REMOVE).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isFalse();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
verifyNoMoreInteractions(mockEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUpdateAsyncEventRepositoryFunctionDoRepositoryOpCallsCrudRepositorySave() {
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
TestRepositoryAsyncEventListener listener = new TestRepositoryAsyncEventListener(mockRepository);
|
||||
|
||||
CreateUpdateAsyncEventRepositoryFunction repositoryFunction =
|
||||
spy(listener.new TestCreateUpdateAsyncEventOperationRepositoryFunction());
|
||||
|
||||
doReturn("TEST").when(mockRepository).save(any());
|
||||
|
||||
assertThat(repositoryFunction.doRepositoryOp("MOCK")).isEqualTo("TEST");
|
||||
|
||||
verify(mockRepository, times(1)).save(eq("MOCK"));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAsyncEventRepositoryFunctionCanProcessRemoveEventReturnsTrue() {
|
||||
|
||||
RemoveAsyncEventRepositoryFunction repositoryFunction = mock(RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(Operation.REMOVE).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isTrue();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAsyncEventRepositoryFunctionCanProcessCreateEventReturnsFalse() {
|
||||
|
||||
RemoveAsyncEventRepositoryFunction repositoryFunction = mock(RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(Operation.CREATE).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isFalse();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAsyncEventRepositoryFunctionCanProcessEventWithNullOperationReturnsFalse() {
|
||||
|
||||
RemoveAsyncEventRepositoryFunction repositoryFunction = mock(RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
AsyncEvent mockEvent = mock(AsyncEvent.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
doReturn(null).when(mockEvent).getOperation();
|
||||
|
||||
assertThat(repositoryFunction.canProcess(mockEvent)).isFalse();
|
||||
|
||||
verify(mockEvent, times(1)).getOperation();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAsyncEventRepositoryFunctionCanProcessNullEventReturnsFalse() {
|
||||
|
||||
RemoveAsyncEventRepositoryFunction repositoryFunction = mock(RemoveAsyncEventRepositoryFunction.class);
|
||||
|
||||
doCallRealMethod().when(repositoryFunction).canProcess(any());
|
||||
|
||||
assertThat(repositoryFunction.canProcess(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAsyncEventRepositoryFunctionDoRepositoryOpCallsCrudRepositoryDelete() {
|
||||
|
||||
CrudRepository mockRepository = mock(CrudRepository.class);
|
||||
|
||||
TestRepositoryAsyncEventListener listener = spy(new TestRepositoryAsyncEventListener(mockRepository));
|
||||
|
||||
doReturn(mockRepository).when(listener).getRepository();
|
||||
|
||||
RemoveAsyncEventRepositoryFunction repositoryFunction =
|
||||
spy(listener.new TestRemoveAsyncEventRepositoryFunction());
|
||||
|
||||
assertThat(repositoryFunction.doRepositoryOp("MOCK")).isNull();
|
||||
|
||||
verify(mockRepository, times(1)).delete(eq("MOCK"));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
}
|
||||
|
||||
private static class TestRepositoryAsyncEventListener<T, ID> extends RepositoryAsyncEventListener<T, ID> {
|
||||
|
||||
private TestRepositoryAsyncEventListener(CrudRepository<T, ID> repository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
private class TestAsyncEventOperationRepositoryFunction
|
||||
extends AbstractAsyncEventOperationRepositoryFunction<T, ID> {
|
||||
|
||||
@Override
|
||||
protected <R> R doRepositoryOp(T entity) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private class TestCreateUpdateAsyncEventOperationRepositoryFunction
|
||||
extends CreateUpdateAsyncEventRepositoryFunction<T, ID> {
|
||||
|
||||
}
|
||||
|
||||
private class TestRemoveAsyncEventRepositoryFunction extends RemoveAsyncEventRepositoryFunction<T, ID> { }
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user