/*
* Copyright 2016 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
*
* http://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.search.lucene.support;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor.newPdxInstanceMethodInterceptor;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.WritablePdxInstance;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Unit tests for {@link PdxInstanceMethodInterceptor}.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor
* @since 1.1.0
*/
@RunWith(MockitoJUnitRunner.class)
public class PdxInstanceMethodInterceptorUnitTests {
@Mock
private MethodInvocation mockMethodInvocation;
@Mock
private PdxInstance mockSource;
@Mock
private WritablePdxInstance mockNewSource;
@SafeVarargs
protected static <T> T[] asArray(T... array) {
return array;
}
@Test
public void newPdxInstanceMethodInterceptorWithValidObjectSourceIsSuccessful() {
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor((Object) mockSource);
assertThat(methodInterceptor).isNotNull();
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
}
@Test(expected = IllegalArgumentException.class)
public void newPdxInstanceMethodInterceptorWithInvalidObjectSourceThrowsIllegalArgumentException() {
try {
newPdxInstanceMethodInterceptor(new Object());
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageStartingWith(String.format(
"Source [java.lang.Object] is not an instance of [%s]", PdxInstance.class.getName()));
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void newPdxInstanceMethodInterceptorWithPdxInstanceIsSuccessful() {
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor(mockSource);
assertThat(methodInterceptor).isNotNull();
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
}
@Test(expected = IllegalArgumentException.class)
public void constructPdxInstanceMethodInterceptorWithNullThrowsIllegalArgumentException() {
try {
new PdxInstanceMethodInterceptor(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Source must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void invokeObjectMethodIsHandled() throws Throwable {
Person jonDoe = Person.newPerson("Jon", "Doe");
Method toString = jonDoe.getClass().getMethod("toString");
when(mockMethodInvocation.getMethod()).thenReturn(toString);
when(mockMethodInvocation.proceed()).thenAnswer(invocationOnMock -> toString.invoke(jonDoe));
assertThat(newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation)).isEqualTo("Jon Doe");
verify(mockMethodInvocation, times(1)).getMethod();
verify(mockMethodInvocation, times(1)).proceed();
verifyZeroInteractions(mockSource);
}
@Test
public void invokeGetterOnSourceIsHandled() throws Throwable {
Person jonDoe = Person.newPerson("Jon", "Doe");
Method getFirstName = jonDoe.getClass().getMethod("getFirstName");
when(mockMethodInvocation.getMethod()).thenReturn(getFirstName);
when(mockSource.hasField(eq("firstName"))).thenReturn(true);
when(mockSource.getField(eq("firstName"))).thenReturn(jonDoe.getFirstName());
assertThat(newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation)).isEqualTo("Jon");
verify(mockMethodInvocation, never()).proceed();
verify(mockSource, times(1)).hasField(eq("firstName"));
verify(mockSource, times(1)).getField(eq("firstName"));
verifyNoMoreInteractions(mockSource);
}
@Test
public void invokeSetterOnSourceIsHandled() throws Throwable {
Person jonDoe = Person.newPerson("Jon", "Doe");
Method setLastName = jonDoe.getClass().getMethod("setLastName", String.class);
when(mockMethodInvocation.getMethod()).thenReturn(setLastName);
when(mockMethodInvocation.getArguments()).thenReturn(asArray("Smith"));
when(mockSource.hasField(eq("lastName"))).thenReturn(true);
when(mockSource.createWriter()).thenReturn(mockNewSource);
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor(mockSource);
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
assertThat(methodInterceptor.invoke(mockMethodInvocation)).isNull();
assertThat(methodInterceptor.getSource()).isEqualTo(mockNewSource);
verify(mockMethodInvocation, times(1)).getMethod();
verify(mockMethodInvocation, times(2)).getArguments();
verify(mockSource, times(1)).hasField(eq("lastName"));
verify(mockNewSource, times(1)).setField(eq("lastName"), eq("Smith"));
}
@Test(expected = IllegalStateException.class)
public void invokeThrowsIllegalStateExceptionWhenPdxInstanceDoesNotHaveField() throws Throwable {
Method getGender = Person.class.getMethod("getGender");
when(mockMethodInvocation.getMethod()).thenReturn(getGender);
when(mockSource.hasField(anyString())).thenReturn(false);
try {
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("Source [%s] does not contain field with name [gender]", mockSource);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockMethodInvocation, times(1)).getMethod();
verify(mockSource, times(1)).hasField(eq("gender"));
verifyNoMoreInteractions(mockSource);
}
}
@Test(expected = IllegalArgumentException.class)
public void invokeThrowsIllegalArgumentExceptionWhenMethodInvocationHasIncorrectNumberOfArguments()
throws Throwable {
Method setGender = Person.class.getMethod("setGender", Gender.class);
when(mockMethodInvocation.getMethod()).thenReturn(setGender);
when(mockSource.hasField(eq("gender"))).thenReturn(true);
when(mockMethodInvocation.getArguments()).thenReturn(asArray(Gender.FEMALE, Gender.MALE));
try {
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage(
"Invoked setter method [setGender] must expect exactly 1 argument; Arguments were [[FEMALE, MALE]]");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockMethodInvocation, times(1)).getMethod();
verify(mockMethodInvocation, times(2)).getArguments();
verify(mockSource, times(1)).hasField(eq("gender"));
}
}
@Test(expected = IllegalStateException.class)
public void invokeThrowsIllegalArgumentExceptionWhenPdxInstanceHasNoWriter() throws Throwable {
Method setGender = Person.class.getMethod("setGender", Gender.class);
when(mockMethodInvocation.getMethod()).thenReturn(setGender);
when(mockMethodInvocation.getArguments()).thenReturn(asArray(Gender.FEMALE));
when(mockSource.hasField(eq("gender"))).thenReturn(true);
when(mockSource.createWriter()).thenReturn(null);
try {
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage(
"No writer for PdxInstance [%s] was found for setting field [gender] to value [FEMALE]", mockSource);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockMethodInvocation, times(1)).getMethod();
verify(mockMethodInvocation, times(2)).getArguments();
verify(mockSource, times(1)).hasField(eq("gender"));
verify(mockSource, times(1)).createWriter();
}
}
enum Gender {
FEMALE,
MALE
}
@Data
@RequiredArgsConstructor(staticName = "newPerson")
@SuppressWarnings("unused")
static class Person {
Gender gender;
@NonNull String firstName;
@NonNull String lastName;
/**
* @inheritDoc
*/
@Override
public String toString() {
return String.format("%1$s %2$s", getFirstName(), getLastName());
}
}
}