From 3cb78ce44db2d809032f5ca005d462e766943a66 Mon Sep 17 00:00:00 2001 From: Alexander Buntakov Date: Thu, 10 Jun 2021 19:48:07 +0300 Subject: [PATCH] add settings module --- README.md | 4 + build.gradle.kts | 2 +- settings-spring-jpa/build.gradle.kts | 32 ++++ .../settings/annotations/SettingMapper.kt | 8 + .../configurations/SettingsConfiguration.kt | 25 ++++ .../SettingsDatabaseConfiguration.kt | 30 ++++ .../ru/touchin/settings/dto/SystemSetting.kt | 6 + .../CannotParseSettingValueException.kt | 8 + .../exceptions/SettingNotFoundException.kt | 7 + .../settings/models/AbstractSettingModel.kt | 15 ++ .../settings/models/SystemSettingModel.kt | 9 ++ .../repositories/SystemSettingsRepository.kt | 13 ++ .../services/SystemSettingsService.kt | 15 ++ .../services/SystemSettingsServiceImpl.kt | 79 ++++++++++ .../src/main/resources/schema-postgresql.sql | 1 + .../db/changelog/db.changelog-master.yaml | 40 +++++ .../settings/SettingsSlowTestConfiguration.kt | 18 +++ .../settings/SettingsTestApplication.kt | 13 ++ .../SystemSettingRepositoryTest.kt | 53 +++++++ ...mSettingsServiceImplDeserializationTest.kt | 137 ++++++++++++++++++ ...temSettingsServiceImplSerializationTest.kt | 121 ++++++++++++++++ .../test/resources/application-test-slow.yml | 3 + .../src/test/resources/application-test.yml | 3 + .../db/changelog/db.changelog-master.yaml | 4 + settings.gradle.kts | 1 + 25 files changed, 646 insertions(+), 1 deletion(-) create mode 100644 settings-spring-jpa/build.gradle.kts create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/annotations/SettingMapper.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsConfiguration.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsDatabaseConfiguration.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/dto/SystemSetting.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/CannotParseSettingValueException.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/SettingNotFoundException.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/AbstractSettingModel.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/SystemSettingModel.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/repositories/SystemSettingsRepository.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsService.kt create mode 100644 settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsServiceImpl.kt create mode 100644 settings-spring-jpa/src/main/resources/schema-postgresql.sql create mode 100644 settings-spring-jpa/src/main/resources/settings/db/changelog/db.changelog-master.yaml create mode 100644 settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsSlowTestConfiguration.kt create mode 100644 settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsTestApplication.kt create mode 100644 settings-spring-jpa/src/test/kotlin/ru/touchin/settings/repositories/SystemSettingRepositoryTest.kt create mode 100644 settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplDeserializationTest.kt create mode 100644 settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplSerializationTest.kt create mode 100644 settings-spring-jpa/src/test/resources/application-test-slow.yml create mode 100644 settings-spring-jpa/src/test/resources/application-test.yml create mode 100644 settings-spring-jpa/src/test/resources/db/changelog/db.changelog-master.yaml diff --git a/README.md b/README.md index 74268b9..3355d21 100644 --- a/README.md +++ b/README.md @@ -106,3 +106,7 @@ Interceptor для логирования запросов/ответов. ## common-geo-spatial4j-spring Реализация интерфейса `GeoCalculator` с помощью библиотеки `spatial4j` + +## settings-spring-jpa + +Модуль для хранения настроек diff --git a/build.gradle.kts b/build.gradle.kts index 6d5a0bd..14bc81b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,7 +58,7 @@ subprojects { dependency("org.junit.jupiter:junit-jupiter-engine:5.4.2") dependency("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") - dependency("org.mockito:mockito-inline:2.13.0") + dependency("org.mockito:mockito-inline:3.11.0") dependency("com.github.zafarkhaja:java-semver:0.9.0") diff --git a/settings-spring-jpa/build.gradle.kts b/settings-spring-jpa/build.gradle.kts new file mode 100644 index 0000000..df1f34c --- /dev/null +++ b/settings-spring-jpa/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation(project(":common")) + implementation(project(":common-spring")) + implementation(project(":common-spring-jpa")) + + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.liquibase:liquibase-core") + + testImplementation(project(":common-spring-test-jpa")) + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + testImplementation("org.testcontainers:testcontainers") + testImplementation("org.testcontainers:postgresql") + testImplementation("org.testcontainers:junit-jupiter") + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.junit.jupiter:junit-jupiter-params") + + testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin") + + testRuntimeOnly("org.postgresql:postgresql") +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/annotations/SettingMapper.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/annotations/SettingMapper.kt new file mode 100644 index 0000000..eb000bc --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/annotations/SettingMapper.kt @@ -0,0 +1,8 @@ +package ru.touchin.settings.annotations + +import org.springframework.beans.factory.annotation.Qualifier + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) +annotation class SettingMapper diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsConfiguration.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsConfiguration.kt new file mode 100644 index 0000000..ab17c03 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsConfiguration.kt @@ -0,0 +1,25 @@ +package ru.touchin.settings.configurations + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import ru.touchin.settings.annotations.SettingMapper + +@Configuration +@ComponentScan( + "ru.touchin.settings.services" +) +class SettingsConfiguration { + + @Bean + @SettingMapper + fun getSettingMapper(): ObjectMapper { + return ObjectMapper() + .registerModule(KotlinModule()) + .setSerializationInclusion(JsonInclude.Include.ALWAYS) + } + +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsDatabaseConfiguration.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsDatabaseConfiguration.kt new file mode 100644 index 0000000..8b204b9 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/configurations/SettingsDatabaseConfiguration.kt @@ -0,0 +1,30 @@ +package ru.touchin.settings.configurations + +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import ru.touchin.common.spring.jpa.EnableJpaAuditingExtra +import ru.touchin.common.spring.jpa.liquibase.LiquibaseParams + +@Configuration +@ComponentScan("ru.touchin.common.spring.jpa.liquibase") +@EntityScan("ru.touchin.settings.models") +@EnableJpaRepositories(basePackages = ["ru.touchin.settings.repositories"]) +@EnableJpaAuditingExtra +class SettingsDatabaseConfiguration { + + companion object { + const val SCHEMA: String = "settings" + } + + @Bean + fun liquibaseParams(): LiquibaseParams { + return LiquibaseParams( + schema = SCHEMA, + changeLogPath = "settings/db/changelog/db.changelog-master.yaml", + ) + } + +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/dto/SystemSetting.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/dto/SystemSetting.kt new file mode 100644 index 0000000..7931479 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/dto/SystemSetting.kt @@ -0,0 +1,6 @@ +package ru.touchin.settings.dto + +data class SystemSetting( + val key: String, + val value: T, +) diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/CannotParseSettingValueException.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/CannotParseSettingValueException.kt new file mode 100644 index 0000000..80db7f6 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/CannotParseSettingValueException.kt @@ -0,0 +1,8 @@ +package ru.touchin.settings.exceptions + +import ru.touchin.common.exceptions.CommonException + +class CannotParseSettingValueException(value: String, clazz: Class<*>, exception: Throwable) : CommonException( + "Cannot parse setting value: $value to ${clazz.simpleName}", + exception, +) diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/SettingNotFoundException.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/SettingNotFoundException.kt new file mode 100644 index 0000000..d9a474b --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/exceptions/SettingNotFoundException.kt @@ -0,0 +1,7 @@ +package ru.touchin.settings.exceptions + +import ru.touchin.common.exceptions.CommonNotFoundException + +class SettingNotFoundException(key: String) : CommonNotFoundException( + "Setting not found key=$key" +) diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/AbstractSettingModel.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/AbstractSettingModel.kt new file mode 100644 index 0000000..ba5d2c6 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/AbstractSettingModel.kt @@ -0,0 +1,15 @@ +package ru.touchin.settings.models + +import ru.touchin.common.spring.jpa.models.AuditableEntity +import javax.persistence.Id +import javax.persistence.MappedSuperclass + +@MappedSuperclass +open class AbstractSettingModel: AuditableEntity() { + + @Id + lateinit var key: String + + lateinit var value: String + +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/SystemSettingModel.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/SystemSettingModel.kt new file mode 100644 index 0000000..1c21155 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/models/SystemSettingModel.kt @@ -0,0 +1,9 @@ +package ru.touchin.settings.models + +import ru.touchin.settings.configurations.SettingsDatabaseConfiguration.Companion.SCHEMA +import javax.persistence.Entity +import javax.persistence.Table + +@Entity +@Table(name = "system_settings", schema = SCHEMA) +class SystemSettingModel : AbstractSettingModel() diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/repositories/SystemSettingsRepository.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/repositories/SystemSettingsRepository.kt new file mode 100644 index 0000000..d72b572 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/repositories/SystemSettingsRepository.kt @@ -0,0 +1,13 @@ +package ru.touchin.settings.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.repository.findByIdOrNull +import ru.touchin.settings.exceptions.SettingNotFoundException +import ru.touchin.settings.models.SystemSettingModel + +interface SystemSettingsRepository: JpaRepository + +fun SystemSettingsRepository.findByIdOrThrow(key: String): SystemSettingModel { + return findByIdOrNull(key) + ?: throw SettingNotFoundException(key) +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsService.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsService.kt new file mode 100644 index 0000000..c8528c4 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsService.kt @@ -0,0 +1,15 @@ +package ru.touchin.settings.services + +import ru.touchin.settings.dto.SystemSetting + +interface SystemSettingsService { + + fun save(setting: SystemSetting): SystemSetting + + fun getOrNull(settingKey: String, clazz: Class): SystemSetting? + + fun get(settingKey: String, clazz: Class): SystemSetting + + fun delete(settingKey: String) + +} diff --git a/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsServiceImpl.kt b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsServiceImpl.kt new file mode 100644 index 0000000..30d7969 --- /dev/null +++ b/settings-spring-jpa/src/main/kotlin/ru/touchin/settings/services/SystemSettingsServiceImpl.kt @@ -0,0 +1,79 @@ +package ru.touchin.settings.services + +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.databind.JsonMappingException +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.settings.annotations.SettingMapper +import ru.touchin.settings.dto.SystemSetting +import ru.touchin.settings.exceptions.CannotParseSettingValueException +import ru.touchin.settings.models.SystemSettingModel +import ru.touchin.settings.repositories.SystemSettingsRepository +import ru.touchin.settings.repositories.findByIdOrThrow + +@Service +class SystemSettingsServiceImpl( + private val systemSettingsRepository: SystemSettingsRepository, + @SettingMapper + private val settingsObjectMapper: ObjectMapper, +) : SystemSettingsService { + + @Transactional + override fun save(setting: SystemSetting): SystemSetting { + val settingModel = systemSettingsRepository.findByIdOrNull(setting.key) + ?: SystemSettingModel().apply { + key = setting.key + } + + settingModel.value = settingsObjectMapper.writeValueAsString(setting.value) + + systemSettingsRepository.save(settingModel) + + return setting + } + + private fun createSetting(model: SystemSettingModel, clazz: Class): SystemSetting { + val value = kotlin + .runCatching { + settingsObjectMapper.readValue(model.value, clazz) + } + .recoverCatching { exception -> + when(exception) { + is JsonProcessingException, + is JsonMappingException -> { + throw CannotParseSettingValueException(model.value, clazz, exception) + } + + else -> { + throw exception + } + } + } + .getOrThrow() + + return SystemSetting( + key = model.key, + value = value, + ) + } + + @Transactional(readOnly = true) + override fun getOrNull(settingKey: String, clazz: Class): SystemSetting? { + return systemSettingsRepository.findByIdOrNull(settingKey) + ?.let { createSetting(it, clazz) } + } + + @Transactional(readOnly = true) + override fun get(settingKey: String, clazz: Class): SystemSetting { + return createSetting(systemSettingsRepository.findByIdOrThrow(settingKey), clazz) + } + + @Transactional + override fun delete(settingKey: String) { + return systemSettingsRepository.deleteById(settingKey) + } + +} + diff --git a/settings-spring-jpa/src/main/resources/schema-postgresql.sql b/settings-spring-jpa/src/main/resources/schema-postgresql.sql new file mode 100644 index 0000000..066a439 --- /dev/null +++ b/settings-spring-jpa/src/main/resources/schema-postgresql.sql @@ -0,0 +1 @@ +CREATE SCHEMA IF NOT EXISTS settings; diff --git a/settings-spring-jpa/src/main/resources/settings/db/changelog/db.changelog-master.yaml b/settings-spring-jpa/src/main/resources/settings/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..6826945 --- /dev/null +++ b/settings-spring-jpa/src/main/resources/settings/db/changelog/db.changelog-master.yaml @@ -0,0 +1,40 @@ +databaseChangeLog: + - changeSet: + id: create_table__system_settings + author: touchin + preConditions: + - onFail: MARK_RAN + not: + tableExists: + tableName: system_settings + changes: + - createTable: + tableName: system_settings + columns: + - column: + name: key + type: VARCHAR(128) + constraints: + nullable: false + primaryKey: true + primaryKeyName: PK_SYSTEM_SETTINGS + - column: + name: value + type: VARCHAR(1024) + constraints: + nullable: false + - column: + name: created_at + type: TIMESTAMP WITH TIME ZONE + defaultValueDate: CURRENT_TIMESTAMP + constraints: + nullable: false + - column: + name: updated_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: created_by + type: VARCHAR(255) + - column: + name: updated_by + type: VARCHAR(255) diff --git a/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsSlowTestConfiguration.kt b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsSlowTestConfiguration.kt new file mode 100644 index 0000000..fe74743 --- /dev/null +++ b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsSlowTestConfiguration.kt @@ -0,0 +1,18 @@ +package ru.touchin.settings + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Import +import org.springframework.context.annotation.Profile +import ru.touchin.settings.configurations.SettingsDatabaseConfiguration + +@Profile("test-slow") +@EnableAutoConfiguration +@TestConfiguration +@ComponentScan +@Import(SettingsDatabaseConfiguration::class) +class SettingsSlowTestConfiguration { + + +} diff --git a/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsTestApplication.kt b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsTestApplication.kt new file mode 100644 index 0000000..6a6fcbb --- /dev/null +++ b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/SettingsTestApplication.kt @@ -0,0 +1,13 @@ +package ru.touchin.settings + +import org.springframework.boot.SpringBootConfiguration +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Import +import org.springframework.test.context.ContextConfiguration +import ru.touchin.settings.configurations.SettingsConfiguration + +@SpringBootConfiguration +@ContextConfiguration(classes = [SettingsConfiguration::class]) +@TestConfiguration +@Import(SettingsConfiguration::class, SettingsSlowTestConfiguration::class) +class SettingsTestApplication diff --git a/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/repositories/SystemSettingRepositoryTest.kt b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/repositories/SystemSettingRepositoryTest.kt new file mode 100644 index 0000000..2b2c0bf --- /dev/null +++ b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/repositories/SystemSettingRepositoryTest.kt @@ -0,0 +1,53 @@ +package ru.touchin.settings.repositories + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.mockito.internal.matchers.apachecommons.ReflectionEquals +import org.springframework.beans.factory.annotation.Autowired +import ru.touchin.common.spring.test.jpa.repository.RepositoryTest +import ru.touchin.settings.exceptions.SettingNotFoundException +import ru.touchin.settings.models.SystemSettingModel +import javax.persistence.EntityManager + +@RepositoryTest +internal class SystemSettingRepositoryTest { + + @Autowired + private lateinit var systemSettingsRepository: SystemSettingsRepository + + @Autowired + private lateinit var entityManager: EntityManager + + @Test + @DisplayName("Настройки должны сохраняться в базе") + fun shouldBeSaved() { + val setting = SystemSettingModel() + .apply { + key = "max.threads" + value = "5" + } + + systemSettingsRepository.save(setting) + + // если не вызывать clear, то репозиторий возвращает тот же самый объект и кеша + entityManager.apply { + flush() + clear() + } + + val actualSetting = systemSettingsRepository.findByIdOrThrow(setting.key) + + Assertions.assertTrue(ReflectionEquals(setting, "createdAt").matches(actualSetting)) + } + + @Test + @DisplayName("Если настройки не найдены, должна быть ошибка SettingNotFoundException") + fun shouldBeSettingNotFoundException() { + assertThrows { + systemSettingsRepository.findByIdOrThrow("missing") + } + } + +} diff --git a/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplDeserializationTest.kt b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplDeserializationTest.kt new file mode 100644 index 0000000..59a2dcf --- /dev/null +++ b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplDeserializationTest.kt @@ -0,0 +1,137 @@ +package ru.touchin.settings.services + +import com.fasterxml.jackson.databind.ObjectMapper +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.spy +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.mockito.internal.matchers.apachecommons.ReflectionEquals +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import ru.touchin.settings.annotations.SettingMapper +import ru.touchin.settings.dto.SystemSetting +import ru.touchin.settings.models.SystemSettingModel +import ru.touchin.settings.repositories.SystemSettingsRepository +import java.util.* +import javax.annotation.PostConstruct + +internal data class UserObject(val name: String, val age: Int) + +@ActiveProfiles("test") +@SpringBootTest +internal class SystemSettingsServiceImplDeserializationTest { + + + @Autowired + @SettingMapper + private lateinit var settingMapper: ObjectMapper + + private val systemSettingsRepository: SystemSettingsRepository = mock {} + + private lateinit var systemSettingsService: SystemSettingsService + + @PostConstruct + fun init() { + systemSettingsService = spy( + SystemSettingsServiceImpl( + systemSettingsRepository = systemSettingsRepository, + settingsObjectMapper = settingMapper + ) + ) + } + + + private fun check(systemSetting: SystemSetting, serializedValue: String, assert: (T, T) -> Unit = { e, a -> + assertTrue(ReflectionEquals(e).matches(a)) + }) { + doReturn( + Optional.of(SystemSettingModel().apply { + key = systemSetting.key + value = serializedValue + }) + ).`when`(systemSettingsRepository).findById(any()) + + systemSetting.value + ?.let { actualValue -> + val actualSystemSetting = systemSettingsService.get(systemSetting.key, actualValue::class.java) + + assert(systemSetting.value, actualSystemSetting.value) + } + ?: run { + assertNull(systemSettingsService.get(systemSetting.key, ""::class.java).value) + } + } + + + @Test + fun stringShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.string", + value = "hello" + ) + + check(systemSetting, "\"hello\"") { expected, actual -> + assertEquals(expected, actual) + } + } + + @Test + fun intShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.int", + value = 23 + ) + + check(systemSetting, "23") + } + + @Test + fun nullShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.null", + value = null + ) + + check(systemSetting, "null") + } + + + @Test + fun arrayShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.array", + value = arrayOf(1,2,3) + ) + + check(systemSetting, "[1,2,3]") + } + + @Test + fun listShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.list", + value = listOf(1,2,3) + ) + + check(systemSetting, "[1,2,3]") { expected, actual -> + assertEquals(expected, actual) + } + } + + + @Test + fun objectShouldBeDeserialized() { + val systemSetting = SystemSetting( + key = "setting.object", + value = UserObject("mike", 32) + ) + + check(systemSetting, "{\"name\":\"mike\",\"age\":32}") + } + +} diff --git a/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplSerializationTest.kt b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplSerializationTest.kt new file mode 100644 index 0000000..d7052d0 --- /dev/null +++ b/settings-spring-jpa/src/test/kotlin/ru/touchin/settings/services/SystemSettingsServiceImplSerializationTest.kt @@ -0,0 +1,121 @@ +package ru.touchin.settings.services + +import com.fasterxml.jackson.databind.ObjectMapper +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.argThat +import com.nhaarman.mockitokotlin2.doAnswer +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.spy +import com.nhaarman.mockitokotlin2.verify +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import ru.touchin.settings.annotations.SettingMapper +import ru.touchin.settings.dto.SystemSetting +import ru.touchin.settings.models.SystemSettingModel +import ru.touchin.settings.repositories.SystemSettingsRepository +import java.util.* +import javax.annotation.PostConstruct + +@ActiveProfiles("test") +@SpringBootTest +internal class SystemSettingsServiceImplSerializationTest { + + @Autowired + @SettingMapper + private lateinit var settingMapper: ObjectMapper + + private val systemSettingsRepository: SystemSettingsRepository = mock {} + + private lateinit var systemSettingsService: SystemSettingsService + + @PostConstruct + fun init() { + systemSettingsService = spy( + SystemSettingsServiceImpl( + systemSettingsRepository = systemSettingsRepository, + settingsObjectMapper = settingMapper + ) + ) + } + + + private fun check(systemSetting: SystemSetting<*>, serializedValue: String) { + doReturn(Optional.empty()).`when`(systemSettingsRepository).findById(any()) + doAnswer { it.getArgument(0) as SystemSettingModel }.`when`(systemSettingsRepository).save(any()) + + systemSettingsService.save(systemSetting) + + verify(systemSettingsRepository).save(argThat { + value == serializedValue + }) + } + + + @Test + fun stringShouldBeSerialized() { + val systemSetting = SystemSetting( + key = "setting.string", + value = "hello" + ) + + check(systemSetting, "\"hello\"") + } + + @Test + fun intShouldBeSerialized() { + val systemSetting = SystemSetting( + key = "setting.int", + value = 23 + ) + + check(systemSetting, "23") + } + + @Test + fun nullShouldBeSerialized() { + val systemSetting = SystemSetting( + key = "setting.null", + value = null + ) + + check(systemSetting, "null") + } + + + @Test + fun arrayShouldBeSerialized() { + val systemSetting = SystemSetting( + key = "setting.array", + value = arrayOf(1,2,3) + ) + + check(systemSetting, "[1,2,3]") + } + + @Test + fun listShouldBeSerialized() { + val systemSetting = SystemSetting( + key = "setting.list", + value = listOf(1,2,3) + ) + + check(systemSetting, "[1,2,3]") + } + + + @Test + fun objectShouldBeSerialized() { + data class UserObject(val name: String, val age: Int) + + val systemSetting = SystemSetting( + key = "setting.object", + value = UserObject("mike", 32) + ) + + check(systemSetting, "{\"name\":\"mike\",\"age\":32}") + } + +} diff --git a/settings-spring-jpa/src/test/resources/application-test-slow.yml b/settings-spring-jpa/src/test/resources/application-test-slow.yml new file mode 100644 index 0000000..40fc4f3 --- /dev/null +++ b/settings-spring-jpa/src/test/resources/application-test-slow.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test-slow.yml" diff --git a/settings-spring-jpa/src/test/resources/application-test.yml b/settings-spring-jpa/src/test/resources/application-test.yml new file mode 100644 index 0000000..956da22 --- /dev/null +++ b/settings-spring-jpa/src/test/resources/application-test.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test.yml" diff --git a/settings-spring-jpa/src/test/resources/db/changelog/db.changelog-master.yaml b/settings-spring-jpa/src/test/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..f77219d --- /dev/null +++ b/settings-spring-jpa/src/test/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,4 @@ +databaseChangeLog: + - changeset: + id: 1 + author: test diff --git a/settings.gradle.kts b/settings.gradle.kts index e8fe346..24583b8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,3 +36,4 @@ include("exception-handler-spring-web") include("exception-handler-logger-spring-web") include("version-spring-web") include("response-wrapper-spring-web") +include("settings-spring-jpa")