diff --git a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java new file mode 100644 index 0000000000..5ad209a3af --- /dev/null +++ b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java @@ -0,0 +1,22 @@ +package greencity.config.dotenv; + +import greencity.constant.AppConstant; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class DotEnvConditionChecker { + private static Boolean cachedValue = null; + + public static boolean isEnabled() { + if (cachedValue == null) { + try { + cachedValue = Files + .exists(Paths.get(System.getProperty("user.dir") + File.separator + AppConstant.DOTENV_FILENAME)); + } catch (SecurityException e) { + cachedValue = false; + } + } + return cachedValue; + } +} diff --git a/service/src/main/java/greencity/config/DotenvConfig.java b/service/src/main/java/greencity/config/dotenv/DotenvConfig.java similarity index 78% rename from service/src/main/java/greencity/config/DotenvConfig.java rename to service/src/main/java/greencity/config/dotenv/DotenvConfig.java index 660495c512..b728ce3377 100644 --- a/service/src/main/java/greencity/config/DotenvConfig.java +++ b/service/src/main/java/greencity/config/dotenv/DotenvConfig.java @@ -1,25 +1,29 @@ -package greencity.config; +package greencity.config.dotenv; import greencity.constant.AppConstant; import greencity.constant.ErrorMessage; import greencity.exception.exceptions.FunctionalityNotAvailableException; import io.github.cdimascio.dotenv.Dotenv; import io.github.cdimascio.dotenv.DotenvException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; @Configuration @Lazy public class DotenvConfig { @Bean + @ConditionalOnMissingBean(Dotenv.class) public Dotenv fallbackDotenv() { return Dotenv.configure().ignoreIfMissing().load(); } @Bean - @ConditionalOnMissingBean(Dotenv.class) + @ConditionalOnExpression("#{T(greencity.config.dotenv.DotEnvConditionChecker).isEnabled()}") + @Primary Dotenv dotenv() { try { return Dotenv.configure() diff --git a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java new file mode 100644 index 0000000000..154676e3d0 --- /dev/null +++ b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java @@ -0,0 +1,90 @@ +package greencity.utils.dotenv; + +import greencity.config.dotenv.DotEnvConditionChecker; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; + +class DotEnvConditionCheckerTest { + private static MockedStatic mockedPaths; + private static MockedStatic mockedFiles; + + @BeforeEach + void setUp() throws NoSuchFieldException, IllegalAccessException { + Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); + cachedValueField.setAccessible(true); + cachedValueField.set(null, null); + mockedPaths.clearInvocations(); + mockedFiles.clearInvocations(); + } + + @BeforeAll + public static void init() { + mockedFiles = Mockito.mockStatic(Files.class); + mockedPaths = Mockito.mockStatic(Paths.class); + } + + @AfterAll + public static void cleanup() { + mockedPaths.close(); + mockedFiles.close(); + } + + @Test + public void checkIsEnabledReturnsTrueIfPathIsValid() { + Path paths = Mockito.mock(Path.class); + + mockedPaths.when(() -> Paths.get(anyString())).thenReturn(paths); + mockedFiles.when(() -> Files.exists(paths)).thenReturn(true); + + assertTrue(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(paths), times(1)); + + } + + @Test + public void checkIsEnabledReturnsFalseIfPathNotExistsTest() { + Path paths = Mockito.mock(Path.class); + + mockedPaths.when(() -> Paths.get(anyString())).thenReturn(paths); + mockedFiles.when(() -> Files.exists(paths)).thenReturn(false); + + assertFalse(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(paths), times(1)); + } + + @Test + public void checkIsFalseIfPathIsProtectedTest() { + mockedPaths.when(() -> Paths.get(anyString())).thenThrow(SecurityException.class); + + assertFalse(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(any(Path.class)), times(0)); + } + + @Test + public void checkIsNoInvocationIfAlreadyCalculatedTest() throws NoSuchFieldException, IllegalAccessException { + Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); + cachedValueField.setAccessible(true); + cachedValueField.set(null, false); + + assertFalse(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(0)); + mockedFiles.verify(() -> Files.exists(any(Path.class)), times(0)); + } +}