From feabcc5b76c0ceb7ad642bd12f7f497cf0f63d21 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Fri, 28 May 2021 18:18:01 -0400 Subject: [PATCH] Ajout des tests des configurations --- .gitlab-ci.yml | 4 +- build.gradle.kts | 4 + buildversion | 1 - .../DatabaseVersioning.kt | 3 +- .../model/Configuration.kt | 3 + .../service/ConfigurationService.kt | 43 ++- .../service/ConfigurationServiceTest.kt | 255 ++++++++++++++++++ .../service/MaterialServiceTest.kt | 2 +- .../service/RecipeServiceTest.kt | 2 +- .../service/files/TouchUpKitServiceTest.kt | 2 +- 10 files changed, 289 insertions(+), 30 deletions(-) delete mode 100644 buildversion create mode 100644 src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationServiceTest.kt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 06825b8..3d4493e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ package: ARTIFACT_NAME: "ColorRecipesExplorer-backend-$CI_PIPELINE_IID" script: - docker rm $PACKAGE_CONTAINER_NAME || true - - docker run --name $PACKAGE_CONTAINER_NAME $CI_REGISTRY_IMAGE_GRADLE gradle bootJar + - docker run --name $PACKAGE_CONTAINER_NAME $CI_REGISTRY_IMAGE_GRADLE gradle bootJar -Pversion=$CI_PIPELINE_IID - docker cp $PACKAGE_CONTAINER_NAME:/usr/src/cre/build/libs/ColorRecipesExplorer.jar $ARTIFACT_NAME.jar - docker build -t $CI_REGISTRY_IMAGE_BACKEND --build-arg JDK_VERSION=$JDK_VERSION --build-arg PORT=$PORT --build-arg ARTIFACT_NAME=$ARTIFACT_NAME . - docker push $CI_REGISTRY_IMAGE_BACKEND @@ -81,4 +81,4 @@ deploy: script: - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker stop $DEPLOYED_CONTAINER_NAME || true && docker rm $DEPLOYED_CONTAINER_NAME || true" - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker pull $CI_REGISTRY_IMAGE_BACKEND" - - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker run -d -p $PORT:$PORT --name=$DEPLOYED_CONTAINER_NAME -v $DATA_VOLUME:/usr/bin/cre/data -e spring_profiles_active=$SPRING_PROFILES -e spring_datasource_username=$DB_USERNAME -e spring_datasource_password=$DB_PASSWORD -e spring_datasource_url=$DB_URL -e databaseupdater_username=$DB_UPDATE_USERNAME -e databaseupdater_password=$DB_UPDATE_PASSWORD $CI_REGISTRY_IMAGE_BACKEND" + - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker run -d -p $PORT:$PORT --name=$DEPLOYED_CONTAINER_NAME -v $DATA_VOLUME:/usr/bin/cre/data -e spring_profiles_active=$SPRING_PROFILES -e $CI_REGISTRY_IMAGE_BACKEND" diff --git a/build.gradle.kts b/build.gradle.kts index bd9f8dd..efcc6f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,6 +62,10 @@ dependencies { implementation("org.springframework.cloud:spring-cloud-starter:2.2.8.RELEASE") } +springBoot { + buildInfo() +} + java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 diff --git a/buildversion b/buildversion deleted file mode 100644 index 3a2e3f4..0000000 --- a/buildversion +++ /dev/null @@ -1 +0,0 @@ --1 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt index 9b0411a..ca54fa3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt @@ -14,6 +14,7 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.DependsOn import org.springframework.context.annotation.Profile import org.springframework.core.env.ConfigurableEnvironment +import java.lang.RuntimeException import javax.sql.DataSource const val SUPPORTED_DATABASE_VERSION = 5 @@ -44,7 +45,7 @@ class DataSourceConfiguration { username = databaseUsername password = databasePassword }) - } catch (ex: CreDatabaseException) { + } catch (ex: Exception) { logger.error("Could not access database, restarting in emergency mode...", ex) emergencyMode = true diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Configuration.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Configuration.kt index e98a938..8f971ba 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Configuration.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Configuration.kt @@ -39,6 +39,9 @@ data class ConfigurationEntity( ) { fun toConfiguration() = configuration(key.toConfigurationType(), content, lastUpdated) + + override fun equals(other: Any?) = + other is ConfigurationEntity && key == other.key && content == other.content } data class ConfigurationDto( diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationService.kt index 218946a..d18384d 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationService.kt @@ -8,6 +8,7 @@ import dev.fyloz.colorrecipesexplorer.emergencyMode import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.ConfigurationRepository import dev.fyloz.colorrecipesexplorer.service.files.FileService +import org.springframework.boot.info.BuildProperties import org.springframework.context.annotation.Lazy import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @@ -51,6 +52,7 @@ interface ConfigurationService { const val CONFIGURATION_LOGO_FILE_PATH = "images/logo" const val CONFIGURATION_ICON_FILE_PATH = "images/icon" +const val CONFIGURATION_FORMATTED_LIST_DELIMITER = ';' @Service class ConfigurationServiceImpl( @@ -58,7 +60,8 @@ class ConfigurationServiceImpl( private val fileService: FileService, private val fileConfiguration: FileConfiguration, private val creProperties: CreProperties, - private val databaseProperties: DatabaseUpdaterProperties + private val databaseProperties: DatabaseUpdaterProperties, + private val buildInfo: BuildProperties ) : ConfigurationService { override fun getAll() = ConfigurationType.values().mapNotNull { @@ -70,7 +73,7 @@ class ConfigurationServiceImpl( } override fun getAll(formattedKeyList: String) = - formattedKeyList.split(';').map(this::get) + formattedKeyList.split(CONFIGURATION_FORMATTED_LIST_DELIMITER).map(this::get) override fun get(key: String) = get(key.toConfigurationType()) @@ -131,25 +134,19 @@ class ConfigurationServiceImpl( ConfigurationType.TOUCH_UP_KIT_CACHE_PDF -> "true" else -> "" } -} - -private fun getComputedConfiguration(key: ConfigurationType) = configuration( - key, when (key) { - ConfigurationType.EMERGENCY_MODE_ENABLED -> emergencyMode - ConfigurationType.VERSION -> getAppVersion() - ConfigurationType.DATABASE_SUPPORTED_VERSION -> SUPPORTED_DATABASE_VERSION - ConfigurationType.JAVA_VERSION -> Runtime.version() - ConfigurationType.OPERATING_SYSTEM -> "${System.getProperty("os.name")} ${System.getProperty("os.version")} ${ - System.getProperty( - "os.arch" - ) - }" - else -> throw IllegalArgumentException("Cannot get the value of the configuration with the key ${key.key} because it is not a computed configuration") - }.toString() -) - -const val APP_VERSION_PATH = "buildversion" - -fun getAppVersion(): Int = with(File(APP_VERSION_PATH)) { - this.readLines()[0].toInt() + + private fun getComputedConfiguration(key: ConfigurationType) = configuration( + key, when (key) { + ConfigurationType.EMERGENCY_MODE_ENABLED -> emergencyMode + ConfigurationType.VERSION -> buildInfo.version + ConfigurationType.DATABASE_SUPPORTED_VERSION -> SUPPORTED_DATABASE_VERSION + ConfigurationType.JAVA_VERSION -> Runtime.version() + ConfigurationType.OPERATING_SYSTEM -> "${System.getProperty("os.name")} ${System.getProperty("os.version")} ${ + System.getProperty( + "os.arch" + ) + }" + else -> throw IllegalArgumentException("Cannot get the value of the configuration with the key ${key.key} because it is not a computed configuration") + }.toString() + ) } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationServiceTest.kt new file mode 100644 index 0000000..5275e97 --- /dev/null +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/ConfigurationServiceTest.kt @@ -0,0 +1,255 @@ +package dev.fyloz.colorrecipesexplorer.service + +import dev.fyloz.colorrecipesexplorer.config.FileConfiguration +import dev.fyloz.colorrecipesexplorer.model.* +import dev.fyloz.colorrecipesexplorer.repository.ConfigurationRepository +import io.mockk.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.time.LocalDateTime +import java.util.* +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ConfigurationServiceTest { + private val repository = mockk() + private val fileConfiguration = mockk() + private val service = spyk(ConfigurationServiceImpl(repository, mockk(), fileConfiguration, mockk(), mockk(), mockk())) + + @AfterEach + fun afterEach() { + clearAllMocks() + } + + // getAll() + + @Test + fun `getAll() gets the Configuration of each ConfigurationType`() { + every { service.get(any()) } answers { throw ConfigurationNotSetException(this.args[0] as ConfigurationType) } + + service.getAll() + + verify { + service.getAll() + ConfigurationType.values().forEach { + service.get(it) + } + } + confirmVerified(service) + } + + @Test + fun `getAll() only returns set configurations`() { + val unsetConfigurationTypes = listOf( + ConfigurationType.INSTANCE_NAME, + ConfigurationType.INSTANCE_LOGO_PATH, + ConfigurationType.INSTANCE_ICON_PATH + ) + + every { service.get(match { it in unsetConfigurationTypes }) } answers { + throw ConfigurationNotSetException(this.firstArg() as ConfigurationType) + } + every { service.get(match { it !in unsetConfigurationTypes }) } answers { + val type = firstArg() + configuration(type, type.key) + } + + val found = service.getAll() + + assertFalse { + found.any { + it.type in unsetConfigurationTypes + } + } + + verify { + service.getAll() + ConfigurationType.values().forEach { + service.get(it) + } + } + confirmVerified(service) + } + + @Test + fun `getAll() only includes configurations matching the formatted formatted key list`() { + val configurationTypes = listOf( + ConfigurationType.INSTANCE_NAME, + ConfigurationType.INSTANCE_LOGO_PATH, + ConfigurationType.INSTANCE_ICON_PATH + ) + val formattedKeyList = configurationTypes + .map { it.key } + .reduce { acc, s -> "$acc$CONFIGURATION_FORMATTED_LIST_DELIMITER$s" } + + every { service.get(any()) } answers { + val key = firstArg() + configuration(key.toConfigurationType(), key) + } + + val found = service.getAll(formattedKeyList) + + assertTrue { + found.all { it.type in configurationTypes } + } + + verify { + service.getAll(formattedKeyList) + configurationTypes.forEach { + service.get(it.key) + } + } + confirmVerified(service) + } + + // get() + + @Test + fun `get(key) calls get() with the ConfigurationType matching the given key`() { + val type = ConfigurationType.INSTANCE_ICON_PATH + val key = type.key + + every { service.get(type) } answers { + val type = firstArg() + configuration(type, type.key) + } + + service.get(key) + + verify { + service.get(key) + service.get(type) + } + confirmVerified(service) + } + + @Test + fun `get(type) gets in the repository when the given ConfigurationType is not computed or a file property`() { + val type = ConfigurationType.INSTANCE_ICON_PATH + + every { repository.findById(type.key) } returns Optional.of( + ConfigurationEntity(type.key, type.key, LocalDateTime.now()) + ) + + val configuration = service.get(type) + + assertTrue { + configuration.key == type.key + } + + verify { + service.get(type) + repository.findById(type.key) + } + confirmVerified(service, repository) + } + + @Test + fun `get(type) gets in the FileConfiguration when the gien ConfigurationType is a file property`() { + val type = ConfigurationType.DATABASE_URL + + every { fileConfiguration.get(type) } returns configuration(type, type.key) + + val configuration = service.get(type) + + assertTrue { + configuration.key == type.key + } + + verify { + service.get(type) + fileConfiguration.get(type) + } + verify(exactly = 0) { + repository.findById(type.key) + } + confirmVerified(service, fileConfiguration, repository) + } + + @Test + fun `get(type) computes computed properties`() { + val type = ConfigurationType.JAVA_VERSION + + val configuration = service.get(type) + + assertTrue { + configuration.key == type.key + } + + verify { + service.get(type) + } + verify(exactly = 0) { + repository.findById(type.key) + fileConfiguration.get(type) + } + confirmVerified(service, repository, fileConfiguration) + } + + @Test + fun `get(type) throws ConfigurationNotSetException when the given ConfigurationType has no set configuration`() { + val type = ConfigurationType.INSTANCE_ICON_PATH + + every { repository.findById(type.key) } returns Optional.empty() + + with(assertThrows { service.get(type) }) { + assertEquals(type, this.type) + } + + verify { + service.get(type) + repository.findById(type.key) + } + } + + @Test + fun `set() set the configuration in the FileConfiguration when the given ConfigurationType is a file configuration`() { + val type = ConfigurationType.DATABASE_URL + val content = "url" + + every { fileConfiguration.set(type, content) } just runs + + service.set(type, content) + + verify { + service.set(type, content) + fileConfiguration.set(type, content) + } + confirmVerified(service, fileConfiguration) + } + + @Test + fun `set() set the configuration in the repository when the given ConfigurationType is not a computed configuration of a file configuration`() { + val type = ConfigurationType.INSTANCE_ICON_PATH + val content = "path" + val configuration = configuration(type, content) + val entity = configuration.toEntity() + + every { repository.save(entity) } returns entity + + service.set(type, content) + + verify { + service.set(type, content) + repository.save(entity) + } + confirmVerified(service, repository) + } + + @Test + fun `set() throws CannotSetComputedConfigurationException when the given ConfigurationType is a computed configuration`() { + val type = ConfigurationType.JAVA_VERSION + val content = "5" + + with(assertThrows { service.set(type, content) }) { + assertEquals(type, this.type) + } + + verify { + service.set(type, content) + } + confirmVerified(service) + } +} diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt index 9b3dacb..dd42bf2 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt @@ -23,7 +23,7 @@ class MaterialServiceTest : private val materialTypeService: MaterialTypeService = mock() private val fileService: FileService = mock() override val service: MaterialService = - spy(MaterialServiceImpl(repository, recipeService, mixService, materialTypeService, fileService)) + spy(MaterialServiceImpl(repository, recipeService, mixService, materialTypeService, fileService, mock())) override val entity: Material = material(id = 0L, name = "material") private val entityOutput = materialOutputDto(entity) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt index a8ccd0b..8d4e612 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt @@ -27,7 +27,7 @@ class RecipeServiceTest : private val groupService: GroupService = mock() private val recipeStepService: RecipeStepService = mock() override val service: RecipeService = - spy(RecipeServiceImpl(repository, companyService, mixService, recipeStepService, groupService, mock())) + spy(RecipeServiceImpl(repository, companyService, mixService, recipeStepService, groupService, mock(), mock())) private val company: Company = company(id = 0L) override val entity: Recipe = recipe(id = 0L, name = "recipe", company = company) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt index 3813fd5..227cbf8 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt @@ -21,7 +21,7 @@ private class TouchUpKitServiceTestContext { val creProperties = mockk { every { cacheGeneratedFiles } returns false } - val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, touchUpKitRepository, creProperties)) + val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, mockk(), touchUpKitRepository)) val pdfDocumentData = mockk() val pdfDocument = mockk { mockkStatic(PdfDocument::toByteArrayResource)