/* * Copyright 2010-2013 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.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheTransactionManager; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.query.SelectResults; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.repository.Wrapper; import org.springframework.data.gemfire.repository.sample.Animal; import org.springframework.data.repository.core.EntityInformation; /** * Unit tests for {@link SimpleGemfireRepository}. * * @author John Blum * @see org.junit.Rule * @see org.junit.Test * @see org.mockito.Mockito * @see org.springframework.data.gemfire.GemfireTemplate * @see org.springframework.data.gemfire.repository.Wrapper * @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository * @since 1.4.5 */ @SuppressWarnings("unchecked") public class SimpleGemfireRepositoryUnitTests { @Rule public ExpectedException exception = ExpectedException.none(); protected Map<Long, Animal> asMap(Iterable<Animal> animals) { Map<Long, Animal> animalMap = new HashMap<>(); for (Animal animal : animals) { animalMap.put(animal.getId(), animal); } return animalMap; } protected Animal newAnimal(String name) { Animal animal = new Animal(); animal.setName(name); return animal; } protected Animal newAnimal(Long id, String name) { Animal animal = newAnimal(name); animal.setId(id); return animal; } protected GemfireTemplate newGemfireTemplate(Region<?, ?> region) { return new GemfireTemplate(region); } protected Cache mockCache(String name, boolean transactionExists) { Cache mockCache = mock(Cache.class, String.format("%s.MockCache", name)); CacheTransactionManager mockCacheTransactionManager = mock(CacheTransactionManager.class, String.format("%s.MockCacheTransactionManager", name)); when(mockCache.getCacheTransactionManager()).thenReturn(mockCacheTransactionManager); when(mockCacheTransactionManager.exists()).thenReturn(transactionExists); return mockCache; } protected EntityInformation<Animal, Long> mockEntityInformation() { EntityInformation<Animal, Long> mockEntityInformation = mock(EntityInformation.class); doAnswer(new Answer<Long>() { private final AtomicLong idSequence = new AtomicLong(0L); @Override public Long answer(InvocationOnMock invocation) throws Throwable { Animal argument = invocation.getArgument(0); argument.setId(resolveId(argument.getId())); return argument.getId(); } private Long resolveId(Long id) { return (id != null ? id : idSequence.incrementAndGet()); } }).when(mockEntityInformation).getRequiredId(any(Animal.class)); return mockEntityInformation; } protected Region mockRegion() { return mockRegion("MockRegion"); } protected Region mockRegion(String name) { Region mockRegion = mock(Region.class, String.format("%s.MockRegion", name)); when(mockRegion.getName()).thenReturn(name); when(mockRegion.getFullPath()).thenReturn(String.format("%1$s%2$s", Region.SEPARATOR, name)); return mockRegion; } protected Region mockRegion(String name, Cache mockCache, DataPolicy dataPolicy) { Region mockRegion = mockRegion(name); when(mockRegion.getRegionService()).thenReturn(mockCache); RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, String.format("%s.MockRegionAttributes", name)); when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); when(mockRegionAttributes.getDataPolicy()).thenReturn(dataPolicy); return mockRegion; } @Test public void constructSimpleGemfireRepositoryWithNullTemplateThrowsIllegalArgumentException() { exception.expect(IllegalArgumentException.class); exception.expectCause(is(nullValue(Throwable.class))); exception.expectMessage("Template must not be null"); new SimpleGemfireRepository<>(null, mockEntityInformation()); } @Test public void constructSimpleGemfireRepositoryWithNullEntityInformationThrowsIllegalArgumentException() { exception.expect(IllegalArgumentException.class); exception.expectCause(is(nullValue(Throwable.class))); exception.expectMessage("EntityInformation must not be null"); new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion()), null); } @Test public void saveEntityIsCorrect() { Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); Animal dog = repository.save(newAnimal("dog")); assertThat(dog).isNotNull(); assertThat(dog.getId().longValue()).isEqualTo(1L); assertThat(dog.getName()).isEqualTo("dog"); verify(mockRegion, times(1)).put(eq(1L), eq(dog)); } @Test public void saveEntitiesIsCorrect() { List<Animal> animals = new ArrayList<>(3); animals.add(newAnimal("bird")); animals.add(newAnimal("cat")); animals.add(newAnimal("dog")); Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); Iterable<Animal> savedAnimals = repository.saveAll(animals); assertThat(savedAnimals).isNotNull(); verify(mockRegion, times(1)).putAll(eq(asMap(savedAnimals))); } @Test public void saveWrapperIsCorrect() { Animal dog = newAnimal(1L, "dog"); Wrapper dogWrapper = new Wrapper(dog, dog.getId()); Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); assertThat(repository.save(dogWrapper)).isEqualTo(dog); verify(mockRegion, times(1)).put(eq(dog.getId()), eq(dog)); } @Test public void countReturnsNumberOfRegionEntries() { Region mockRegion = mockRegion("Example"); GemfireTemplate template = spy(newGemfireTemplate(mockRegion)); SelectResults mockSelectResults = mock(SelectResults.class); doReturn(mockSelectResults).when(template).find(eq("SELECT count(*) FROM /Example")); when(mockSelectResults.iterator()).thenReturn(Collections.singletonList(21).iterator()); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>( template, mockEntityInformation()); assertThat(repository.count()).isEqualTo(21); verify(mockRegion, times(1)).getFullPath(); verify(template, times(1)).find(eq("SELECT count(*) FROM /Example")); verify(mockSelectResults, times(1)).iterator(); } @Test public void existsIsCorrect() { Animal dog = newAnimal(1L, "dog"); Region<Long, Animal> mockRegion = mockRegion(); when(mockRegion.get(any(Long.class))).then( invocation -> (dog.getId().equals(invocation.getArguments()[0]) ? dog : null)); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); assertThat(repository.existsById(1L)).isTrue(); assertThat(repository.existsById(10L)).isFalse(); } @Test public void findOneIsCorrect() { Animal dog = newAnimal(1L, "dog"); Region<Long, Animal> mockRegion = mockRegion(); when(mockRegion.get(any(Long.class))).then( invocation -> (dog.getId().equals(invocation.getArguments()[0]) ? dog : null)); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); assertThat(repository.findById(1L).orElse(null)).isEqualTo(dog); assertThat(repository.findById(10L).isPresent()).isFalse(); } @Test public void findAllIsCorrect() { Map<Long, Animal> animals = Stream.of(newAnimal(1L, "bird"), newAnimal(2L, "cat"), newAnimal(3L, "dog")) .collect(Collectors.toMap(Animal::getId, Function.identity())); Region<Long, Animal> mockRegion = mockRegion(); when(mockRegion.getAll(any(Collection.class))).then(invocation -> { Collection<Long> keys = invocation.getArgument(0); return animals.values().stream().filter((animal -> keys.contains(animal.getId()))) .collect(Collectors.toMap(Animal::getId, Function.identity())); }); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>( newGemfireTemplate(mockRegion), mockEntityInformation()); Collection<Animal> animalsFound = repository.findAllById(Arrays.asList(1L, 3L)); assertThat(animalsFound).isNotNull(); assertThat(animalsFound).hasSize(2); assertThat(animalsFound).contains(animals.get(1L), animals.get(3L)); verify(mockRegion, times(1)).getAll(eq(Arrays.asList(1L, 3L))); } @Test public void findAllWithIdsReturnsNoMatches() { Region<Long, Animal> mockRegion = mockRegion(); when(mockRegion.getAll(any(Collection.class))).then(invocation -> { Collection<Long> keys = invocation.getArgument(0); Map<Long, Animal> result = new HashMap<>(keys.size()); for (Long key : keys) { result.put(key, null); } return result; }); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); Collection<Animal> animalsFound = repository.findAllById(Arrays.asList(1L, 2L, 3L)); assertThat(animalsFound).isNotNull(); assertThat(animalsFound).isEmpty(); verify(mockRegion, times(1)).getAll(eq(Arrays.asList(1L, 2L, 3L))); } @Test public void findAllWithIdsReturnsPartialMatches() { Map<Long, Animal> animals = Stream.of(newAnimal(1L, "bird"), newAnimal(2L, "cat"), newAnimal(3L, "dog")) .collect(Collectors.toMap(Animal::getId, Function.identity())); Region<Long, Animal> mockRegion = mockRegion(); when(mockRegion.getAll(any(Collection.class))).then(invocation -> { Collection<Long> keys = invocation.getArgument(0); Map<Long, Animal> result = new HashMap<>(keys.size()); for (Long key : keys) { result.put(key, animals.get(key)); } return result; }); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); Collection<Animal> animalsFound = repository.findAllById(Arrays.asList(0L, 1L, 2L, 4L)); assertThat(animalsFound).isNotNull(); assertThat(animalsFound).hasSize(2); assertThat(animalsFound).contains(animals.get(1L), animals.get(2L)); verify(mockRegion, times(1)).getAll(eq(Arrays.asList(0L, 1L, 2L, 4L))); } @Test public void deleteByIdIsCorrect() { Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); repository.deleteById(1L); verify(mockRegion, times(1)).remove(eq(1L)); } @Test public void deleteEntityIsCorrect() { Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); repository.delete(newAnimal(1L, "dog")); verify(mockRegion, times(1)).remove(eq(1L)); } @Test public void deleteEntitiesIsCorrect() { Region<Long, Animal> mockRegion = mockRegion(); SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); repository.deleteAll(Arrays.asList(newAnimal(1L, "bird"), newAnimal(2L, "cat"), newAnimal(3L, "dog"))); verify(mockRegion, times(1)).remove(eq(1L)); verify(mockRegion, times(1)).remove(eq(2L)); verify(mockRegion, times(1)).remove(eq(3L)); } @Test public void deleteAllWithClear() { Cache mockCache = mockCache("MockCache", false); Region<Long, Animal> mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.REPLICATE); SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); gemfireRepository.deleteAll(); verify(mockCache, times(1)).getCacheTransactionManager(); verify(mockRegion, times(2)).getAttributes(); verify(mockRegion, times(2)).getRegionService(); verify(mockRegion, times(1)).clear(); } @Test public void deleteAllWithKeysWhenClearThrowsException() { Cache mockCache = mockCache("MockCache", false); Region<Long, Animal> mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.PERSISTENT_REPLICATE); Set<Long> keys = new HashSet<>(Arrays.asList(1L, 2L, 3L)); doThrow(new UnsupportedOperationException("Not Implemented!")).when(mockRegion).clear(); when(mockRegion.keySet()).thenReturn(keys); SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); gemfireRepository.deleteAll(); verify(mockCache, times(1)).getCacheTransactionManager(); verify(mockRegion, times(2)).getAttributes(); verify(mockRegion, times(2)).getRegionService(); verify(mockRegion, times(1)).clear(); verify(mockRegion, times(1)).removeAll(eq(keys)); } @Test public void deleteAllWithKeysWhenPartitionRegion() { Cache mockCache = mockCache("MockCache", false); Region<Long, Animal> mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.PERSISTENT_PARTITION); Set<Long> keys = new HashSet<>(Arrays.asList(1L, 2L, 3L)); when(mockRegion.keySet()).thenReturn(keys); SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); gemfireRepository.deleteAll(); verify(mockCache, times(0)).getCacheTransactionManager(); verify(mockRegion, times(2)).getAttributes(); verify(mockRegion, times(0)).getRegionService(); verify(mockRegion, times(0)).clear(); verify(mockRegion, times(1)).removeAll(eq(keys)); } @Test public void deleteAllWithKeysWhenTransactionPresent() { Cache mockCache = mockCache("MockCache", true); Region<Long, Animal> mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.REPLICATE); Set<Long> keys = new HashSet<>(Arrays.asList(1L, 2L, 3L)); when(mockRegion.keySet()).thenReturn(keys); SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<>(newGemfireTemplate(mockRegion), mockEntityInformation()); gemfireRepository.deleteAll(); verify(mockCache, times(1)).getCacheTransactionManager(); verify(mockRegion, times(2)).getAttributes(); verify(mockRegion, times(2)).getRegionService(); verify(mockRegion, times(0)).clear(); verify(mockRegion, times(1)).removeAll(eq(keys)); } }