/* * 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> { } }