diff --git a/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java b/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java new file mode 100644 index 00000000..457b95af --- /dev/null +++ b/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java @@ -0,0 +1,272 @@ +/* + * Copyright 2020 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.geode.data; + +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.geode.cache.Region; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.data.gemfire.util.ArrayUtils; +import org.springframework.data.gemfire.util.CollectionUtils; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Abstract base class implementing the {@link CacheDataExporter} and {@link CacheDataImporter} interface in order to + * simply import/export data operation implementations in a consistent way. + * + * @author John Blum + * @see org.apache.geode.cache.Region + * @see org.springframework.context.ApplicationContext + * @see org.springframework.context.ApplicationContextAware + * @see org.springframework.context.EnvironmentAware + * @see org.springframework.core.env.Environment + * @see org.springframework.geode.data.CacheDataExporter + * @see org.springframework.geode.data.CacheDataImporter + * @since 1.3.0 + */ +@SuppressWarnings({ "rawtypes", "unused" }) +public abstract class AbstractCacheDataImporterExporter + implements ApplicationContextAware, CacheDataImporterExporter, EnvironmentAware { + + protected static final boolean DEFAULT_CACHE_DATA_EXPORT_ENABLED = false; + + protected static final String CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME = + "spring.boot.data.gemfire.cache.data.export.enabled"; + + protected static final String CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME = + "spring.boot.data.gemfire.cache.data.import.active-profiles"; + + protected static final String DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES = ""; + + private static final String RESERVED_DEFAULT_PROFILE_NAME = "default"; + + private ApplicationContext applicationContext; + + private Environment environment; + + /** + * Sets a reference to a {@link ApplicationContext} used by this data importer/exporter to perform its function. + * + * @param applicationContext {@link ApplicationContext} used by this data importer/exporter. + * @see org.springframework.context.ApplicationContext + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + + } + + /** + * Return an {@link Optional} reference to the configured {@link ApplicationContext} used by + * this data importer/exporter to perform its function. + * + * @return an {@link Optional} reference to the configured {@link ApplicationContext} used by + * this data importer/exporter. + * @see org.springframework.context.ApplicationContext + * @see java.util.Optional + */ + protected Optional getApplicationContext() { + return Optional.ofNullable(this.applicationContext); + } + + /** + * Returns a required reference to the configured {@link ApplicationContext} used by this data importer/exporter. + * + * @return a required reference to the configured {@link ApplicationContext} used by this data importer/exporter. + * @throws IllegalStateException if an {@link ApplicationContext} was not configured + * ({@link #setApplicationContext(ApplicationContext)} set). + * @see org.springframework.context.ApplicationContext + * @see #getApplicationContext() + */ + protected ApplicationContext requireApplicationContext() { + + return getApplicationContext() + .orElseThrow(() -> newIllegalStateException("ApplicationContext was not configured")); + } + + /** + * Sets a reference to the configured {@link Environment} used by this data importer/exporter + * to perform its function. + * + * @param environment reference to the configured {@link Environment}. + * @see org.springframework.core.env.Environment + */ + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + /** + * Returns an {@link Optional} reference to the configured {@link Environment} used by this data importer/exporter + * to access {@link Environment} specific configuration. + * + * @return an {@link Optional} reference to the configured {@link Environment} used by this data importer/exporter + * to access {@link Environment} specific configuration. + * @see org.springframework.core.env.Environment + * @see java.util.Optional + */ + protected Optional getEnvironment() { + return Optional.ofNullable(this.environment); + } + + /** + * Returns a required reference to the configured {@link Environment} used by this data importer/exporter + * to access {@link Environment} specific configuration. + * + * @return a required reference to the configured {@link Environment}. + * @throws IllegalStateException if the {@link Environment} was not configured + * ({@link #setEnvironment(Environment) set}). + * @see org.springframework.core.env.Environment + * @see #getEnvironment() + */ + protected Environment requireEnvironment() { + + return getEnvironment() + .orElseThrow(() -> newIllegalStateException("Environment was not configured")); + } + + /** + * Exports data contained in the given {@link Region}. + * + * @param region {@link Region} to export data from. + * @return the given {@link Region}. + * @see org.apache.geode.cache.Region + */ + @NonNull @Override + public Region exportFrom(@NonNull Region region) { + + Assert.notNull(region, "Region must not be null"); + + boolean exportEnabled = getEnvironment() + .filter(environment -> environment.getProperty(CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME, Boolean.class, + DEFAULT_CACHE_DATA_EXPORT_ENABLED)) + .isPresent(); + + return exportEnabled ? doExportFrom(region) : region; + } + + /** + * Exports data contained in the given {@link Region}. + * + * @param region {@link Region} to export data from. + * @return the given {@link Region}. + * @see org.apache.geode.cache.Region + */ + protected abstract @NonNull Region doExportFrom(@NonNull Region region); + + /** + * Imports data into the given {@link Region}. + * + * @param region {@link Region} to import data into. + * @return the given {@link Region}. + * @see org.apache.geode.cache.Region + */ + @NonNull @Override + public Region importInto(@NonNull Region region) { + + Assert.notNull(region, "Region must not be null"); + + boolean importEnabled = getEnvironment() + .map(Environment::getActiveProfiles) + .map(CollectionUtils::asSet) + .map(this::getDefaultProfilesIfEmpty) + .filter(activeProfiles -> { + + String cacheDataImportActiveProfiles = requireEnvironment() + .getProperty(CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME, + DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES); + + return isImportEnabled(activeProfiles, cacheDataImportActiveProfiles); + + }) + .isPresent(); + + return importEnabled ? doImportInto(region) : region; + } + + @Nullable Set getDefaultProfilesIfEmpty(@Nullable Set activeProfiles) { + + Set resolvedProfiles = activeProfiles; + + if (CollectionUtils.nullSafeSet(activeProfiles).isEmpty()) { + + Set defaultProfiles = + CollectionUtils.asSet(ArrayUtils.nullSafeArray(requireEnvironment().getDefaultProfiles(), String.class)); + + if (isNonDefaultProfileSet(defaultProfiles)) { + resolvedProfiles = defaultProfiles; + } + } + + return resolvedProfiles; + } + + // The Set of Profiles cannot be null, empty or contain only the "default" Profile. + boolean isNonDefaultProfileSet(@Nullable Set profiles) { + + return Objects.nonNull(profiles) + && !profiles.isEmpty() + && !Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME).containsAll(profiles); + } + + // Active Spring Profiles must contain at least 1 of the configured cacheDataImportActiveProfiles unless unset. + boolean isImportEnabled(Set activeProfiles, String cacheDataImportActiveProfiles) { + return isNotSet(cacheDataImportActiveProfiles) + || containsAny(activeProfiles, commaDelimitedListOfStringsToSet(cacheDataImportActiveProfiles)); + } + + Set commaDelimitedListOfStringsToSet(@NonNull String commaDelimitedListOfStrings) { + + return StringUtils.hasText(commaDelimitedListOfStrings) + ? Arrays.stream(commaDelimitedListOfStrings.split(",")) + .map(String::trim) + .collect(Collectors.toSet()) + : Collections.emptySet(); + } + + boolean containsAny(Collection source, Collection elements) { + return CollectionUtils.containsAny(source, elements); + } + + boolean isNotSet(String value) { + return !StringUtils.hasText((value)); + } + + /** + * Imports data into the given {@link Region}. + * + * @param region {@link Region} to import data into. + * @return the given {@link Region}. + * @see org.apache.geode.cache.Region + */ + protected abstract @NonNull Region doImportInto(@NonNull Region region); + +} diff --git a/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java b/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java new file mode 100644 index 00000000..d587c451 --- /dev/null +++ b/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java @@ -0,0 +1,534 @@ +/* + * Copyright 2020 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.geode.data; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import org.junit.Test; + +import org.apache.geode.cache.Region; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +/** + * Unit Tests for {@link AbstractCacheDataImporterExporter}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.apache.geode.cache.Region + * @see org.springframework.context.ApplicationContext + * @see org.springframework.core.env.Environment + * @see org.springframework.geode.data.AbstractCacheDataImporterExporter + * @since 1.3.0 + */ +public class AbstractCacheDataImporterExporterUnitTests { + + @Test + public void setAndGetApplicationContext() { + + ApplicationContext mockApplicationContext = mock(ApplicationContext.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).setApplicationContext(any()); + doCallRealMethod().when(importerExporter).getApplicationContext(); + doCallRealMethod().when(importerExporter).requireApplicationContext(); + + assertThat(importerExporter.getApplicationContext().orElse(null)).isNull(); + + importerExporter.setApplicationContext(mockApplicationContext); + + assertThat(importerExporter.getApplicationContext().orElse(null)).isEqualTo(mockApplicationContext); + assertThat(importerExporter.requireApplicationContext()).isEqualTo(mockApplicationContext); + + importerExporter.setApplicationContext(null); + + assertThat(importerExporter.getApplicationContext().orElse(null)).isNull(); + } + + @Test(expected = IllegalStateException.class) + public void requireApplicationContextWhenAnApplicationContextIsNotConfiguredThrowsIllegalStateException() { + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).requireApplicationContext(); + + try { + importerExporter.requireApplicationContext(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("ApplicationContext was not configured"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void setAndGetEnvironment() { + + Environment mockEnvironment = mock(Environment.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).setEnvironment(any()); + doCallRealMethod().when(importerExporter).getEnvironment(); + doCallRealMethod().when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.getEnvironment().orElse(null)).isNull(); + + importerExporter.setEnvironment(mockEnvironment); + + assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment); + assertThat(importerExporter.requireEnvironment()).isEqualTo(mockEnvironment); + + importerExporter.setEnvironment(null); + + assertThat(importerExporter.getEnvironment().orElse(null)).isNull(); + } + + @Test(expected = IllegalStateException.class) + public void requireEnvironmentWhenAnEnvironmentWasNotConfiguredThrowsIllegalStateException() { + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).requireEnvironment(); + + try { + importerExporter.requireEnvironment(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("Environment was not configured"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + @SuppressWarnings("unchecked") + public void exportFromWhenEnvironmentIsPresentAndPropertyIsTrue() { + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), + eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED))) + .thenReturn(true); + + Region mockRegion = mock(Region.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).exportFrom(any()); + doReturn(mockRegion).when(importerExporter).doExportFrom(eq(mockRegion)); + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + + assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), + eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED)); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).doExportFrom(eq(mockRegion)); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void exportFromWhenEnvironmentIsNotPresentWillNotCallDoExportFrom() { + + Region mockRegion = mock(Region.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).exportFrom(any()); + doReturn(Optional.empty()).when(importerExporter).getEnvironment(); + + assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, never()).doExportFrom(any(Region.class)); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void exportFromWhenEnvironmentPropertyIsNotSetWillNotCallDoExportFrom() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), + eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED))) + .thenReturn(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).exportFrom(any()); + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + + assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), + eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED)); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, never()).doExportFrom(any(Region.class)); + verifyNoInteractions(mockRegion); + } + + @Test(expected = IllegalArgumentException.class) + public void exportFromNullRegionThrowsIllegalArgumentException() { + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).exportFrom(any()); + + try { + importerExporter.exportFrom(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Region must not be null"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(importerExporter, never()).getEnvironment(); + verify(importerExporter, never()).doExportFrom(any(Region.class)); + } + } + + @SuppressWarnings("unchecked") + private AbstractCacheDataImporterExporter callRealMethodsFor(AbstractCacheDataImporterExporter importerExporter) { + + doCallRealMethod().when(importerExporter).importInto(any()); + doCallRealMethod().when(importerExporter).getDefaultProfilesIfEmpty(any(Set.class)); + doCallRealMethod().when(importerExporter).isNonDefaultProfileSet(any(Set.class)); + doCallRealMethod().when(importerExporter).isImportEnabled(any(Set.class), anyString()); + doCallRealMethod().when(importerExporter).commaDelimitedListOfStringsToSet(anyString()); + doCallRealMethod().when(importerExporter).containsAny(any(Collection.class), any(Collection.class)); + doCallRealMethod().when(importerExporter).isNotSet(anyString()); + + return importerExporter; + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentIsPresentAndActiveProfilesPropertiesAreSetAndMatch() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(new String[] { "TEST" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))) + .thenReturn(" DEV , TEST"); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(mockRegion).when(importerExporter).doImportInto(eq(mockRegion)); + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(mockEnvironment, times(1)).getActiveProfiles(); + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).requireEnvironment(); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("TEST")), eq(" DEV , TEST")); + verify(importerExporter, times(1)).doImportInto(eq(mockRegion)); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentIsNullWillNotCallDoImportInto() { + + Region mockRegion = mock(Region.class); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, never()).requireEnvironment(); + verify(importerExporter, never()).isImportEnabled(any(Set.class), anyString()); + verify(importerExporter, never()).doImportInto(eq(mockRegion)); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentActiveProfilesIsNullWillNotCallDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, never()).requireEnvironment(); + verify(importerExporter, never()).isImportEnabled(any(Set.class), anyString()); + verify(importerExporter, never()).doImportInto(eq(mockRegion)); + verify(mockEnvironment, times(1)).getActiveProfiles(); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentActiveProfilesDoesNotContainImportActiveProfilesWillNotCallDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(new String[0]).thenReturn(new String[] { "PROD" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))).thenReturn("DEV,TEST"); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(2)).getEnvironment(); + verify(importerExporter, times(3)).requireEnvironment(); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.emptySet()), eq("DEV,TEST")); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("PROD")), eq("DEV,TEST")); + verify(importerExporter, never()).doImportInto(eq(mockRegion)); + verify(mockEnvironment, times(2)).getActiveProfiles(); + verify(mockEnvironment, times(1)).getDefaultProfiles(); + verify(mockEnvironment, times(2)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenImportActiveProfilesPropertyIsNotSetCallsDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(new String[] { "PROD" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))) + .thenReturn(" "); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(mockRegion).when(importerExporter).doImportInto(eq(mockRegion)); + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).requireEnvironment(); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("PROD")), eq(" ")); + verify(importerExporter, times(1)).doImportInto(eq(mockRegion)); + verify(mockEnvironment, times(1)).getActiveProfiles(); + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentDefaultProfilesDoesNotContainImportActiveProfilesWillNotCallDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(new String[0]); + when(mockEnvironment.getDefaultProfiles()).thenReturn(new String[] { "PROD" }).thenReturn(new String[] { "default" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))).thenReturn("DEV,TEST"); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(2)).getEnvironment(); + verify(importerExporter, times(4)).requireEnvironment(); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("PROD")), eq("DEV,TEST")); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.emptySet()), eq("DEV,TEST")); + verify(importerExporter, never()).doImportInto(any(Region.class)); + verify(mockEnvironment, times(2)).getActiveProfiles(); + verify(mockEnvironment, times(2)).getDefaultProfiles(); + verify(mockEnvironment, times(2)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentDefaultProfilesAndActiveProfilesConflictWillNotCallDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(null).thenReturn(new String[] { "PROD" }); + when(mockEnvironment.getDefaultProfiles()).thenReturn(new String[] { "DEV", "TEST" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))).thenReturn("DEV,TEST"); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(2)).getEnvironment(); + verify(importerExporter, times(1)).requireEnvironment(); + verify(importerExporter, times(1)) + .getDefaultProfilesIfEmpty(Collections.singleton("PROD")); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("PROD")), eq("DEV,TEST")); + verify(importerExporter, never()).doImportInto(any(Region.class)); + verify(mockEnvironment, times(2)).getActiveProfiles(); + verify(mockEnvironment, never()).getDefaultProfiles(); + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenEnvironmentActiveProfilesNotSetDefaultProfilesContainImportActiveProfilesCallsDoImportInto() { + + Region mockRegion = mock(Region.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockEnvironment.getActiveProfiles()).thenReturn(new String[0]); + when(mockEnvironment.getDefaultProfiles()).thenReturn(new String[] { "TEST" }); + when(mockEnvironment.getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES))).thenReturn("DEV,TEST"); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(mockRegion).when(importerExporter).doImportInto(eq(mockRegion)); + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(2)).requireEnvironment(); + verify(importerExporter, times(1)) + .isImportEnabled(eq(Collections.singleton("TEST")), eq("DEV,TEST")); + verify(importerExporter, times(1)).doImportInto(eq(mockRegion)); + verify(mockEnvironment, times(1)).getActiveProfiles(); + verify(mockEnvironment, times(1)).getDefaultProfiles(); + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verifyNoMoreInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test(expected = IllegalArgumentException.class) + public void importIntoNullRegionThrowsIllegalArgumentException() { + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).importInto(any()); + + try { + importerExporter.importInto(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Region must not be null"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(importerExporter, never()).getEnvironment(); + verify(importerExporter, never()).doImportInto(any(Region.class)); + } + } +}