From b359b55975854aaeed46b8ecb133c44fda5b6e67 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 7 Feb 2017 22:23:12 -0800 Subject: [PATCH] SGF-594 - Difficult to determine whether GemFireRepository.findAll(keys) records not found. (cherry picked from commit 15aae69de569d83d15897418975be7fa54c1ce01) Signed-off-by: John Blum (cherry picked from commit 6b5ffcbe383b4e25b6f1e67a78bd13a9e5cd4085) Signed-off-by: John Blum --- .../support/SimpleGemfireRepository.java | 95 ++-- ...mpleGemfireRepositoryIntegrationTests.java | 131 +++-- .../SimpleGemfireRepositoryUnitTest.java | 393 ------------- .../SimpleGemfireRepositoryUnitTests.java | 516 ++++++++++++++++++ 4 files changed, 626 insertions(+), 509 deletions(-) delete mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTests.java diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java index 3764baff..e9fb0454 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.gemfire.repository.support; +import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -22,23 +25,26 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.data.domain.Sort; -import org.springframework.data.gemfire.GemfireCallback; -import org.springframework.data.gemfire.GemfireTemplate; -import org.springframework.data.gemfire.repository.GemfireRepository; -import org.springframework.data.gemfire.repository.Wrapper; -import org.springframework.data.gemfire.repository.query.QueryString; -import org.springframework.data.repository.core.EntityInformation; -import org.springframework.util.Assert; - +import com.gemstone.gemfire.GemFireCheckedException; +import com.gemstone.gemfire.GemFireException; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheTransactionManager; import com.gemstone.gemfire.cache.DataPolicy; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.cache.query.SelectResults; +import org.springframework.data.domain.Sort; +import org.springframework.data.gemfire.GemfireCallback; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.repository.GemfireRepository; +import org.springframework.data.gemfire.repository.Wrapper; +import org.springframework.data.gemfire.repository.query.QueryString; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.util.Assert; + /** - * Basic Repository implementation for GemFire. + * Basic {@link Repository} implementation for GemFire. * * @author Oliver Gierke * @author David Turanski @@ -51,9 +57,10 @@ import com.gemstone.gemfire.cache.query.SelectResults; */ public class SimpleGemfireRepository implements GemfireRepository { - private final GemfireTemplate template; private final EntityInformation entityInformation; + private final GemfireTemplate template; + /** * Creates a new {@link SimpleGemfireRepository}. * @@ -61,9 +68,8 @@ public class SimpleGemfireRepository implements Gemf * @param entityInformation must not be {@literal null}. */ public SimpleGemfireRepository(GemfireTemplate template, EntityInformation entityInformation) { - - Assert.notNull(template); - Assert.notNull(entityInformation); + Assert.notNull(template, "Template must not be null"); + Assert.notNull(entityInformation, "EntityInformation must not be null"); this.template = template; this.entityInformation = entityInformation; @@ -71,7 +77,6 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#save(S) */ @Override @@ -82,7 +87,6 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ @Override @@ -100,19 +104,17 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * - * @see - * org.springframework.data.gemfire.repository.GemfireRepository#save( - * org.springframework.data.gemfire.repository.Wrapper) + * @see org.springframework.data.gemfire.repository.GemfireRepository#save(org.springframework.data.gemfire.repository.Wrapper) */ @Override public T save(Wrapper wrapper) { - return template.put(wrapper.getKey(), wrapper.getEntity()); + T entity = wrapper.getEntity(); + template.put(wrapper.getKey(), entity); + return entity; } /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#count() */ @Override @@ -123,7 +125,6 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) */ @Override @@ -133,7 +134,6 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) */ @Override @@ -144,7 +144,6 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#findAll() */ @Override @@ -170,24 +169,31 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) */ @Override @SuppressWarnings("unchecked") public Collection findAll(Iterable ids) { - List parameters = new ArrayList(); + List keys = new ArrayList(); for (ID id : ids) { - parameters.add(id); + keys.add(id); } - return (Collection) template.getAll(parameters).values(); + Collection values = (Collection) nullSafeMap(template.getAll(keys)).values(); + List results = new ArrayList(values.size()); + + for (T value : values) { + if (value != null) { + results.add(value); + } + } + + return results; } /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) */ @Override @@ -197,9 +203,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * - * @see - * org.springframework.data.repository.CrudRepository#delete(java.lang.Object) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) */ @Override public void delete(T entity) { @@ -208,9 +212,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * - * @see - * org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) */ @Override public void delete(Iterable entities) { @@ -221,61 +223,55 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * * @see com.gemstone.gemfire.cache.Region#getAttributes() * @see com.gemstone.gemfire.cache.RegionAttributes#getDataPolicy() */ - boolean isPartitioned(final Region region) { + boolean isPartitioned(Region region) { return (region != null && region.getAttributes() != null && isPartitioned(region.getAttributes().getDataPolicy())); } /* * (non-Javadoc) - * * @see com.gemstone.gemfire.cache.DataPolicy#withPartitioning() */ - boolean isPartitioned(final DataPolicy dataPolicy) { + boolean isPartitioned(DataPolicy dataPolicy) { return (dataPolicy != null && dataPolicy.withPartitioning()); } /* * (non-Javadoc) - * * @see com.gemstone.gemfire.cache.Region#getRegionService() * @see com.gemstone.gemfire.cache.Cache#getCacheTransactionManager() */ - boolean isTransactionPresent(final Region region) { + boolean isTransactionPresent(Region region) { return (region.getRegionService() instanceof Cache && isTransactionPresent(((Cache) region.getRegionService()).getCacheTransactionManager())); } /* * (non-Javadoc) - * * @see com.gemstone.gemfire.cache.CacheTransactionManager#exists() */ - boolean isTransactionPresent(final CacheTransactionManager cacheTransactionManager) { + boolean isTransactionPresent(CacheTransactionManager cacheTransactionManager) { return (cacheTransactionManager != null && cacheTransactionManager.exists()); } /* (non-Javadoc) */ @SuppressWarnings("unchecked") - void doRegionClear(final Region region) { + void doRegionClear(Region region) { region.removeAll(region.keySet()); } /* * (non-Javadoc) - * * @see org.springframework.data.repository.CrudRepository#deleteAll() */ @Override public void deleteAll() { - template.execute(new GemfireCallback() { + template.execute(new GemfireCallback() { @Override - @SuppressWarnings("rawtypes") - public Void doInGemfire(final Region region) { + public Object doInGemfire(Region region) throws GemFireCheckedException, GemFireException { if (isPartitioned(region) || isTransactionPresent(region)) { doRegionClear(region); } @@ -292,5 +288,4 @@ public class SimpleGemfireRepository implements Gemf } }); } - } diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java index 8509c7a6..e2c83c00 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java @@ -13,6 +13,7 @@ * 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; @@ -20,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Properties; import javax.annotation.Resource; @@ -35,15 +35,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.gemfire.CacheFactoryBean; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.LocalRegionFactoryBean; +import org.springframework.data.gemfire.config.annotation.PeerCacheApplication; import org.springframework.data.gemfire.repository.sample.Person; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.support.ReflectionEntityInformation; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; /** * Integration tests for {@link SimpleGemfireRepository}. @@ -52,8 +51,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author John Blum * @see org.junit.Test * @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner */ -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(SpringRunner.class) @ContextConfiguration @SuppressWarnings("unused") public class SimpleGemfireRepositoryIntegrationTests { @@ -80,22 +81,6 @@ public class SimpleGemfireRepositoryIntegrationTests { repository = new SimpleGemfireRepository(template, information); } - @Test - public void saveAndDeleteEntity() { - Person oliverGierke = new Person(1L, "Oliver", "Gierke"); - - assertThat(repository.save(oliverGierke)).isEqualTo(oliverGierke); - assertThat(repository.count()).isEqualTo(1L); - assertThat(repository.findOne(oliverGierke.getId())).isEqualTo(oliverGierke); - assertThat(repository.findAll()).isEqualTo(Collections.singletonList(oliverGierke)); - - repository.delete(oliverGierke); - - assertThat(repository.count()).isEqualTo(0L); - assertThat(repository.findOne(oliverGierke.getId())).isNull(); - assertThat(repository.findAll()).isEmpty(); - } - @Test public void deleteAllFiresClearEvent() { assertThat(regionClearListener.eventFired).isFalse(); @@ -104,20 +89,7 @@ public class SimpleGemfireRepositoryIntegrationTests { } @Test - public void queryRegion() throws Exception { - Person oliverGierke = new Person(1L, "Oliver", "Gierke"); - - assertThat(template.put(oliverGierke.getId(), oliverGierke)).isNull(); - - SelectResults people = template.find("SELECT * FROM /People p WHERE p.firstname = $1", - oliverGierke.getFirstname()); - - assertThat(people.size()).isEqualTo(1); - assertThat(people.iterator().next()).isEqualTo(oliverGierke); - } - - @Test - public void findAllWithGivenIds() { + public void findAllWithIds() { Person dave = new Person(1L, "Dave", "Matthews"); Person carter = new Person(2L, "Carter", "Beauford"); Person leroi = new Person(3L, "Leroi", "Moore"); @@ -133,13 +105,67 @@ public class SimpleGemfireRepositoryIntegrationTests { assertThat(result).containsAll(Arrays.asList(carter, leroi)); } + @Test + public void findAllWithIdsReturnsNoMatches() { + Collection results = repository.findAll(Arrays.asList(1L, 2L)); + + assertThat(results).isNotNull(); + assertThat(results).isEmpty(); + } + + @Test + public void findAllWithIdsReturnsPartialMatches() { + Person kurt = new Person(1L, "Kurt", "Cobain"); + Person eddie = new Person(2L, "Eddie", "Veddar"); + Person michael = new Person(3L, "Michael", "Jackson"); + + template.put(kurt.getId(), kurt); + template.put(eddie.getId(), eddie); + + Collection results = repository.findAll(Arrays.asList(0L, 1L, 2L, 4L)); + + assertThat(results).isNotNull(); + assertThat(results).hasSize(2); + assertThat(results).contains(kurt, eddie); + assertThat(results).doesNotContain(michael); + } + + @Test + public void queryRegion() throws Exception { + Person oliverGierke = new Person(1L, "Oliver", "Gierke"); + + assertThat(template.put(oliverGierke.getId(), oliverGierke)).isNull(); + + SelectResults people = template.find("SELECT * FROM /People p WHERE p.firstname = $1", + oliverGierke.getFirstname()); + + assertThat(people.size()).isEqualTo(1); + assertThat(people.iterator().next()).isEqualTo(oliverGierke); + } + + @Test + public void saveAndDeleteEntity() { + Person oliverGierke = new Person(1L, "Oliver", "Gierke"); + + assertThat(repository.save(oliverGierke)).isEqualTo(oliverGierke); + assertThat(repository.count()).isEqualTo(1L); + assertThat(repository.findOne(oliverGierke.getId())).isEqualTo(oliverGierke); + assertThat(repository.findAll()).isEqualTo(Collections.singletonList(oliverGierke)); + + repository.delete(oliverGierke); + + assertThat(repository.count()).isEqualTo(0L); + assertThat(repository.findOne(oliverGierke.getId())).isNull(); + assertThat(repository.findAll()).isEmpty(); + } + @Test public void saveEntities() { assertThat(template.getRegion()).isEmpty(); - Person johnBlum = new Person(1l, "John", "Blum"); - Person jonBloom = new Person(2l, "Jon", "Bloom"); - Person juanBlume = new Person(3l, "Juan", "Blume"); + Person johnBlum = new Person(1L, "John", "Blum"); + Person jonBloom = new Person(2L, "Jon", "Bloom"); + Person juanBlume = new Person(3L, "Juan", "Blume"); repository.save(Arrays.asList(johnBlum, jonBloom, juanBlume)); @@ -160,36 +186,9 @@ public class SimpleGemfireRepositoryIntegrationTests { } } - @Configuration + @PeerCacheApplication(name = "SimpleGemfireRepositoryIntegrationTests", logLevel = DEFAULT_GEMFIRE_LOG_LEVEL) static class SimpleGemfireRepositoryConfiguration { - Properties gemfireProperties() { - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", applicationName()); - gemfireProperties.setProperty("mcast-port", "0"); - gemfireProperties.setProperty("log-level", logLevel()); - return gemfireProperties; - } - - String applicationName() { - return SimpleGemfireRepositoryIntegrationTests.class.getName(); - } - - String logLevel() { - return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL); - } - - @Bean - CacheFactoryBean gemfireCache() { - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(false); - gemfireCache.setProperties(gemfireProperties()); - - return gemfireCache; - } - @Bean(name = "People") LocalRegionFactoryBean peopleRegion(GemFireCache gemfireCache) { LocalRegionFactoryBean peopleRegion = new LocalRegionFactoryBean(); diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTest.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTest.java deleted file mode 100644 index 50d2b2d1..00000000 --- a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * 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.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -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.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - -import com.gemstone.gemfire.cache.Cache; -import com.gemstone.gemfire.cache.CacheTransactionManager; -import com.gemstone.gemfire.cache.DataPolicy; -import com.gemstone.gemfire.cache.Region; -import com.gemstone.gemfire.cache.RegionAttributes; - -import org.junit.Test; -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.gemfire.util.CollectionUtils; -import org.springframework.data.repository.core.EntityInformation; - -/** - * The SimpleGemfireRepositoryUnitTest class is a test suite of test cases testing the contract and functionality - * of the SimpleGemfireRepository class. - * - * @author John Blum - * @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 SimpleGemfireRepositoryUnitTest { - - protected Map asMap(Iterable animals) { - Map animalMap = new HashMap(); - for (Animal animal : animals) { - animalMap.put(animal.getId(), animal); - } - return animalMap; - } - - protected Animal createAnimal(final String name) { - Animal animal = new Animal(); - animal.setName(name); - return animal; - } - - protected Animal createAnimal(final Long id, final String name) { - Animal animal = createAnimal(name); - animal.setId(id); - return animal; - } - - protected GemfireTemplate createGemfireTemplate(final Region region) { - return new GemfireTemplate(region); - } - - protected Cache mockCache(final String mockName, final boolean transactionExists) { - Cache mockCache = mock(Cache.class, String.format("%1$s.MockCache", mockName)); - - CacheTransactionManager mockCacheTransactionManager = mock(CacheTransactionManager.class, String.format( - "%1$s.MockCacheTransactionManager", mockName)); - - when(mockCache.getCacheTransactionManager()).thenReturn(mockCacheTransactionManager); - when(mockCacheTransactionManager.exists()).thenReturn(transactionExists); - - return mockCache; - } - - protected EntityInformation mockEntityInformation() { - EntityInformation mockEntityInformation = mock(EntityInformation.class); - - doAnswer(new Answer() { - private final AtomicLong idSequence = new AtomicLong(0l); - @Override public Long answer(final InvocationOnMock invocation) throws Throwable { - Animal argument = Animal.class.cast(invocation.getArguments()[0]); - Long id = argument.getId(); - id = (id != null ? id : idSequence.incrementAndGet()); - argument.setId(id); - return id; - - } - }).when(mockEntityInformation).getId(any(Animal.class)); - - return mockEntityInformation; - } - - protected Region mockRegion(final String mockName, final Cache mockCache, final DataPolicy dataPolicy) { - Region mockRegion = mock(Region.class, String.format("%1$s.MockRegion", mockName)); - - when(mockRegion.getRegionService()).thenReturn(mockCache); - - RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, String.format( - "%1$s.MockRegionAttributes", mockName)); - - when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); - when(mockRegionAttributes.getDataPolicy()).thenReturn(dataPolicy); - - return mockRegion; - } - - @Test - public void testSave() { - Region mockRegion = mock(Region.class, "testSave"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - Animal dog = repository.save(createAnimal("dog")); - - assertNotNull(dog); - assertEquals(1l, dog.getId().longValue()); - assertEquals("dog", dog.getName()); - - verify(mockRegion, times(1)).put(eq(1l), eq(dog)); - } - - @Test - public void testSaveEntities() { - List animals = new ArrayList(3); - - animals.add(createAnimal("bird")); - animals.add(createAnimal("cat")); - animals.add(createAnimal("dog")); - - Region mockRegion = mock(Region.class, "testSaveAll"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - Iterable savedAnimals = repository.save(animals); - - assertNotNull(savedAnimals); - - verify(mockRegion, times(1)).putAll(eq(asMap(savedAnimals))); - } - - @Test - public void testSaveWrapper() { - Animal dog = createAnimal(1l, "dog"); - - Wrapper dogWrapper = new Wrapper(dog, dog.getId()); - - Region mockRegion = mock(Region.class, "testSaveWrapper"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - repository.save(dogWrapper); - - verify(mockRegion, times(1)).put(eq(dog.getId()), eq(dog)); - } - - @Test - public void testExists() { - final Animal dog = createAnimal(1l, "dog"); - - Region mockRegion = mock(Region.class, "testFindOne"); - - when(mockRegion.get(any(Long.class))).then(new Answer() { - @Override public Animal answer(final InvocationOnMock invocation) throws Throwable { - return (dog.getId().equals(invocation.getArguments()[0]) ? dog : null); - } - }); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - assertTrue(repository.exists(1l)); - assertFalse(repository.exists(10l)); - } - - @Test - public void testFindOne() { - final Animal dog = createAnimal(1l, "dog"); - - Region mockRegion = mock(Region.class, "testFindOne"); - - when(mockRegion.get(any(Long.class))).then(new Answer() { - @Override public Animal answer(final InvocationOnMock invocation) throws Throwable { - return (dog.getId().equals(invocation.getArguments()[0]) ? dog : null); - } - }); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - assertEquals(dog, repository.findOne(1l)); - assertNull(repository.findOne(10l)); - } - - @Test - public void testFindAll() { - long id = 0; - - final List animals = new ArrayList(3); - - animals.add(createAnimal(++id, "bird")); - animals.add(createAnimal(++id, "cat")); - animals.add(createAnimal(++id, "dog")); - - Region mockRegion = mock(Region.class, "testSaveAll"); - - when(mockRegion.getAll(any(Collection.class))).then(new Answer>() { - @Override public Map answer(final InvocationOnMock invocation) throws Throwable { - Collection keys = (Collection) invocation.getArguments()[0]; - Map result = new HashMap(keys.size()); - for (Animal animal : animals) { - if (keys.contains(animal.getId())) { - result.put(animal.getId(), animal); - } - } - return result; - } - }); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - Collection animalsFound = repository.findAll(Arrays.asList(1l, 3l)); - - assertNotNull(animalsFound); - assertEquals(2, animalsFound.size()); - assertTrue(animalsFound.containsAll(CollectionUtils.subList(animals, 0, 2))); - - verify(mockRegion, times(1)).getAll(eq(Arrays.asList(1l, 3l))); - } - - @Test - public void testDeleteById() { - Region mockRegion = mock(Region.class, "testDeleteById"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - repository.delete(1l); - - verify(mockRegion, times(1)).remove(eq(1l)); - } - - @Test - public void testDeleteEntity() { - Region mockRegion = mock(Region.class, "testDeleteEntity"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - repository.delete(createAnimal(1l, "dog")); - - verify(mockRegion, times(1)).remove(eq(1l)); - } - - @Test - public void testDeleteEntities() { - Region mockRegion = mock(Region.class, "testDeleteEntities"); - - SimpleGemfireRepository repository = new SimpleGemfireRepository( - createGemfireTemplate(mockRegion), mockEntityInformation()); - - repository.delete(Arrays.asList(createAnimal(1l, "bird"), createAnimal(2l, "cat"), createAnimal(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 testDeleteAllWithClear() { - Cache mockCache = mockCache("testDeleteAllWithClear.MockCache", false); - - Region mockRegion = mockRegion("testDeleteAllWithClear.MockRegion", - mockCache, DataPolicy.REPLICATE); - - SimpleGemfireRepository gemfireRepository = new SimpleGemfireRepository( - createGemfireTemplate(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 testDeleteAllWithKeysWhenClearThrowsException() { - Cache mockCache = mockCache("testDeleteAllWithKeysWhenClearThrowsException.MockCache", false); - - Region mockRegion = mockRegion("testDeleteAllWithKeysWhenClearThrowsException.MockRegion", - mockCache, DataPolicy.PERSISTENT_REPLICATE); - - Set keys = new HashSet(Arrays.asList(1l, 2l, 3l)); - - doThrow(new UnsupportedOperationException("Not Implemented!")).when(mockRegion).clear(); - when(mockRegion.keySet()).thenReturn(keys); - - SimpleGemfireRepository gemfireRepository = new SimpleGemfireRepository( - createGemfireTemplate(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 testDeleteAllWithKeysWhenPartitionRegion() { - Cache mockCache = mockCache("testDeleteAllWithKeysWhenPartitionRegion.MockCache", false); - - Region mockRegion = mockRegion("testDeleteAllWithKeysWhenPartitionRegion.MockRegion", - mockCache, DataPolicy.PERSISTENT_PARTITION); - - Set keys = new HashSet(Arrays.asList(1l, 2l, 3l)); - - when(mockRegion.keySet()).thenReturn(keys); - - SimpleGemfireRepository gemfireRepository = new SimpleGemfireRepository( - createGemfireTemplate(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 testDeleteAllWithKeysWhenTransactionPresent() { - Cache mockCache = mockCache("testDeleteAllWithKeysWhenTransactionPresent.MockCache", true); - - Region mockRegion = mockRegion("testDeleteAllWithKeysWhenTransactionPresent.MockRegion", - mockCache, DataPolicy.REPLICATE); - - Set keys = new HashSet(Arrays.asList(1l, 2l, 3l)); - - when(mockRegion.keySet()).thenReturn(keys); - - SimpleGemfireRepository gemfireRepository = new SimpleGemfireRepository( - createGemfireTemplate(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)); - } -} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTests.java new file mode 100644 index 00000000..8205a6aa --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryUnitTests.java @@ -0,0 +1,516 @@ +/* + * 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.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.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 com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheTransactionManager; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionAttributes; +import com.gemstone.gemfire.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 asMap(Iterable animals) { + Map 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 mockEntityInformation() { + EntityInformation mockEntityInformation = mock(EntityInformation.class); + + doAnswer(new Answer() { + private final AtomicLong idSequence = new AtomicLong(0L); + + @Override + public Long answer(InvocationOnMock invocation) throws Throwable { + Animal argument = invocation.getArgumentAt(0, Animal.class); + Long id = argument.getId(); + id = (id != null ? id : idSequence.incrementAndGet()); + argument.setId(id); + return id; + + } + }).when(mockEntityInformation).getId(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 testSave() { + Region mockRegion = mockRegion(); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + Animal dog = repository.save(newAnimal("dog")); + + assertNotNull(dog); + assertEquals(1L, dog.getId().longValue()); + assertEquals("dog", dog.getName()); + + verify(mockRegion, times(1)).put(eq(1L), eq(dog)); + } + + @Test + public void testSaveEntities() { + List animals = new ArrayList(3); + + animals.add(newAnimal("bird")); + animals.add(newAnimal("cat")); + animals.add(newAnimal("dog")); + + Region mockRegion = mockRegion(); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + Iterable savedAnimals = repository.save(animals); + + assertNotNull(savedAnimals); + + verify(mockRegion, times(1)).putAll(eq(asMap(savedAnimals))); + } + + @Test + public void testSaveWrapper() { + Animal dog = newAnimal(1L, "dog"); + + Wrapper dogWrapper = new Wrapper(dog, dog.getId()); + + Region mockRegion = mockRegion(); + + SimpleGemfireRepository 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 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 testExists() { + final Animal dog = newAnimal(1L, "dog"); + + Region mockRegion = mockRegion(); + + when(mockRegion.get(any(Long.class))).then(new Answer() { + @Override public Animal answer(InvocationOnMock invocation) throws Throwable { + return (dog.getId().equals(invocation.getArguments()[0]) ? dog : null); + } + }); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + assertTrue(repository.exists(1L)); + assertFalse(repository.exists(10L)); + } + + @Test + public void testFindOne() { + final Animal dog = newAnimal(1L, "dog"); + + Region mockRegion = mockRegion(); + + when(mockRegion.get(any(Long.class))).then(new Answer() { + @Override public Animal answer(InvocationOnMock invocation) throws Throwable { + return (dog.getId().equals(invocation.getArguments()[0]) ? dog : null); + } + }); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + assertEquals(dog, repository.findOne(1L)); + assertNull(repository.findOne(10L)); + } + + @Test + public void testFindAll() { + final Map animals = new HashMap(3); + + animals.put(1L, newAnimal(1L, "bird")); + animals.put(2L, newAnimal(2L, "cat")); + animals.put(3L, newAnimal(3L, "dog")); + + Region mockRegion = mockRegion(); + + when(mockRegion.getAll(any(Collection.class))).then(new Answer>() { + @Override public Map answer(InvocationOnMock invocation) throws Throwable { + Collection keys = invocation.getArgumentAt(0, Collection.class); + Map results = new HashMap(keys.size()); + + for (Long key : keys) { + results.put(key, animals.get(key)); + } + + return results; + } + }); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + Collection animalsFound = repository.findAll(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 mockRegion = mockRegion(); + + when(mockRegion.getAll(any(Collection.class))).then(new Answer>() { + @Override public Map answer(InvocationOnMock invocation) throws Throwable { + Collection keys = invocation.getArgumentAt(0, Collection.class); + Map result = new HashMap(keys.size()); + + for (Long key : keys) { + result.put(key, null); + } + + return result; + } + }); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + Collection animalsFound = repository.findAll(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() { + final Map animals = new HashMap(3); + + animals.put(1L, newAnimal(1L, "bird")); + animals.put(2L, newAnimal(2L, "cat")); + animals.put(3L, newAnimal(3L, "dog")); + + Region mockRegion = mockRegion(); + + when(mockRegion.getAll(any(Collection.class))).then(new Answer>() { + @Override public Map answer(InvocationOnMock invocation) throws Throwable { + Collection keys = invocation.getArgumentAt(0, Collection.class); + Map result = new HashMap(keys.size()); + + for (Long key : keys) { + result.put(key, animals.get(key)); + } + + return result; + } + }); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + Collection animalsFound = repository.findAll(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 testDeleteById() { + Region mockRegion = mockRegion(); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + repository.delete(1L); + + verify(mockRegion, times(1)).remove(eq(1L)); + } + + @Test + public void testDeleteEntity() { + Region mockRegion = mockRegion(); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + repository.delete(newAnimal(1L, "dog")); + + verify(mockRegion, times(1)).remove(eq(1L)); + } + + @Test + public void testDeleteEntities() { + Region mockRegion = mockRegion(); + + SimpleGemfireRepository repository = new SimpleGemfireRepository( + newGemfireTemplate(mockRegion), mockEntityInformation()); + + repository.delete(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 testDeleteAllWithClear() { + Cache mockCache = mockCache("MockCache", false); + + Region mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.REPLICATE); + + SimpleGemfireRepository 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 testDeleteAllWithKeysWhenClearThrowsException() { + Cache mockCache = mockCache("MockCache", false); + + Region mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.PERSISTENT_REPLICATE); + + Set keys = new HashSet(Arrays.asList(1L, 2L, 3L)); + + doThrow(new UnsupportedOperationException("Not Implemented!")).when(mockRegion).clear(); + when(mockRegion.keySet()).thenReturn(keys); + + SimpleGemfireRepository 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 testDeleteAllWithKeysWhenPartitionRegion() { + Cache mockCache = mockCache("MockCache", false); + + Region mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.PERSISTENT_PARTITION); + + Set keys = new HashSet(Arrays.asList(1L, 2L, 3L)); + + when(mockRegion.keySet()).thenReturn(keys); + + SimpleGemfireRepository 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 testDeleteAllWithKeysWhenTransactionPresent() { + Cache mockCache = mockCache("MockCache", true); + + Region mockRegion = mockRegion("MockRegion", mockCache, DataPolicy.REPLICATE); + + Set keys = new HashSet(Arrays.asList(1L, 2L, 3L)); + + when(mockRegion.keySet()).thenReturn(keys); + + SimpleGemfireRepository 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)); + } +}