Migration to Drone CI #8

Merged
william merged 22 commits from features into master 2021-08-01 17:28:44 -04:00
9 changed files with 118 additions and 120 deletions
Showing only changes of commit 84af6f3d8a - Show all commits

View File

@ -2,12 +2,12 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
group = "dev.fyloz.colorrecipesexplorer"
val kotlinVersion = "1.5.0"
val kotlinVersion = "1.5.21"
val springBootVersion = "2.3.4.RELEASE"
plugins {
// Outer scope variables can't be accessed in the plugins section, so we have to redefine them here
val kotlinVersion = "1.5.0"
val kotlinVersion = "1.5.21"
val springBootVersion = "2.3.4.RELEASE"
id("java")
@ -46,7 +46,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-devtools:${springBootVersion}")
testImplementation("org.springframework:spring-test:5.1.6.RELEASE")
testImplementation("org.mockito:mockito-inline:3.6.0")
testImplementation("org.mockito:mockito-inline:3.11.2")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.2")
testImplementation("io.mockk:mockk:1.10.6")

View File

@ -66,10 +66,16 @@ data class ConfigurationImageDto(
fun configuration(
type: ConfigurationType,
content: String,
content: String = type.defaultContent.toString(),
lastUpdated: LocalDateTime? = null
) = Configuration(type, content, lastUpdated ?: LocalDateTime.now())
fun configuration(
dto: ConfigurationDto
) = with(dto) {
configuration(type = key.toConfigurationType(), content = content)
}
enum class ConfigurationType(
val key: String,
val defaultContent: Any? = null,

View File

@ -1,6 +1,5 @@
package dev.fyloz.colorrecipesexplorer.service
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
import dev.fyloz.colorrecipesexplorer.model.touchupkit.*
import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository
@ -30,12 +29,12 @@ interface TouchUpKitService :
/**
* Generates and returns a [PdfDocument] for the given [job] as a [ByteArrayResource].
*
* If [CreProperties.cacheGeneratedFiles] is enabled and a file exists for the job, its content will be returned.
* If TOUCH_UP_KIT_CACHE_PDF is enabled and a file exists for the job, its content will be returned.
* If caching is enabled but no file exists for the job, the generated ByteArrayResource will be cached on the disk.
*/
fun generateJobPdfResource(job: String): ByteArrayResource
/** Writes the given [document] to the [FileService] if [CreProperties.cacheGeneratedFiles] is enabled. */
/** Writes the given [document] to the [FileService] if TOUCH_UP_KIT_CACHE_PDF is enabled. */
fun String.cachePdfDocument(document: PdfDocument)
}
@ -48,6 +47,10 @@ class TouchUpKitServiceImpl(
) : AbstractExternalModelService<TouchUpKit, TouchUpKitSaveDto, TouchUpKitUpdateDto, TouchUpKitOutputDto, TouchUpKitRepository>(
touchUpKitRepository
), TouchUpKitService {
private val cacheGeneratedFiles by lazy {
configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF).content == true.toString()
}
override fun idNotFoundException(id: Long) = touchUpKitIdNotFoundException(id)
override fun idAlreadyExistsException(id: Long) = touchUpKitIdAlreadyExistsException(id)
@ -118,7 +121,7 @@ class TouchUpKitServiceImpl(
}
override fun generateJobPdfResource(job: String): ByteArrayResource {
if (cacheGeneratedFiles()) {
if (cacheGeneratedFiles) {
with(job.pdfDocumentPath()) {
if (fileService.exists(this)) {
return fileService.read(this)
@ -132,7 +135,7 @@ class TouchUpKitServiceImpl(
}
override fun String.cachePdfDocument(document: PdfDocument) {
if (!cacheGeneratedFiles()) return
if (!cacheGeneratedFiles) return
fileService.write(document.toByteArrayResource(), this.pdfDocumentPath(), true)
}
@ -142,7 +145,4 @@ class TouchUpKitServiceImpl(
private fun TouchUpKit.pdfUrl() =
"${configService.get(ConfigurationType.INSTANCE_URL).content}$TOUCH_UP_KIT_CONTROLLER_PATH/pdf?job=$project"
private fun cacheGeneratedFiles() =
configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF).content == "true"
}

View File

@ -0,0 +1,17 @@
package dev.fyloz.colorrecipesexplorer.utils
import org.springframework.security.crypto.encrypt.Encryptors
import org.springframework.security.crypto.encrypt.TextEncryptor
fun String.encrypt(password: String, salt: String): String =
withTextEncryptor(password, salt) {
it.encrypt(this)
}
fun String.decrypt(password: String, salt: String): String =
withTextEncryptor(password, salt) {
it.decrypt(this)
}
private fun withTextEncryptor(password: String, salt: String, op: (TextEncryptor) -> String) =
op(Encryptors.text(password, salt))

View File

@ -1,7 +1,7 @@
package dev.fyloz.colorrecipesexplorer.service
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.colorrecipesexplorer.config.defaultGroupCookieName
import dev.fyloz.colorrecipesexplorer.config.security.defaultGroupCookieName
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
import dev.fyloz.colorrecipesexplorer.model.account.*

View File

@ -1,22 +1,26 @@
package dev.fyloz.colorrecipesexplorer.service
import dev.fyloz.colorrecipesexplorer.config.FileConfiguration
import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
import dev.fyloz.colorrecipesexplorer.model.*
import dev.fyloz.colorrecipesexplorer.repository.ConfigurationRepository
import dev.fyloz.colorrecipesexplorer.service.config.CONFIGURATION_FORMATTED_LIST_DELIMITER
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationServiceImpl
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationSource
import dev.fyloz.colorrecipesexplorer.utils.encrypt
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<ConfigurationRepository>()
private val fileConfiguration = mockk<FileConfiguration>()
private val service = spyk(ConfigurationServiceImpl(repository, mockk(), fileConfiguration, mockk(), mockk(), mockk()))
private val fileService = mockk<FileService>()
private val configurationSource = mockk<ConfigurationSource>()
private val securityProperties = mockk<CreSecurityProperties> {
every { configSalt } returns "d32270943af7e1cc"
}
private val service = spyk(ConfigurationServiceImpl(fileService, configurationSource, securityProperties, mockk()))
@AfterEach
fun afterEach() {
@ -126,73 +130,22 @@ class ConfigurationServiceTest {
}
@Test
fun `get(type) gets in the repository when the given ConfigurationType is not computed or a file property`() {
fun `get(type) gets the configuration in the ConfigurationSource`() {
val type = ConfigurationType.INSTANCE_ICON_PATH
val configuration = configuration(type = type)
every { repository.findById(type.key) } returns Optional.of(
ConfigurationEntity(type.key, type.key, LocalDateTime.now())
)
every { configurationSource.get(type) } returns configuration
val configuration = service.get(type)
val found = 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)
assertEquals(configuration, found)
}
@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()
every { configurationSource.get(type) } returns null
with(assertThrows<ConfigurationNotSetException> { service.get(type) }) {
assertEquals(type, this.type)
@ -200,56 +153,64 @@ class ConfigurationServiceTest {
verify {
service.get(type)
repository.findById(type.key)
configurationSource.get(type)
}
}
@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"
fun `get(type) throws InvalidConfigurationKeyException when the given ConfigurationType is encryption salt`() {
val type = ConfigurationType.GENERATED_ENCRYPTION_SALT
every { fileConfiguration.set(type, content) } just runs
service.set(type, content)
verify {
service.set(type, content)
fileConfiguration.set(type, content)
}
confirmVerified(service, fileConfiguration)
assertThrows<InvalidConfigurationKeyException> { service.get(type) }
}
@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()
fun `get(type) decrypts configuration content when the given ConfigurationType is secure`() {
val type = ConfigurationType.DATABASE_PASSWORD
val content = "securepassword"
val configuration = configuration(
type = type,
content = content.encrypt(type.key, securityProperties.configSalt!!)
)
every { repository.save(entity) } returns entity
every { configurationSource.get(type) } returns configuration
service.set(type, content)
val found = service.get(type)
verify {
service.set(type, content)
repository.save(entity)
}
confirmVerified(service, repository)
assertEquals(content, found.content)
}
@Test
fun `set() throws CannotSetComputedConfigurationException when the given ConfigurationType is a computed configuration`() {
val type = ConfigurationType.JAVA_VERSION
val content = "5"
fun `set(configuration) set configuration in ConfigurationSource`() {
val configuration = configuration(type = ConfigurationType.INSTANCE_NAME)
with(assertThrows<CannotSetComputedConfigurationException> { service.set(type, content) }) {
assertEquals(type, this.type)
}
every { configurationSource.set(any<Configuration>()) } just runs
service.set(configuration)
verify {
service.set(type, content)
configurationSource.set(configuration)
}
}
@Test
fun `set(configuration) encrypts secure configurations`() {
val type = ConfigurationType.DATABASE_PASSWORD
val content = "securepassword"
val encryptedContent =content.encrypt(type.key, securityProperties.configSalt!!)
val configuration = configuration(type = type, content = content)
mockkStatic(String::encrypt)
every { configurationSource.set(any<Configuration>()) } just runs
every { content.encrypt(any(), any()) } returns encryptedContent
service.set(configuration)
verify {
configurationSource.set(match<Configuration> {
it.content == encryptedContent
})
}
confirmVerified(service)
}
}

View File

@ -15,7 +15,6 @@ import kotlin.test.assertTrue
private val creProperties = CreProperties().apply {
dataDirectory = "data"
deploymentUrl = "http://localhost"
}
private const val mockFilePath = "existingFile"
private val mockFilePathPath = Path.of(mockFilePath)

View File

@ -5,6 +5,7 @@ import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
import dev.fyloz.colorrecipesexplorer.model.*
import dev.fyloz.colorrecipesexplorer.model.account.group
import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationService
import io.mockk.*
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
@ -271,7 +272,7 @@ private class RecipeImageServiceTestContext {
val recipeImagesIds = setOf(1L, 10L, 21L)
val recipeImagesNames = recipeImagesIds.map { it.imageName }.toSet()
val recipeImagesFiles = recipeImagesNames.map { File(it) }.toTypedArray()
val recipeDirectory = spyk(File(recipe.imagesDirectoryPath)) {
val recipeDirectory = mockk<File> {
every { exists() } returns true
every { isDirectory } returns true
every { listFiles() } returns recipeImagesFiles

View File

@ -5,6 +5,7 @@ import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
import dev.fyloz.colorrecipesexplorer.model.configuration
import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository
import dev.fyloz.colorrecipesexplorer.service.*
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationService
import dev.fyloz.colorrecipesexplorer.utils.PdfDocument
import dev.fyloz.colorrecipesexplorer.utils.toByteArrayResource
import io.mockk.*
@ -18,9 +19,7 @@ private class TouchUpKitServiceTestContext {
val fileService = mockk<FileService> {
every { write(any<ByteArrayResource>(), any(), any()) } just Runs
}
val creProperties = mockk<CreProperties> {
every { cacheGeneratedFiles } returns false
}
val creProperties = mockk<CreProperties>()
val configService = mockk<ConfigurationService>(relaxed = true)
val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, configService, touchUpKitRepository))
val pdfDocumentData = mockk<ByteArrayResource>()
@ -79,10 +78,13 @@ class TouchUpKitServiceTest {
@Test
fun `generateJobPdfResource() returns a cached ByteArrayResource from the FileService when caching is enabled and a cached file eixsts for the given job`() {
test {
every { creProperties.cacheGeneratedFiles } returns true
enableCachePdf()
every { fileService.exists(any()) } returns true
every { fileService.read(any()) } returns pdfDocumentData
every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF, "true")
every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(
ConfigurationType.TOUCH_UP_KIT_CACHE_PDF,
"true"
)
val redResource = touchUpKitService.generateJobPdfResource(job)
@ -95,7 +97,7 @@ class TouchUpKitServiceTest {
@Test
fun `cachePdfDocument() does nothing when caching is disabled`() {
test {
every { creProperties.cacheGeneratedFiles } returns false
disableCachePdf()
with(touchUpKitService) {
job.cachePdfDocument(pdfDocument)
@ -110,8 +112,7 @@ class TouchUpKitServiceTest {
@Test
fun `cachePdfDocument() writes the given document to the FileService when cache is enabled`() {
test {
every { creProperties.cacheGeneratedFiles } returns true
every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF, "true")
enableCachePdf()
with(touchUpKitService) {
job.cachePdfDocument(pdfDocument)
@ -123,6 +124,19 @@ class TouchUpKitServiceTest {
}
}
private fun TouchUpKitServiceTestContext.enableCachePdf() =
this.setCachePdf(true)
private fun TouchUpKitServiceTestContext.disableCachePdf() =
this.setCachePdf(false)
private fun TouchUpKitServiceTestContext.setCachePdf(enabled: Boolean) {
every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(
type = ConfigurationType.TOUCH_UP_KIT_CACHE_PDF,
enabled.toString()
)
}
private fun test(test: TouchUpKitServiceTestContext.() -> Unit) {
TouchUpKitServiceTestContext().test()
}