/*
* Copyright 2012 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.repository.support;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
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 static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.aop.framework.Advised;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.repository.GemfireRepository;
import org.springframework.data.gemfire.repository.sample.Person;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
/**
* Unit tests for {@link GemfireRepositoryFactory}.
*
* @author Oliver Gierke
* @author John Blum
* @see org.springframework.data.gemfire.repository.support.GemfireRepositoryFactory
*/
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("unused")
public class GemfireRepositoryFactoryUnitTests {
@Rule
public ExpectedException exception = ExpectedException.none();
private GemfireMappingContext gemfireMappingContext = new GemfireMappingContext();
@Mock
private Region<Object, Object> mockRegion;
@Mock
@SuppressWarnings("rawtypes")
private RegionAttributes mockRegionAttributes;
@SuppressWarnings("unchecked")
protected <K, V> Region<K, V> configureMockRegion(Region<K, V> mockRegion, String name,
Class<K> keyType, Class<V> valueType) {
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegion.getFullPath()).thenReturn(String.format("%1$s%2$s", Region.SEPARATOR, name));
when(mockRegion.getName()).thenReturn(name);
when(mockRegionAttributes.getKeyConstraint()).thenReturn(keyType);
when(mockRegionAttributes.getValueConstraint()).thenReturn(valueType);
return mockRegion;
}
@SuppressWarnings("unchecked")
protected <K, V> Region<K, V> mockRegion(String name, Class<K> keyType, Class<V> valueType) {
return configureMockRegion(mock(Region.class, name), name, keyType, valueType);
}
protected RepositoryMetadata mockRepositoryMetadata(final Class<?> domainType, final Class<?> idType,
final Class<?> repositoryInterface) {
RepositoryMetadata mockRepositoryMetadata = mock(RepositoryMetadata.class);
when(mockRepositoryMetadata.getDomainType()).then(new Answer<Class<?>>() {
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
return domainType;
}
});
when(mockRepositoryMetadata.getIdType()).then(new Answer<Class<?>>() {
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
return idType;
}
});
when(mockRepositoryMetadata.getRepositoryInterface()).then(new Answer<Class<?>>() {
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
return repositoryInterface;
}
});
return mockRepositoryMetadata;
}
@Before
@SuppressWarnings("unchecked")
public void setup() {
configureMockRegion(mockRegion, "simple", Object.class, Object.class);
}
@Test
public void constructGemfireRepositoryFactoryWithNullMappingContextThrowsIllegalArgumentException() {
exception.expect(IllegalArgumentException.class);
exception.expectCause(is(nullValue(Throwable.class)));
exception.expectMessage("MappingContext must not be null");
new GemfireRepositoryFactory(Collections.<Region<?, ?>>emptyList(), null);
}
@Test
public void constructGemfireRepositoryFactoryWithNullRegionsThrowsIllegalArgumentException() {
exception.expect(IllegalArgumentException.class);
exception.expectCause(is(nullValue(Throwable.class)));
exception.expectMessage("Regions must not be null");
new GemfireRepositoryFactory(null, gemfireMappingContext);
}
@Test
public void getRepositoryRegionNameFromRepositoryInterfaceWithRegionAnnotation() {
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>emptyList(), gemfireMappingContext);
assertThat(gemfireRepositoryFactory.getRepositoryRegionName(PersonRepository.class),
is(equalTo("People")));
}
@Test
public void getRepositoryRegionNameFromRepositoryInterfaceWithoutRegionAnnotation() {
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>emptyList(), gemfireMappingContext);
assertThat(gemfireRepositoryFactory.getRepositoryRegionName(SampleCustomGemfireRepository.class),
is(nullValue(String.class)));
}
@Test
@SuppressWarnings("unchecked")
public void getTemplateReturnsGemfireTemplateForPeopleRegion() {
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
PersonRepository.class);
Region<Long, Person> mockPeopleRegion = mockRegion("People", Long.class, Person.class);
Iterable<Region<?, ?>> regions = Arrays.asList(mockRegion, mockPeopleRegion);
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
regions, gemfireMappingContext);
GemfireTemplate gemfireTemplate = gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
assertThat(gemfireTemplate, is(notNullValue(GemfireTemplate.class)));
assertThat(gemfireTemplate.<Long, Person>getRegion(), is(equalTo(mockPeopleRegion)));
verify(mockPeopleRegion, times(1)).getAttributes();
verify(mockPeopleRegion, times(1)).getFullPath();
verify(mockPeopleRegion, times(1)).getName();
verify(mockRegionAttributes, times(1)).getKeyConstraint();
verify(mockRepositoryMetadata, times(1)).getDomainType();
verify(mockRepositoryMetadata, times(1)).getIdType();
}
@Test
public void getTemplateReturnsGemfireTemplateForSimpleRegion() {
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
SampleCustomGemfireRepository.class);
Iterable<Region<?, ?>> regions = Collections.<Region<?, ?>>singleton(mockRegion);
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
regions, gemfireMappingContext);
GemfireTemplate gemfireTemplate = gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
assertThat(gemfireTemplate, is(notNullValue(GemfireTemplate.class)));
assertThat(gemfireTemplate.getRegion(), is(equalTo(mockRegion)));
verify(mockRepositoryMetadata, times(1)).getDomainType();
verify(mockRepositoryMetadata, times(1)).getIdType();
}
@Test
public void getTemplateThrowsIllegalArgumentExceptionForIncompatibleRegionKeyTypeAndRepositoryIdType() {
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
PersonRepository.class);
Region<String, Person> mockPeopleRegion = mockRegion("People", String.class, Person.class);
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>singleton(mockPeopleRegion), gemfireMappingContext);
try {
exception.expect(IllegalArgumentException.class);
exception.expectCause(is(nullValue(Throwable.class)));
exception.expectMessage(String.format(
"The Region referenced only supports keys of type [%1$s], but the entity to be stored has an id of type [%2$s]",
String.class.getName(), Long.class.getName()));
gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
}
finally {
verify(mockRepositoryMetadata, times(1)).getDomainType();
verify(mockRepositoryMetadata, times(1)).getIdType();
verify(mockPeopleRegion, times(1)).getAttributes();
verify(mockPeopleRegion, times(1)).getFullPath();
verify(mockPeopleRegion, times(1)).getName();
verify(mockRegionAttributes, times(1)).getKeyConstraint();
}
}
@Test
public void getTemplateThrowsIllegalStateExceptionForRegionNotFound() {
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
PersonRepository.class);
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>singleton(mockRegion), gemfireMappingContext);
try {
exception.expect(IllegalStateException.class);
exception.expectCause(is(nullValue(Throwable.class)));
exception.expectMessage(String.format(
"No Region [People] was found for domain class [%s]; Make sure you have configured a GemFire Region of that name in your application context",
Person.class.getName()));
gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
}
finally {
verify(mockRepositoryMetadata, times(2)).getDomainType();
verify(mockRepositoryMetadata, never()).getIdType();
verify(mockRegion, times(1)).getFullPath();
verify(mockRegion, times(1)).getName();
verifyZeroInteractions(mockRegionAttributes);
}
}
/**
* @link https://jira.spring.io/browse/SGF-112
*/
@Test
public void rejectsInterfacesExtendingPagingAndSortingRepository() {
exception.expect(IllegalStateException.class);
exception.expectCause(is(nullValue(Throwable.class)));
exception.expectMessage(startsWith("Pagination is not supported by GemFire Repositories"));
GemfireRepositoryFactory repositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>singletonList(mockRegion), new GemfireMappingContext());
repositoryFactory.getRepository(SamplePagingAndSortingRepository.class);
}
@Test
public void usesConfiguredRepositoryBaseClass() {
GemfireRepositoryFactory repositoryFactory = new GemfireRepositoryFactory(
Collections.<Region<?, ?>>singletonList(mockRegion), new GemfireMappingContext());
repositoryFactory.setRepositoryBaseClass(CustomBaseRepository.class);
GemfireRepository<?, ?> gemfireRepository = repositoryFactory.getRepository(SampleCustomGemfireRepository.class,
new SampleCustomRepositoryImpl());
assertThat(((Advised) gemfireRepository).getTargetClass(), is(equalTo((Class) CustomBaseRepository.class)));
}
interface SamplePagingAndSortingRepository extends PagingAndSortingRepository<Person, Long> {
}
static class CustomBaseRepository<T, ID extends Serializable> extends SimpleGemfireRepository<T, ID> {
public CustomBaseRepository(GemfireTemplate template, EntityInformation<T, ID> entityInformation) {
super(template, entityInformation);
}
}
interface SampleCustomRepository<T> {
void doCustomUpdate(T entity);
}
class SampleCustomRepositoryImpl<T> implements SampleCustomRepository<T> {
@Override
public void doCustomUpdate(final T entity) {
throw new UnsupportedOperationException("Not Implemented");
}
}
interface SampleCustomGemfireRepository extends GemfireRepository<Person, Long>, SampleCustomRepository<Person> {
}
@org.springframework.data.gemfire.mapping.annotation.Region("People")
interface PersonRepository extends GemfireRepository<Person, Long> {
}
}