diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java index 4efb721..e100bc0 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/GemFireMockObjectsSupport.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import static org.springframework.data.gemfire.tests.util.IOUtils.doSafeIo; import static org.springframework.data.gemfire.tests.util.ObjectUtils.rethrowAsRuntimeException; import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; @@ -50,6 +51,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.Set; @@ -74,16 +76,19 @@ import org.apache.geode.cache.CacheFactory; import org.apache.geode.cache.CacheListener; import org.apache.geode.cache.CacheLoader; import org.apache.geode.cache.CacheWriter; +import org.apache.geode.cache.CacheWriterException; import org.apache.geode.cache.CustomExpiry; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.cache.EntryEvent; import org.apache.geode.cache.EntryNotFoundException; import org.apache.geode.cache.EvictionAttributes; import org.apache.geode.cache.EvictionAttributesMutator; import org.apache.geode.cache.ExpirationAction; import org.apache.geode.cache.ExpirationAttributes; import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.LoaderHelper; import org.apache.geode.cache.PartitionAttributes; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; @@ -787,7 +792,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(batchTimeInterval, mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setDiskStoreName(anyString())) - .thenAnswer(newSetter(diskStoreName, mockAsyncEventQueueFactory)); + .thenAnswer(newSetter(diskStoreName, () -> mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setDiskSynchronous(anyBoolean())) .thenAnswer(newSetter(diskSynchronous, mockAsyncEventQueueFactory)); @@ -799,13 +804,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(forwardExpirationDestroy, mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setGatewayEventSubstitutionListener(any(GatewayEventSubstitutionFilter.class))) - .thenAnswer(newSetter(gatewayEventSubstitutionFilter, mockAsyncEventQueueFactory)); + .thenAnswer(newSetter(gatewayEventSubstitutionFilter, () -> mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setMaximumQueueMemory(anyInt())) .thenAnswer(newSetter(maximumQueueMemory, mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setOrderPolicy(any(GatewaySender.OrderPolicy.class))) - .thenAnswer(newSetter(orderPolicy, mockAsyncEventQueueFactory)); + .thenAnswer(newSetter(orderPolicy, () -> mockAsyncEventQueueFactory)); when(mockAsyncEventQueueFactory.setParallel(anyBoolean())) .thenAnswer(newSetter(parallel, mockAsyncEventQueueFactory)); @@ -882,10 +887,10 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { AtomicReference hostnameForClients = new AtomicReference<>(CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS); AtomicReference serverLoadProbe = new AtomicReference<>(null); - doAnswer(newSetter(bindAddress, null)) + doAnswer(newSetter(bindAddress, () -> null)) .when(mockCacheServer).setBindAddress(anyString()); - doAnswer(newSetter(hostnameForClients, null)) + doAnswer(newSetter(hostnameForClients, () -> null)) .when(mockCacheServer).setHostnameForClients(anyString()); doAnswer(newSetter(loadPollInterval, null)) @@ -909,7 +914,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(newSetter(port, null)) .when(mockCacheServer).setPort(anyInt()); - doAnswer(newSetter(serverLoadProbe, null)) + doAnswer(newSetter(serverLoadProbe, () -> null)) .when(mockCacheServer).setLoadProbe(any(ServerLoadProbe.class)); doAnswer(newSetter(socketBufferSize, null)) @@ -1048,7 +1053,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(cloningEnabled, mockClientRegionFactory)); when(mockClientRegionFactory.setCompressor(any(Compressor.class))) - .thenAnswer(newSetter(compressor, mockClientRegionFactory)); + .thenAnswer(newSetter(compressor, () -> mockClientRegionFactory)); doAnswer(newSetter(concurrencyChecksEnabled, mockClientRegionFactory)) .when(mockClientRegionFactory).setConcurrencyChecksEnabled(anyBoolean()); @@ -1057,49 +1062,49 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(concurrencyLevel, mockClientRegionFactory)); when(mockClientRegionFactory.setCustomEntryIdleTimeout(any(CustomExpiry.class))) - .thenAnswer(newSetter(customEntryIdleTimeout, mockClientRegionFactory)); + .thenAnswer(newSetter(customEntryIdleTimeout, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setCustomEntryTimeToLive(any(CustomExpiry.class))) - .thenAnswer(newSetter(customEntryTimeToLive, mockClientRegionFactory)); + .thenAnswer(newSetter(customEntryTimeToLive, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setDiskStoreName(anyString())) - .thenAnswer(newSetter(diskStoreName, mockClientRegionFactory)); + .thenAnswer(newSetter(diskStoreName, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setDiskSynchronous(anyBoolean())) .thenAnswer(newSetter(diskSynchronous, mockClientRegionFactory)); when(mockClientRegionFactory.setEntryIdleTimeout(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(entryIdleTimeout, mockClientRegionFactory)); + .thenAnswer(newSetter(entryIdleTimeout, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setEntryTimeToLive(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(entryTimeToLive, mockClientRegionFactory)); + .thenAnswer(newSetter(entryTimeToLive, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setEvictionAttributes(any(EvictionAttributes.class))) - .thenAnswer(newSetter(evictionAttributes, mockClientRegionFactory)); + .thenAnswer(newSetter(evictionAttributes, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setInitialCapacity(anyInt())) .thenAnswer(newSetter(initialCapacity, mockClientRegionFactory)); when(mockClientRegionFactory.setKeyConstraint(any(Class.class))) - .thenAnswer(newSetter(keyConstraint, mockClientRegionFactory)); + .thenAnswer(newSetter(keyConstraint, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setLoadFactor(anyFloat())) - .thenAnswer(newSetter(loadFactor, mockClientRegionFactory)); + .thenAnswer(newSetter(loadFactor, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setPoolName(anyString())) - .thenAnswer(newSetter(poolName, mockClientRegionFactory)); + .thenAnswer(newSetter(poolName, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setRegionIdleTimeout(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(regionIdleTimeout, mockClientRegionFactory)); + .thenAnswer(newSetter(regionIdleTimeout, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setRegionTimeToLive(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(regionTimeToLive, mockClientRegionFactory)); + .thenAnswer(newSetter(regionTimeToLive, () -> mockClientRegionFactory)); when(mockClientRegionFactory.setStatisticsEnabled(anyBoolean())) .thenAnswer(newSetter(statisticsEnabled, mockClientRegionFactory)); when(mockClientRegionFactory.setValueConstraint(any(Class.class))) - .thenAnswer(newSetter(valueConstraint, mockClientRegionFactory)); + .thenAnswer(newSetter(valueConstraint, () -> mockClientRegionFactory)); RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, mockObjectIdentifier("MockRegionAttributes")); @@ -1157,7 +1162,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(newSetter(subscriptionCapacity, null)) .when(mockClientSubscriptionConfig).setCapacity(anyInt()); - doAnswer(newSetter(subscriptionDiskStoreName, null)) + doAnswer(newSetter(subscriptionDiskStoreName, () -> null)) .when(mockClientSubscriptionConfig).setDiskStoreName(anyString()); doAnswer(newSetter(subscriptionEvictionPolicy, stringToSubscriptionEvictionPolicyConverter, null)) @@ -1228,10 +1233,10 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { }); when(mockDiskStoreFactory.setDiskUsageCriticalPercentage(anyFloat())) - .thenAnswer(newSetter(diskUsageCriticalPercentage, mockDiskStoreFactory)); + .thenAnswer(newSetter(diskUsageCriticalPercentage, () -> mockDiskStoreFactory)); when(mockDiskStoreFactory.setDiskUsageWarningPercentage(anyFloat())) - .thenAnswer(newSetter(diskUsageWarningPercentage, mockDiskStoreFactory)); + .thenAnswer(newSetter(diskUsageWarningPercentage, () -> mockDiskStoreFactory)); when(mockDiskStoreFactory.setMaxOplogSize(anyLong())) .thenAnswer(newSetter(maxOplogSize, mockDiskStoreFactory)); @@ -1325,13 +1330,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { CopyOnWriteArrayList gatewayTransportFilters = new CopyOnWriteArrayList<>(); when(mockGatewayReceiverFactory.setBindAddress(anyString())) - .thenAnswer(newSetter(bindAddress, mockGatewayReceiverFactory)); + .thenAnswer(newSetter(bindAddress, () -> mockGatewayReceiverFactory)); when(mockGatewayReceiverFactory.setEndPort(anyInt())) .thenAnswer(newSetter(endPort, mockGatewayReceiverFactory)); when(mockGatewayReceiverFactory.setHostnameForSenders(anyString())) - .thenAnswer(newSetter(hostnameForSenders, mockGatewayReceiverFactory)); + .thenAnswer(newSetter(hostnameForSenders, () -> mockGatewayReceiverFactory)); when(mockGatewayReceiverFactory.setManualStart(anyBoolean())) .thenAnswer(newSetter(manualStart, mockGatewayReceiverFactory)); @@ -1433,7 +1438,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(batchTimeInterval, mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setDiskStoreName(anyString())) - .thenAnswer(newSetter(diskStoreName, mockGatewaySenderFactory)); + .thenAnswer(newSetter(diskStoreName, () -> mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setDiskSynchronous(anyBoolean())) .thenAnswer(newSetter(diskSynchronous, mockGatewaySenderFactory)); @@ -1442,13 +1447,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(dispatcherThreads, mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setGatewayEventSubstitutionFilter(any(GatewayEventSubstitutionFilter.class))) - .thenAnswer(newSetter(gatewayEventSubstitutionFilter, mockGatewaySenderFactory)); + .thenAnswer(newSetter(gatewayEventSubstitutionFilter, () -> mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setMaximumQueueMemory(anyInt())) .thenAnswer(newSetter(maximumQueueMemory, mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setOrderPolicy(any(GatewaySender.OrderPolicy.class))) - .thenAnswer(newSetter(orderPolicy, mockGatewaySenderFactory)); + .thenAnswer(newSetter(orderPolicy, () -> mockGatewaySenderFactory)); when(mockGatewaySenderFactory.setParallel(anyBoolean())).thenAnswer(newSetter(parallel, mockGatewaySenderFactory)); @@ -1624,7 +1629,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(retryAttempts, mockPoolFactory)); when(mockPoolFactory.setServerGroup(anyString())) - .thenAnswer(newSetter(serverGroup, mockPoolFactory)); + .thenAnswer(newSetter(serverGroup, () -> mockPoolFactory)); when(mockPoolFactory.setSocketBufferSize(anyInt())) .thenAnswer(newSetter(socketBufferSize, mockPoolFactory)); @@ -2168,14 +2173,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { Map data = new ConcurrentHashMap<>(); - Region mockRegion = mock(Region.class, name); + Region mockRegion = mock(Region.class, withSettings().name(name).lenient()); RegionAttributes mockRegionAttributes = mockRegionAttributes(mockRegion, regionAttributes); Set invalidatedKeys = new HashSet<>(); Set> subRegions = new CopyOnWriteArraySet<>(); - when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); when(mockRegion.getFullPath()).thenReturn(toRegionPath(name)); when(mockRegion.getName()).thenReturn(toRegionName(name)); when(mockRegion.getRegionService()).thenReturn(regionService); @@ -2196,15 +2200,33 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { when(mockRegion.get(ArgumentMatchers.any())).thenAnswer(invocation -> { K key = invocation.getArgument(0); - V value = data.get(key); + V value = invalidatedKeys.contains(key) ? null : data.get(key); - if (invalidatedKeys.contains(key)) { - value = null; + if (value == null) { + + value = Optional.ofNullable(mockRegionAttributes.getCacheLoader()) + .map(cacheLoader -> { + + LoaderHelper mockLoaderHelper = mock(LoaderHelper.class, withSettings().lenient()); + + when(mockLoaderHelper.getArgument()).thenReturn(null); + when(mockLoaderHelper.getKey()).thenReturn(key); + when(mockLoaderHelper.getRegion()).thenReturn(mockRegion); + + return cacheLoader.load(mockLoaderHelper); + + }) + .map(loadedValue -> { + + data.put(key, loadedValue); + invalidatedKeys.remove(key); + + return loadedValue; + + }) + .orElse(value); } - // TODO: CacheLoader - Optional.ofNullable(value); - return value; }); @@ -2231,13 +2253,24 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { K key = invocation.getArgument(0); - if (data.containsKey(key)) { - invalidatedKeys.add(key); - } - else { + if (!data.containsKey(key)) { throw new EntryNotFoundException(String.format("Entry with key [%s] not found", key)); } + if (invalidatedKeys.add(key)) { + + EntryEvent mockEntryEvent = mock(EntryEvent.class, withSettings().lenient()); + + when(mockEntryEvent.getKey()).thenReturn(key); + when(mockEntryEvent.getNewValue()).thenReturn(null); + when(mockEntryEvent.getOldValue()).thenReturn(data.get(key)); + when(mockEntryEvent.getRegion()).thenReturn(mockRegion); + + Arrays.stream(ArrayUtils.nullSafeArray(mockRegionAttributes.getCacheListeners(), CacheListener.class)) + .filter(Objects::nonNull) + .forEach(cacheListener -> cacheListener.afterInvalidate(mockEntryEvent)); + } + return null; }).when(mockRegion).invalidate(any()); @@ -2247,8 +2280,50 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { K key = invocation.getArgument(0); V newValue = invocation.getArgument(1); + + Assert.notNull(newValue, "Value is required"); + + boolean entryExists = data.containsKey(key); + + EntryEvent mockEntryEvent = mock(EntryEvent.class, withSettings().lenient()); + + V entryEventValue = invalidatedKeys.contains(key) ? null : data.get(key); + + when(mockEntryEvent.getKey()).thenReturn(key); + when(mockEntryEvent.getNewValue()).thenReturn(newValue); + when(mockEntryEvent.getOldValue()).thenReturn(entryEventValue); + when(mockEntryEvent.getRegion()).thenReturn(mockRegion); + + CacheWriter cacheWriter = mockRegionAttributes.getCacheWriter(); + + if (cacheWriter != null) { + try { + if (entryExists) { + cacheWriter.beforeUpdate(mockEntryEvent); + } + else { + cacheWriter.beforeCreate(mockEntryEvent); + } + } + catch (Throwable cause) { + throw new CacheWriterException("Create/Update Error", cause); + } + } + V existingValue = data.put(key, newValue); + Arrays.stream(ArrayUtils.nullSafeArray(mockRegionAttributes.getCacheListeners(), CacheListener.class)) + .filter(Objects::nonNull) + .forEach(cacheListener -> { + + if (entryExists) { + cacheListener.afterUpdate(mockEntryEvent); + } + else { + cacheListener.afterCreate(mockEntryEvent); + } + }); + return invalidatedKeys.remove(key) ? null : existingValue; }); @@ -2256,8 +2331,33 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { when(mockRegion.remove(any())).thenAnswer(invocation -> { K key = invocation.getArgument(0); + + EntryEvent mockEntryEvent = mock(EntryEvent.class, withSettings().lenient()); + + V entryEventValue = invalidatedKeys.contains(key) ? null : data.get(key); + + when(mockEntryEvent.getKey()).thenReturn(key); + when(mockEntryEvent.getNewValue()).thenReturn(null); + when(mockEntryEvent.getOldValue()).thenReturn(entryEventValue); + when(mockEntryEvent.getRegion()).thenReturn(mockRegion); + + CacheWriter cacheWriter = mockRegionAttributes.getCacheWriter(); + + if (cacheWriter != null) { + try { + cacheWriter.beforeDestroy(mockEntryEvent); + } + catch (Throwable cause) { + throw new CacheWriterException("Destroy Error", cause); + } + } + V value = data.remove(key); + Arrays.stream(ArrayUtils.nullSafeArray(mockRegionAttributes.getCacheListeners(), CacheListener.class)) + .filter(Objects::nonNull) + .forEach(cacheListener -> cacheListener.afterDestroy(mockEntryEvent)); + return invalidatedKeys.remove(key) ? null : value; }); @@ -2282,12 +2382,14 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { private static RegionAttributes mockRegionAttributes(Region mockRegion, RegionAttributes baseRegionAttributes) { - AttributesMutator mockAttributesMutator = mock(AttributesMutator.class); + AttributesMutator mockAttributesMutator = mock(AttributesMutator.class, withSettings().lenient()); - EvictionAttributesMutator mockEvictionAttributesMutator = mock(EvictionAttributesMutator.class); + EvictionAttributesMutator mockEvictionAttributesMutator = + mock(EvictionAttributesMutator.class, withSettings().lenient()); - RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, withSettings().lenient()); + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator); when(mockAttributesMutator.getEvictionAttributesMutator()).thenReturn(mockEvictionAttributesMutator); when(mockAttributesMutator.getRegion()).thenReturn(mockRegion); @@ -2357,7 +2459,6 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { }).when(mockAttributesMutator).initCacheListeners(any(CacheListener[].class)); - doAnswer(invocation -> asyncEventQueueIds.remove(invocation.getArgument(0))) .when(mockAttributesMutator).removeAsyncEventQueueId(anyString()); @@ -2367,35 +2468,36 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(invocation -> gatewaySenderIds.remove(invocation.getArgument(0))) .when(mockAttributesMutator).removeGatewaySenderId(anyString()); - doAnswer(newSetter(cacheLoader, baseRegionAttributes.getCacheLoader())) + doAnswer(newSetter(cacheLoader)) .when(mockAttributesMutator).setCacheLoader(any(CacheLoader.class)); - doAnswer(newSetter(cacheWriter, baseRegionAttributes.getCacheWriter())) + doAnswer(newSetter(cacheWriter)) .when(mockAttributesMutator).setCacheWriter(any(CacheWriter.class)); doAnswer(newSetter(cloningEnabled, null)) .when(mockAttributesMutator).setCloningEnabled(anyBoolean()); - doAnswer(newSetter(customEntryIdleTimeout, baseRegionAttributes.getCustomEntryIdleTimeout())) + doAnswer(newSetter(customEntryIdleTimeout)) .when(mockAttributesMutator).setCustomEntryIdleTimeout(any(CustomExpiry.class)); - doAnswer(newSetter(customEntryTimeToLive, baseRegionAttributes.getCustomEntryTimeToLive())) + doAnswer(newSetter(customEntryTimeToLive)) .when(mockAttributesMutator).setCustomEntryTimeToLive(any(CustomExpiry.class)); - doAnswer(newSetter(entryIdleTimeout, baseRegionAttributes.getEntryIdleTimeout())) + doAnswer(newSetter(entryIdleTimeout)) .when(mockAttributesMutator).setEntryIdleTimeout(any(ExpirationAttributes.class)); - doAnswer(newSetter(entryTimeToLive, baseRegionAttributes.getEntryTimeToLive())) + doAnswer(newSetter(entryTimeToLive)) .when(mockAttributesMutator).setEntryTimeToLive(any(ExpirationAttributes.class)); - doAnswer(newSetter(regionIdleTimeout, baseRegionAttributes.getRegionIdleTimeout())) + doAnswer(newSetter(regionIdleTimeout)) .when(mockAttributesMutator).setRegionIdleTimeout(any(ExpirationAttributes.class)); - doAnswer(newSetter(regionTimeToLive, baseRegionAttributes.getRegionTimeToLive())) + doAnswer(newSetter(regionTimeToLive)) .when(mockAttributesMutator).setRegionTimeToLive(any(ExpirationAttributes.class)); // Mock EvictionAttributesMutator - doAnswer(newSetter(evictionMaximum, null)).when(mockEvictionAttributesMutator).setMaximum(anyInt()); + doAnswer(newSetter(evictionMaximum, null)) + .when(mockEvictionAttributesMutator).setMaximum(anyInt()); // Mock RegionAttributes when(mockRegionAttributes.getAsyncEventQueueIds()) @@ -2619,16 +2721,16 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { }); when(mockRegionFactory.setCacheLoader(any(CacheLoader.class))) - .thenAnswer(newSetter(cacheLoader, mockRegionFactory)); + .thenAnswer(newSetter(cacheLoader, () -> mockRegionFactory)); when(mockRegionFactory.setCacheWriter(any(CacheWriter.class))) - .thenAnswer(newSetter(cacheWriter, mockRegionFactory)); + .thenAnswer(newSetter(cacheWriter, () -> mockRegionFactory)); when(mockRegionFactory.setCloningEnabled(anyBoolean())) .thenAnswer(newSetter(cloningEnabled, mockRegionFactory)); when(mockRegionFactory.setCompressor(any(Compressor.class))) - .thenAnswer(newSetter(compressor, mockRegionFactory)); + .thenAnswer(newSetter(compressor, () -> mockRegionFactory)); when(mockRegionFactory.setConcurrencyChecksEnabled(anyBoolean())) .then(newSetter(concurrencyChecksEnabled, mockRegionFactory)); @@ -2637,15 +2739,16 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(concurrencyLevel, mockRegionFactory)); when(mockRegionFactory.setCustomEntryIdleTimeout(any(CustomExpiry.class))) - .thenAnswer(newSetter(customEntryIdleTimeout, mockRegionFactory)); + .thenAnswer(newSetter(customEntryIdleTimeout, () -> mockRegionFactory)); when(mockRegionFactory.setCustomEntryTimeToLive(any(CustomExpiry.class))) - .thenAnswer(newSetter(customEntryTimeToLive, mockRegionFactory)); + .thenAnswer(newSetter(customEntryTimeToLive, () -> mockRegionFactory)); when(mockRegionFactory.setDataPolicy(any(DataPolicy.class))) - .thenAnswer(newSetter(dataPolicy, mockRegionFactory)); + .thenAnswer(newSetter(dataPolicy, () -> mockRegionFactory)); - when(mockRegionFactory.setDiskStoreName(anyString())).thenAnswer(newSetter(diskStoreName, mockRegionFactory)); + when(mockRegionFactory.setDiskStoreName(anyString())) + .thenAnswer(newSetter(diskStoreName, () -> mockRegionFactory)); when(mockRegionFactory.setDiskSynchronous(anyBoolean())) .thenAnswer(newSetter(diskSynchronous, mockRegionFactory)); @@ -2657,13 +2760,13 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { .thenAnswer(newSetter(enableSubscriptionConflation, mockRegionFactory)); when(mockRegionFactory.setEntryIdleTimeout(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(entryIdleTimeout, mockRegionFactory)); + .thenAnswer(newSetter(entryIdleTimeout, () -> mockRegionFactory)); when(mockRegionFactory.setEntryTimeToLive(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(entryTimeToLive, mockRegionFactory)); + .thenAnswer(newSetter(entryTimeToLive, () -> mockRegionFactory)); when(mockRegionFactory.setEvictionAttributes(any(EvictionAttributes.class))) - .thenAnswer(newSetter(evictionAttributes, mockRegionFactory)); + .thenAnswer(newSetter(evictionAttributes, () -> mockRegionFactory)); when(mockRegionFactory.setIgnoreJTA(anyBoolean())).thenAnswer(newSetter(ignoreJta, mockRegionFactory)); @@ -2673,9 +2776,9 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { when(mockRegionFactory.setInitialCapacity(anyInt())).thenAnswer(newSetter(initialCapacity, mockRegionFactory)); when(mockRegionFactory.setKeyConstraint(any(Class.class))) - .thenAnswer(newSetter(keyConstraint, mockRegionFactory)); + .thenAnswer(newSetter(keyConstraint, () -> mockRegionFactory)); - when(mockRegionFactory.setLoadFactor(anyFloat())).thenAnswer(newSetter(loadFactor, mockRegionFactory)); + when(mockRegionFactory.setLoadFactor(anyFloat())).thenAnswer(newSetter(loadFactor, () -> mockRegionFactory)); when(mockRegionFactory.setLockGrantor(anyBoolean())).thenAnswer(newSetter(lockGrantor, mockRegionFactory)); @@ -2685,26 +2788,26 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { when(mockRegionFactory.setOffHeap(anyBoolean())).thenAnswer(newSetter(offHeap, mockRegionFactory)); when(mockRegionFactory.setPartitionAttributes(any(PartitionAttributes.class))) - .thenAnswer(newSetter(partitionAttributes, mockRegionFactory)); + .thenAnswer(newSetter(partitionAttributes, () -> mockRegionFactory)); - when(mockRegionFactory.setPoolName(anyString())).thenAnswer(newSetter(poolName, mockRegionFactory)); + when(mockRegionFactory.setPoolName(anyString())).thenAnswer(newSetter(poolName, () -> mockRegionFactory)); when(mockRegionFactory.setRegionIdleTimeout(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(regionIdleTimeout, mockRegionFactory)); + .thenAnswer(newSetter(regionIdleTimeout, () -> mockRegionFactory)); when(mockRegionFactory.setRegionTimeToLive(any(ExpirationAttributes.class))) - .thenAnswer(newSetter(regionTimeToLive, mockRegionFactory)); + .thenAnswer(newSetter(regionTimeToLive, () -> mockRegionFactory)); - when(mockRegionFactory.setScope(any(Scope.class))).thenAnswer(newSetter(scope, mockRegionFactory)); + when(mockRegionFactory.setScope(any(Scope.class))).thenAnswer(newSetter(scope, () -> mockRegionFactory)); when(mockRegionFactory.setStatisticsEnabled(anyBoolean())) .thenAnswer(newSetter(statisticsEnabled, mockRegionFactory)); when(mockRegionFactory.setSubscriptionAttributes(any(SubscriptionAttributes.class))) - .thenAnswer(newSetter(subscriptionAttributes, mockRegionFactory)); + .thenAnswer(newSetter(subscriptionAttributes, () -> mockRegionFactory)); when(mockRegionFactory.setValueConstraint(any(Class.class))) - .thenAnswer(newSetter(valueConstraint, mockRegionFactory)); + .thenAnswer(newSetter(valueConstraint, () -> mockRegionFactory)); RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, mockObjectIdentifier("MockRegionAttributes")); @@ -2771,16 +2874,16 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { AtomicReference evictionOffHeapPercentage = new AtomicReference<>(0.0f); - doAnswer(newSetter(criticalHeapPercentage, null)) + doAnswer(newSetter(criticalHeapPercentage, () -> null)) .when(mockResourceManager).setCriticalHeapPercentage(anyFloat()); - doAnswer(newSetter(criticalOffHeapPercentage, null)) + doAnswer(newSetter(criticalOffHeapPercentage, () -> null)) .when(mockResourceManager).setCriticalOffHeapPercentage(anyFloat()); - doAnswer(newSetter(evictionHeapPercentage, null)) + doAnswer(newSetter(evictionHeapPercentage, () -> null)) .when(mockResourceManager).setEvictionHeapPercentage(anyFloat()); - doAnswer(newSetter(evictionOffHeapPercentage, null)) + doAnswer(newSetter(evictionOffHeapPercentage, () -> null)) .when(mockResourceManager).setEvictionOffHeapPercentage(anyFloat()); when(mockResourceManager.getCriticalHeapPercentage()).thenAnswer(newGetter(criticalHeapPercentage)); @@ -2812,7 +2915,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { AtomicReference pdxDiskStoreName = new AtomicReference<>(null); AtomicReference pdxSerializer = new AtomicReference<>(null); - doAnswer(newSetter(pdxDiskStoreName, cacheFactorySpy)) + doAnswer(newSetter(pdxDiskStoreName, () -> cacheFactorySpy)) .when(cacheFactorySpy).setPdxDiskStore(anyString()); doAnswer(newSetter(pdxIgnoreUnreadFields, cacheFactorySpy)) @@ -2824,7 +2927,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(newSetter(pdxReadSerialized, cacheFactorySpy)) .when(cacheFactorySpy).setPdxReadSerialized(anyBoolean()); - doAnswer(newSetter(pdxSerializer, cacheFactorySpy)) + doAnswer(newSetter(pdxSerializer, () -> cacheFactorySpy)) .when(cacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); when(mockCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); @@ -2865,7 +2968,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { AtomicReference pdxSerializer = new AtomicReference<>(null); AtomicReference defaultPool = new AtomicReference<>(null); - doAnswer(newSetter(pdxDiskStoreName, clientCacheFactorySpy)) + doAnswer(newSetter(pdxDiskStoreName, () -> clientCacheFactorySpy)) .when(clientCacheFactorySpy).setPdxDiskStore(anyString()); doAnswer(newSetter(pdxIgnoreUnreadFields, clientCacheFactorySpy)) @@ -2877,7 +2980,7 @@ public abstract class GemFireMockObjectsSupport extends MockObjectsSupport { doAnswer(newSetter(pdxReadSerialized, clientCacheFactorySpy)) .when(clientCacheFactorySpy).setPdxReadSerialized(anyBoolean()); - doAnswer(newSetter(pdxSerializer, clientCacheFactorySpy)) + doAnswer(newSetter(pdxSerializer, () -> clientCacheFactorySpy)) .when(clientCacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); PoolFactory mockPoolFactory = mockPoolFactory(); diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/MockObjectsSupport.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/MockObjectsSupport.java index 230bceb..dc61531 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/MockObjectsSupport.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/mock/MockObjectsSupport.java @@ -36,6 +36,13 @@ import org.springframework.util.StringUtils; * used in mocking using Mockito. * * @author John Blum + * @see java.util.concurrent.atomic.AtomicBoolean + * @see java.util.concurrent.atomic.AtomicInteger + * @see java.util.concurrent.atomic.AtomicLong + * @see java.util.concurrent.atomic.AtomicReference + * @see java.util.function.Consumer + * @see java.util.function.Function + * @see java.util.function.Supplier * @see org.mockito.invocation.InvocationOnMock * @see org.mockito.stubbing.Answer * @since 0.0.1 @@ -144,35 +151,53 @@ public abstract class MockObjectsSupport { }; } - protected static Answer newSetter(AtomicReference argument, R returnValue) { + protected static Answer newSetter(AtomicReference argument) { + return invocation -> argument.getAndSet(invocation.getArgument(0)); + } + + protected static Answer newSetter(AtomicReference argument, Supplier returnValue) { return invocation -> { argument.set(invocation.getArgument(0)); - return returnValue; + return returnValue.get(); }; } - protected static Answer newSetter(AtomicReference argument, T value, R returnValue) { + protected static Answer newSetterWithArument(AtomicReference argument, T value) { + return invocation -> argument.getAndSet(value); + } + + protected static Answer newSetterWithArgument(AtomicReference argument, T value, + Supplier returnValue) { return invocation -> { argument.set(value); - return returnValue; + return returnValue.get(); }; } - protected static Answer newSetter(AtomicReference argument, Function converter, R returnValue) { + protected static Answer newSetter(AtomicReference argument, Function converter) { + return invocation -> argument.getAndSet(converter.apply(invocation.getArgument(0))); + } + + protected static Answer newSetter(AtomicReference argument, Function converter, + Supplier returnValue) { return invocation -> { argument.set(converter.apply(invocation.getArgument(0))); - return returnValue; + return returnValue.get(); }; } - protected static Answer newSetter(Map argument, R returnValue) { + protected static Answer newSetter(Map argument) { + return invocation -> argument.put(invocation.getArgument(0), invocation.getArgument(1)); + } + + protected static Answer newSetter(Map argument, Supplier returnValue) { return invocation -> { argument.put(invocation.getArgument(0), invocation.getArgument(1)); - return returnValue; + return returnValue.get(); }; } diff --git a/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/MockRegionDataAccessOperationsAndEventsUnitTests.java b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/MockRegionDataAccessOperationsAndEventsUnitTests.java new file mode 100644 index 0000000..c830561 --- /dev/null +++ b/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/tests/MockRegionDataAccessOperationsAndEventsUnitTests.java @@ -0,0 +1,423 @@ +/* + * Copyright 2019 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 + * + * https://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.tests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +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.util.RuntimeExceptionFactory.newIllegalStateException; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import org.apache.geode.cache.CacheListener; +import org.apache.geode.cache.CacheLoader; +import org.apache.geode.cache.CacheWriter; +import org.apache.geode.cache.CacheWriterException; +import org.apache.geode.cache.EntryEvent; +import org.apache.geode.cache.EntryNotFoundException; +import org.apache.geode.cache.LoaderHelper; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.cache.RegionService; + +import org.springframework.data.gemfire.tests.mock.GemFireMockObjectsSupport; + +/** + * Unit Tests for Mock {@link Region}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.apache.geode.cache.CacheListener + * @see org.apache.geode.cache.CacheLoader + * @see org.apache.geode.cache.CacheWriter + * @see org.apache.geode.cache.EntryEvent + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.RegionService + * @since 0.0.3 + */ +@RunWith(MockitoJUnitRunner.class) +@SuppressWarnings("unchecked") +public class MockRegionDataAccessOperationsAndEventsUnitTests { + + private Region mockRegion; + + @Mock + private RegionAttributes mockRegionAttributes; + + @Mock + private RegionService mockRegionService; + + @Before + public void setup() { + + this.mockRegion = GemFireMockObjectsSupport.mockRegion(this.mockRegionService, + "MockRegion", this.mockRegionAttributes); + } + + @After + public void tearDown() { + GemFireMockObjectsSupport.destroy(); + } + + @Test + public void putGetRemoveIsSuccessful() { + + assertThat(this.mockRegion).doesNotContainKey(1); + assertThat(this.mockRegion).hasSize(0); + assertThat(this.mockRegion.put(1, "test")).isNull(); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get(1)).isEqualTo("test"); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.remove(1)).isEqualTo("test"); + assertThat(this.mockRegion).doesNotContainKey(1); + assertThat(this.mockRegion).hasSize(0); + } + + @Test + public void putInvalidateGetIsSuccessful() { + + assertThat(this.mockRegion).hasSize(0); + assertThat(this.mockRegion.put(1, "test")); + assertThat(this.mockRegion).hasSize(1); + + this.mockRegion.invalidate(1); + + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get(1)).isNull(); + } + + @Test + public void putGetInvalidateGetPutGetIsSuccessful() { + + assertThat(this.mockRegion).hasSize(0); + assertThat(this.mockRegion.put(1, "test")); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get(1)).isEqualTo("test"); + + this.mockRegion.invalidate(1); + + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get(1)).isNull(); + assertThat(this.mockRegion.put(1, "mock")).isNull(); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get(1)).isEqualTo("mock"); + } + + @Test + public void cacheLoaderLoadsValueOnCacheMiss() { + + assertThat(this.mockRegion).doesNotContainKey("key"); + assertThat(this.mockRegion).hasSize(0); + assertThat(this.mockRegion.get("key")).isNull(); + + AtomicInteger counter = new AtomicInteger(0); + + CacheLoader mockCacheLoader = mock(CacheLoader.class); + + when(mockCacheLoader.load(any(LoaderHelper.class))).thenAnswer(invocation -> counter.incrementAndGet()); + + this.mockRegion.getAttributesMutator().setCacheLoader(mockCacheLoader); + + assertThat(this.mockRegion.get("key")).isEqualTo(1); + assertThat(this.mockRegion.get("key")).isEqualTo(1); + assertThat(this.mockRegion).containsKey("key"); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.remove("key")).isEqualTo(1); + assertThat(this.mockRegion).doesNotContainKey("key"); + assertThat(this.mockRegion).hasSize(0); + assertThat(this.mockRegion.get("key")).isEqualTo(2); + assertThat(this.mockRegion).containsKey("key"); + assertThat(this.mockRegion).hasSize(1); + + this.mockRegion.invalidate("key"); + + assertThat(this.mockRegion).containsKey("key"); + assertThat(this.mockRegion).hasSize(1); + assertThat(this.mockRegion.get("key")).isEqualTo(3); + assertThat(this.mockRegion).containsKey("key"); + assertThat(this.mockRegion).hasSize(1); + + verify(mockCacheLoader, times(3)).load(isA(LoaderHelper.class)); + } + + @Test(expected = EntryNotFoundException.class) + public void invalidateWithNonExistingKeyThrowsException() { + + try { + this.mockRegion.invalidate(1); + } + catch (EntryNotFoundException expected) { + + assertThat(expected).hasMessage("Entry with key [1] not found"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void invalidateFiresCacheListenerAfterInvalidateEntryEvent() { + + assertThat(this.mockRegion.put(1, "test")).isNull(); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion.get(1)).isEqualTo("test"); + + CacheListener mockCacheListener = mock(CacheListener.class); + + doAnswer(invocation -> { + + EntryEvent entryEvent = invocation.getArgument(0); + + assertThat(entryEvent).isNotNull(); + assertThat(entryEvent.getKey()).isEqualTo(1); + assertThat(entryEvent.getNewValue()).isNull(); + assertThat(entryEvent.getOldValue()).isEqualTo("test"); + assertThat(entryEvent.getRegion()).isEqualTo(this.mockRegion); + + return null; + + }).when(mockCacheListener).afterInvalidate(any(EntryEvent.class)); + + this.mockRegion.getAttributesMutator().addCacheListener(mockCacheListener); + this.mockRegion.invalidate(1); + + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion.get(1)).isNull(); + + this.mockRegion.invalidate(1); + + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion.get(1)).isNull(); + + verify(mockCacheListener, times(1)).afterInvalidate(isA(EntryEvent.class)); + verifyNoMoreInteractions(mockCacheListener); + } + + @Test + public void putFiresCacheWriterThenCacheListenersEntryCreateAndUpdateEvents() { + + Answer createEntryEvent = invocation -> { + + EntryEvent entryEvent = invocation.getArgument(0); + + assertThat(entryEvent.getKey()).isEqualTo("key"); + assertThat(entryEvent.getOldValue()).isNull(); + assertThat(entryEvent.getNewValue()).isEqualTo(1); + assertThat(entryEvent.getRegion()).isEqualTo(this.mockRegion); + + return null; + + }; + + Answer updateEntryEvent = invocation -> { + + EntryEvent entryEvent = invocation.getArgument(0); + + assertThat(entryEvent.getKey()).isEqualTo("key"); + assertThat(entryEvent.getOldValue()).isEqualTo(1); + assertThat(entryEvent.getNewValue()).isEqualTo(2); + assertThat(entryEvent.getRegion()).isEqualTo(this.mockRegion); + + return null; + + }; + + CacheListener mockCacheListener = mock(CacheListener.class); + + doAnswer(createEntryEvent).when(mockCacheListener).afterCreate(any(EntryEvent.class)); + doAnswer(updateEntryEvent).when(mockCacheListener).afterUpdate(any(EntryEvent.class)); + + CacheWriter mockCacheWriter = mock(CacheWriter.class); + + doAnswer(createEntryEvent).when(mockCacheWriter).beforeCreate(any(EntryEvent.class)); + doAnswer(updateEntryEvent).when(mockCacheWriter).beforeUpdate(any(EntryEvent.class)); + + this.mockRegion.getAttributesMutator().addCacheListener(mockCacheListener); + this.mockRegion.getAttributesMutator().setCacheWriter(mockCacheWriter); + + assertThat(this.mockRegion.put("key", 1)).isNull(); + assertThat(this.mockRegion.put("key", 2)).isEqualTo(1); + + InOrder ordered = inOrder(this.mockRegion, mockCacheWriter, mockCacheListener); + + ordered.verify(this.mockRegion, times(1)).put(eq("key"), eq(1)); + ordered.verify(mockCacheWriter, times(1)).beforeCreate(isA(EntryEvent.class)); + ordered.verify(mockCacheListener, times(1)).afterCreate(isA(EntryEvent.class)); + ordered.verify(this.mockRegion, times(1)).put(eq("key"), eq(2)); + ordered.verify(mockCacheWriter, times(1)).beforeUpdate(isA(EntryEvent.class)); + ordered.verify(mockCacheListener, times(1)).afterUpdate(isA(EntryEvent.class)); + verifyNoMoreInteractions(mockCacheListener); + verifyNoMoreInteractions(mockCacheWriter); + } + + @Test(expected = IllegalArgumentException.class) + public void putNullValue() { + + try { + this.mockRegion.put(1, null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Value is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = CacheWriterException.class) + public void putStoppedByCacheWriterException() { + + CacheListener mockCacheListener = mock(CacheListener.class); + + CacheWriter mockCacheWriter = mock(CacheWriter.class); + + doThrow(newIllegalStateException("TEST")).when(mockCacheWriter).beforeCreate(any(EntryEvent.class)); + + this.mockRegion.getAttributesMutator().addCacheListener(mockCacheListener); + this.mockRegion.getAttributesMutator().setCacheWriter(mockCacheWriter); + + try { + this.mockRegion.put(1, "test"); + } + catch (CacheWriterException cause) { + + assertThat(cause).hasMessage("Create/Update Error"); + assertThat(cause).hasCauseInstanceOf(IllegalStateException.class); + assertThat(cause.getCause()).hasMessage("TEST"); + assertThat(cause.getCause()).hasNoCause(); + + throw cause; + } + finally { + + assertThat(this.mockRegion).doesNotContainKey(1); + assertThat(this.mockRegion).hasSize(0); + + verify(mockCacheWriter, times(1)).beforeCreate(isA(EntryEvent.class)); + verifyZeroInteractions(mockCacheListener); + } + } + + @Test + public void removeFiresCacheWriterThenCacheListenerEntryDestroyEvent() { + + Answer destroyEntryEvent = invocation -> { + + EntryEvent entryEvent = invocation.getArgument(0); + + assertThat(entryEvent.getKey()).isEqualTo(1); + assertThat(entryEvent.getNewValue()).isNull(); + assertThat(entryEvent.getOldValue()).isEqualTo("test"); + assertThat(entryEvent.getRegion()).isEqualTo(this.mockRegion); + + return null; + + }; + + CacheListener mockCacheListener = mock(CacheListener.class); + + doAnswer(destroyEntryEvent).when(mockCacheListener).afterDestroy(any(EntryEvent.class)); + + CacheWriter mockCacheWriter = mock(CacheWriter.class); + + doAnswer(destroyEntryEvent).when(mockCacheWriter).beforeDestroy(any(EntryEvent.class)); + + assertThat(this.mockRegion.put(1, "test")).isNull(); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + + this.mockRegion.getAttributesMutator().addCacheListener(mockCacheListener); + this.mockRegion.getAttributesMutator().setCacheWriter(mockCacheWriter); + + assertThat(this.mockRegion.remove(1)).isEqualTo("test"); + assertThat(this.mockRegion).doesNotContainKey(1); + assertThat(this.mockRegion).hasSize(0); + + InOrder ordered = inOrder(this.mockRegion, mockCacheWriter, mockCacheListener); + + ordered.verify(this.mockRegion, times(1)).remove(eq(1)); + ordered.verify(mockCacheWriter, times(1)).beforeDestroy(isA(EntryEvent.class)); + ordered.verify(mockCacheListener, times(1)).afterDestroy(isA(EntryEvent.class)); + verifyNoMoreInteractions(mockCacheListener); + verifyNoMoreInteractions(mockCacheWriter); + } + + @Test(expected = CacheWriterException.class) + public void removeStoppedByCacheWriterException() { + + CacheListener mockCacheListener = mock(CacheListener.class); + + CacheWriter mockCacheWriter = mock(CacheWriter.class); + + doThrow(newIllegalStateException("TEST")).when(mockCacheWriter).beforeDestroy(any(EntryEvent.class)); + + assertThat(this.mockRegion.put(1, "test")).isNull(); + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + + this.mockRegion.getAttributesMutator().addCacheListener(mockCacheListener); + this.mockRegion.getAttributesMutator().setCacheWriter(mockCacheWriter); + + try { + this.mockRegion.remove(1); + } + catch (CacheWriterException expected) { + + assertThat(expected).hasMessage("Destroy Error"); + assertThat(expected).hasCauseInstanceOf(IllegalStateException.class); + assertThat(expected.getCause()).hasMessage("TEST"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; + } + finally { + + assertThat(this.mockRegion).containsKey(1); + assertThat(this.mockRegion).hasSize(1); + + verify(mockCacheWriter, times(1)).beforeDestroy(isA(EntryEvent.class)); + verifyZeroInteractions(mockCacheListener); + } + } +}