This commit is contained in:
parent
b598652594
commit
0e97fef70e
|
@ -0,0 +1,15 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.dtos
|
||||||
|
|
||||||
|
data class RecipeStepDto(
|
||||||
|
override val id: Long = 0L,
|
||||||
|
|
||||||
|
val position: Int,
|
||||||
|
|
||||||
|
val message: String
|
||||||
|
) : EntityDto {
|
||||||
|
companion object {
|
||||||
|
const val VALIDATION_ERROR_CODE_INVALID_FIRST_STEP = "first"
|
||||||
|
const val VALIDATION_ERROR_CODE_DUPLICATED_STEPS_POSITION = "duplicated"
|
||||||
|
const val VALIDATION_ERROR_CODE_GAP_BETWEEN_STEPS_POSITIONS = "gap"
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ abstract class BaseLogic<D : EntityDto, S : Service<D, *, *>>(
|
||||||
override fun deleteById(id: Long) =
|
override fun deleteById(id: Long) =
|
||||||
service.deleteById(id)
|
service.deleteById(id)
|
||||||
|
|
||||||
protected fun notFoundException(identifierName: String = idIdentifierName, value: Any) =
|
protected fun notFoundException(identifierName: String = ID_IDENTIFIER_NAME, value: Any) =
|
||||||
NotFoundException(
|
NotFoundException(
|
||||||
typeNameLowerCase,
|
typeNameLowerCase,
|
||||||
"$typeName not found",
|
"$typeName not found",
|
||||||
|
@ -70,7 +70,7 @@ abstract class BaseLogic<D : EntityDto, S : Service<D, *, *>>(
|
||||||
identifierName
|
identifierName
|
||||||
)
|
)
|
||||||
|
|
||||||
protected fun alreadyExistsException(identifierName: String = nameIdentifierName, value: Any) =
|
protected fun alreadyExistsException(identifierName: String = NAME_IDENTIFIER_NAME, value: Any) =
|
||||||
AlreadyExistsException(
|
AlreadyExistsException(
|
||||||
typeNameLowerCase,
|
typeNameLowerCase,
|
||||||
"$typeName already exists",
|
"$typeName already exists",
|
||||||
|
@ -87,7 +87,7 @@ abstract class BaseLogic<D : EntityDto, S : Service<D, *, *>>(
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val idIdentifierName = "id"
|
const val ID_IDENTIFIER_NAME = "id"
|
||||||
const val nameIdentifierName = "name"
|
const val NAME_IDENTIFIER_NAME = "name"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,18 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||||
import dev.fyloz.colorrecipesexplorer.model.RecipeGroupInformation
|
import dev.fyloz.colorrecipesexplorer.model.RecipeGroupInformation
|
||||||
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
||||||
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
||||||
import dev.fyloz.colorrecipesexplorer.model.recipeStepIdAlreadyExistsException
|
import dev.fyloz.colorrecipesexplorer.model.recipeStepDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.recipeStepIdNotFoundException
|
import dev.fyloz.colorrecipesexplorer.service.RecipeStepService
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository
|
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.findDuplicated
|
import dev.fyloz.colorrecipesexplorer.utils.findDuplicated
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.hasGaps
|
import dev.fyloz.colorrecipesexplorer.utils.hasGaps
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
interface RecipeStepLogic : ModelService<RecipeStep, RecipeStepRepository> {
|
interface RecipeStepLogic : Logic<RecipeStepDto, RecipeStepService> {
|
||||||
/** Validates the steps of the given [groupInformation], according to the criteria of [validateSteps]. */
|
/** Validates the steps of the given [groupInformation], according to the criteria of [validateSteps]. */
|
||||||
fun validateGroupInformationSteps(groupInformation: RecipeGroupInformation)
|
fun validateGroupInformationSteps(groupInformation: RecipeGroupInformation)
|
||||||
|
|
||||||
|
@ -22,106 +21,112 @@ interface RecipeStepLogic : ModelService<RecipeStep, RecipeStepRepository> {
|
||||||
* There must also be no gap between the positions.
|
* There must also be no gap between the positions.
|
||||||
* If any of those criteria are not met, an [InvalidGroupStepsPositionsException] will be thrown.
|
* If any of those criteria are not met, an [InvalidGroupStepsPositionsException] will be thrown.
|
||||||
*/
|
*/
|
||||||
fun validateSteps(steps: Set<RecipeStep>)
|
fun validateSteps(steps: Set<RecipeStepDto>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service
|
@LogicComponent
|
||||||
@RequireDatabase
|
class DefaultRecipeStepLogic(recipeStepService: RecipeStepService) :
|
||||||
class DefaultRecipeStepLogic(recipeStepRepository: RecipeStepRepository) :
|
BaseLogic<RecipeStepDto, RecipeStepService>(recipeStepService, RecipeStep::class.simpleName!!), RecipeStepLogic {
|
||||||
AbstractModelService<RecipeStep, RecipeStepRepository>(recipeStepRepository),
|
|
||||||
RecipeStepLogic {
|
|
||||||
override fun idNotFoundException(id: Long) = recipeStepIdNotFoundException(id)
|
|
||||||
override fun idAlreadyExistsException(id: Long) = recipeStepIdAlreadyExistsException(id)
|
|
||||||
|
|
||||||
override fun validateGroupInformationSteps(groupInformation: RecipeGroupInformation) {
|
override fun validateGroupInformationSteps(groupInformation: RecipeGroupInformation) {
|
||||||
if (groupInformation.steps == null) return
|
if (groupInformation.steps == null) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
validateSteps(groupInformation.steps!!)
|
validateSteps(groupInformation.steps!!.map { recipeStepDto(it) }.toSet())
|
||||||
} catch (validationException: InvalidStepsPositionsException) {
|
} catch (validationException: InvalidStepsPositionsException) {
|
||||||
throw InvalidGroupStepsPositionsException(groupInformation.group, validationException)
|
throw InvalidGroupStepsPositionsException(groupInformation.group, validationException)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validateSteps(steps: Set<RecipeStep>) {
|
override fun validateSteps(steps: Set<RecipeStepDto>) {
|
||||||
if (steps.isEmpty()) return
|
if (steps.isEmpty()) return
|
||||||
|
|
||||||
val sortedSteps = steps.sortedBy { it.position }
|
val sortedSteps = steps.sortedBy { it.position }
|
||||||
val errors = mutableSetOf<InvalidStepsPositionsError>()
|
val errors = mutableSetOf<InvalidStepsPositionsError>()
|
||||||
|
|
||||||
// Check if the first step position is 1
|
// Check if the first step position is 1
|
||||||
fun isFirstStepPositionInvalid() =
|
validateFirstStepPosition(sortedSteps, errors)
|
||||||
sortedSteps[0].position != 1
|
|
||||||
|
|
||||||
// Check if any position is duplicated
|
// Check if any position is duplicated
|
||||||
fun getDuplicatedPositionsErrors() =
|
validateDuplicatedStepsPositions(sortedSteps, errors)
|
||||||
sortedSteps
|
|
||||||
.findDuplicated { it.position }
|
// Check for gaps between positions
|
||||||
.map { duplicatedStepsPositions(it) }
|
validateGapsInStepsPositions(sortedSteps, errors)
|
||||||
|
|
||||||
// Find all errors and throw if there is any
|
|
||||||
if (isFirstStepPositionInvalid()) errors += invalidFirstStepPosition(sortedSteps[0])
|
|
||||||
errors += getDuplicatedPositionsErrors()
|
|
||||||
if (errors.isEmpty() && steps.hasGaps { it.position }) errors += gapBetweenStepsPositions()
|
|
||||||
if (errors.isNotEmpty()) {
|
if (errors.isNotEmpty()) {
|
||||||
throw InvalidStepsPositionsException(errors)
|
throw InvalidStepsPositionsException(errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validateFirstStepPosition(
|
||||||
|
steps: List<RecipeStepDto>,
|
||||||
|
errors: MutableSet<InvalidStepsPositionsError>
|
||||||
|
) {
|
||||||
|
if (steps[0].position != 1) {
|
||||||
|
errors += InvalidStepsPositionsError(
|
||||||
|
RecipeStepDto.VALIDATION_ERROR_CODE_INVALID_FIRST_STEP,
|
||||||
|
"The first step must be at position 1"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateDuplicatedStepsPositions(
|
||||||
|
steps: List<RecipeStepDto>,
|
||||||
|
errors: MutableSet<InvalidStepsPositionsError>
|
||||||
|
) {
|
||||||
|
errors += steps
|
||||||
|
.findDuplicated { it.position }
|
||||||
|
.map {
|
||||||
|
InvalidStepsPositionsError(
|
||||||
|
RecipeStepDto.VALIDATION_ERROR_CODE_DUPLICATED_STEPS_POSITION,
|
||||||
|
"The position $it is duplicated"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateGapsInStepsPositions(
|
||||||
|
steps: List<RecipeStepDto>,
|
||||||
|
errors: MutableSet<InvalidStepsPositionsError>
|
||||||
|
) {
|
||||||
|
if (errors.isEmpty() && steps.hasGaps { it.position }) {
|
||||||
|
errors += InvalidStepsPositionsError(
|
||||||
|
RecipeStepDto.VALIDATION_ERROR_CODE_GAP_BETWEEN_STEPS_POSITIONS,
|
||||||
|
"There is a gap between steps positions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class InvalidStepsPositionsError(
|
data class InvalidStepsPositionsError(
|
||||||
val type: String,
|
val type: String,
|
||||||
val details: String
|
val details: String
|
||||||
)
|
)
|
||||||
|
|
||||||
class InvalidStepsPositionsException(
|
class InvalidStepsPositionsException(
|
||||||
val errors: Set<InvalidStepsPositionsError>
|
val errors: Set<InvalidStepsPositionsError>
|
||||||
) : RestException(
|
) : RestException(
|
||||||
"invalid-recipestep-position",
|
"invalid-recipestep-position",
|
||||||
"Invalid steps positions",
|
"Invalid steps positions",
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
"The position of steps are invalid",
|
"The position of steps are invalid",
|
||||||
mapOf(
|
mapOf(
|
||||||
"invalidSteps" to errors
|
"invalidSteps" to errors
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
class InvalidGroupStepsPositionsException(
|
class InvalidGroupStepsPositionsException(
|
||||||
val group: Group,
|
val group: Group,
|
||||||
val exception: InvalidStepsPositionsException
|
val exception: InvalidStepsPositionsException
|
||||||
) : RestException(
|
) : RestException(
|
||||||
"invalid-groupinformation-recipestep-position",
|
"invalid-groupinformation-recipestep-position",
|
||||||
"Invalid steps positions",
|
"Invalid steps positions",
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
"The position of steps for the group ${group.name} are invalid",
|
"The position of steps for the group ${group.name} are invalid",
|
||||||
mapOf(
|
mapOf(
|
||||||
"group" to group.name,
|
"group" to group.name,
|
||||||
"groupId" to group.id!!,
|
"groupId" to group.id!!,
|
||||||
"invalidSteps" to exception.errors
|
"invalidSteps" to exception.errors
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
val errors: Set<InvalidStepsPositionsError>
|
val errors: Set<InvalidStepsPositionsError>
|
||||||
get() = exception.errors
|
get() = exception.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
const val INVALID_FIRST_STEP_POSITION_ERROR_CODE = "first"
|
|
||||||
const val DUPLICATED_STEPS_POSITIONS_ERROR_CODE = "duplicated"
|
|
||||||
const val GAP_BETWEEN_STEPS_POSITIONS_ERROR_CODE = "gap"
|
|
||||||
|
|
||||||
private fun invalidFirstStepPosition(step: RecipeStep) =
|
|
||||||
InvalidStepsPositionsError(
|
|
||||||
INVALID_FIRST_STEP_POSITION_ERROR_CODE,
|
|
||||||
"The position ${step.position} is under the minimum of 1"
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun duplicatedStepsPositions(position: Int) =
|
|
||||||
InvalidStepsPositionsError(
|
|
||||||
DUPLICATED_STEPS_POSITIONS_ERROR_CODE,
|
|
||||||
"The position $position is duplicated"
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun gapBetweenStepsPositions() =
|
|
||||||
InvalidStepsPositionsError(
|
|
||||||
GAP_BETWEEN_STEPS_POSITIONS_ERROR_CODE,
|
|
||||||
"There is a gap between steps positions"
|
|
||||||
)
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.model
|
package dev.fyloz.colorrecipesexplorer.model
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -16,31 +15,7 @@ data class RecipeStep(
|
||||||
val message: String
|
val message: String
|
||||||
) : ModelEntity
|
) : ModelEntity
|
||||||
|
|
||||||
// ==== DSL ====
|
@Deprecated("Temporary DSL for transition")
|
||||||
fun recipeStep(
|
fun recipeStepDto(
|
||||||
id: Long? = null,
|
entity: RecipeStep
|
||||||
position: Int = 0,
|
) = RecipeStepDto(entity.id!!, entity.position, entity.message)
|
||||||
message: String = "message",
|
|
||||||
op: RecipeStep.() -> Unit = {}
|
|
||||||
) = RecipeStep(id, position, message).apply(op)
|
|
||||||
|
|
||||||
// ==== Exceptions ====
|
|
||||||
private const val RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE = "Recipe step not found"
|
|
||||||
private const val RECIPE_STEP_ALREADY_EXISTS_EXCEPTION_TITLE = "Recipe step already exists"
|
|
||||||
private const val RECIPE_STEP_EXCEPTION_ERROR_CODE = "recipestep"
|
|
||||||
|
|
||||||
fun recipeStepIdNotFoundException(id: Long) =
|
|
||||||
NotFoundException(
|
|
||||||
RECIPE_STEP_EXCEPTION_ERROR_CODE,
|
|
||||||
RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE,
|
|
||||||
"A recipe step with the id $id could not be found",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
|
|
||||||
fun recipeStepIdAlreadyExistsException(id: Long) =
|
|
||||||
AlreadyExistsException(
|
|
||||||
RECIPE_STEP_EXCEPTION_ERROR_CODE,
|
|
||||||
RECIPE_STEP_ALREADY_EXISTS_EXCEPTION_TITLE,
|
|
||||||
"A recipe step with the id $id already exists",
|
|
||||||
id
|
|
||||||
)
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.service
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
||||||
|
import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository
|
||||||
|
|
||||||
|
interface RecipeStepService : Service<RecipeStepDto, RecipeStep, RecipeStepRepository>
|
||||||
|
|
||||||
|
@ServiceComponent
|
||||||
|
class DefaultRecipeStepService(repository: RecipeStepRepository) :
|
||||||
|
BaseService<RecipeStepDto, RecipeStep, RecipeStepRepository>(repository), RecipeStepService {
|
||||||
|
override fun toDto(entity: RecipeStep) =
|
||||||
|
RecipeStepDto(entity.id!!, entity.position, entity.message)
|
||||||
|
|
||||||
|
override fun toEntity(dto: RecipeStepDto) =
|
||||||
|
RecipeStep(dto.id, dto.position, dto.message)
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.RecipeGroupInformation
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
||||||
|
import dev.fyloz.colorrecipesexplorer.service.RecipeStepService
|
||||||
|
import io.mockk.*
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertDoesNotThrow
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class DefaultRecipeStepLogicTest {
|
||||||
|
private val recipeStepServiceMock = mockk<RecipeStepService>()
|
||||||
|
|
||||||
|
private val recipeStepLogic = spyk(DefaultRecipeStepLogic(recipeStepServiceMock))
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
internal fun afterEach() {
|
||||||
|
clearAllMocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateGroupInformationSteps_normalBehavior_callsValidateSteps() {
|
||||||
|
// Arrange
|
||||||
|
every { recipeStepLogic.validateSteps(any()) } just runs
|
||||||
|
|
||||||
|
val group = Group(1L, "Unit test group")
|
||||||
|
val steps = mutableSetOf(RecipeStep(1L, 1, "A message"))
|
||||||
|
val groupInfo = RecipeGroupInformation(1L, group, "A note", steps)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
recipeStepLogic.validateGroupInformationSteps(groupInfo)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify {
|
||||||
|
recipeStepLogic.validateSteps(any()) // TODO replace with actual steps dtos when RecipeGroupInformation updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateGroupInformationSteps_stepSetIsNull_doesNothing() {
|
||||||
|
// Arrange
|
||||||
|
every { recipeStepLogic.validateSteps(any()) } just runs
|
||||||
|
|
||||||
|
val group = Group(1L, "Unit test group")
|
||||||
|
val groupInfo = RecipeGroupInformation(1L, group, "A note", null)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
recipeStepLogic.validateGroupInformationSteps(groupInfo)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(exactly = 0) {
|
||||||
|
recipeStepLogic.validateSteps(any()) // TODO replace with actual steps dtos when RecipeGroupInformation updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateGroupInformationSteps_invalidSteps_throwsInvalidGroupStepsPositionsException() {
|
||||||
|
// Arrange
|
||||||
|
val errors = setOf(InvalidStepsPositionsError("error", "An unit test error"))
|
||||||
|
every { recipeStepLogic.validateSteps(any()) } throws InvalidStepsPositionsException(errors)
|
||||||
|
|
||||||
|
val group = Group(1L, "Unit test group")
|
||||||
|
val steps = mutableSetOf(RecipeStep(1L, 1, "A message"))
|
||||||
|
val groupInfo = RecipeGroupInformation(1L, group, "A note", steps)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
assertThrows<InvalidGroupStepsPositionsException> { recipeStepLogic.validateGroupInformationSteps(groupInfo) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_normalBehavior_doesNothing() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf(
|
||||||
|
RecipeStepDto(1L, 1, "A message"),
|
||||||
|
RecipeStepDto(2L, 2, "Another message")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
assertDoesNotThrow { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_emptyStepSet_doesNothing() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf<RecipeStepDto>()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
assertDoesNotThrow { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_hasInvalidPositions_throwsInvalidStepsPositionsException() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf(
|
||||||
|
RecipeStepDto(1L, 2, "A message"),
|
||||||
|
RecipeStepDto(2L, 3, "Another message")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
assertThrows<InvalidStepsPositionsException> { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_firstStepPositionInvalid_returnsInvalidStepValidationError() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf(
|
||||||
|
RecipeStepDto(1L, 2, "A message"),
|
||||||
|
RecipeStepDto(2L, 3, "Another message")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val exception = assertThrows<InvalidStepsPositionsException> { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertTrue {
|
||||||
|
exception.errors.any { it.type == RecipeStepDto.VALIDATION_ERROR_CODE_INVALID_FIRST_STEP }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_duplicatedPositions_returnsInvalidStepValidationError() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf(
|
||||||
|
RecipeStepDto(1L, 1, "A message"),
|
||||||
|
RecipeStepDto(2L, 1, "Another message")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val exception = assertThrows<InvalidStepsPositionsException> { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertTrue {
|
||||||
|
exception.errors.any { it.type == RecipeStepDto.VALIDATION_ERROR_CODE_DUPLICATED_STEPS_POSITION }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateSteps_gapsInPositions_returnsInvalidStepValidationError() {
|
||||||
|
// Arrange
|
||||||
|
val recipeSteps = setOf(
|
||||||
|
RecipeStepDto(1L, 1, "A message"),
|
||||||
|
RecipeStepDto(2L, 3, "Another message")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val exception = assertThrows<InvalidStepsPositionsException> { recipeStepLogic.validateSteps(recipeSteps) }
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertTrue {
|
||||||
|
exception.errors.any { it.type == RecipeStepDto.VALIDATION_ERROR_CODE_GAP_BETWEEN_STEPS_POSITIONS }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
//class RecipeStepLogicTest :
|
||||||
|
// AbstractModelServiceTest<RecipeStep, RecipeStepLogic, RecipeStepRepository>() {
|
||||||
|
// override val repository: RecipeStepRepository = mock()
|
||||||
|
// override val logic: RecipeStepLogic = spy(DefaultRecipeStepLogic(repository))
|
||||||
|
//
|
||||||
|
// override val entity: RecipeStep = recipeStep(id = 0L, message = "message")
|
||||||
|
// override val anotherEntity: RecipeStep = recipeStep(id = 1L, message = "another message")
|
||||||
|
//
|
||||||
|
// // validateGroupInformationSteps()
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun `validateGroupInformationSteps() calls validateSteps() with the given RecipeGroupInformation steps`() {
|
||||||
|
// withGroupInformation {
|
||||||
|
// logic.validateGroupInformationSteps(this)
|
||||||
|
//
|
||||||
|
// verify(logic).validateSteps(this.steps!!)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun `validateGroupInformationSteps() throws InvalidGroupStepsPositionsException when validateSteps() throws an InvalidStepsPositionsException`() {
|
||||||
|
// withGroupInformation {
|
||||||
|
// doAnswer { throw InvalidStepsPositionsException(setOf()) }.whenever(logic).validateSteps(this.steps!!)
|
||||||
|
//
|
||||||
|
// assertThrows<InvalidGroupStepsPositionsException> {
|
||||||
|
// logic.validateGroupInformationSteps(this)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // validateSteps()
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun `validateSteps() throws an InvalidStepsPositionsException when the position of the first step of the given groupInformation is not 1`() {
|
||||||
|
// assertInvalidStepsPositionsException(
|
||||||
|
// mutableSetOf(
|
||||||
|
// recipeStep(id = 0L, position = 0),
|
||||||
|
// recipeStep(id = 1L, position = 1),
|
||||||
|
// recipeStep(id = 2L, position = 2),
|
||||||
|
// recipeStep(id = 3L, position = 3)
|
||||||
|
// ),
|
||||||
|
// INVALID_FIRST_STEP_POSITION_ERROR_CODE
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun `validateSteps() throws an InvalidStepsPositionsException when steps positions are duplicated in the given groupInformation`() {
|
||||||
|
// assertInvalidStepsPositionsException(
|
||||||
|
// mutableSetOf(
|
||||||
|
// recipeStep(id = 0L, position = 1),
|
||||||
|
// recipeStep(id = 1L, position = 2),
|
||||||
|
// recipeStep(id = 2L, position = 2),
|
||||||
|
// recipeStep(id = 3L, position = 3)
|
||||||
|
// ),
|
||||||
|
// DUPLICATED_STEPS_POSITIONS_ERROR_CODE
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun `validateSteps() throws an InvalidStepsPositionsException when there is a gap between steps positions in the given groupInformation`() {
|
||||||
|
// assertInvalidStepsPositionsException(
|
||||||
|
// mutableSetOf(
|
||||||
|
// recipeStep(id = 0L, position = 1),
|
||||||
|
// recipeStep(id = 1L, position = 2),
|
||||||
|
// recipeStep(id = 2L, position = 4),
|
||||||
|
// recipeStep(id = 3L, position = 5)
|
||||||
|
// ),
|
||||||
|
// GAP_BETWEEN_STEPS_POSITIONS_ERROR_CODE
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun withGroupInformation(steps: MutableSet<RecipeStep>? = null, test: RecipeGroupInformation.() -> Unit) {
|
||||||
|
// recipeGroupInformation(
|
||||||
|
// group = group(id = 0L),
|
||||||
|
// steps = steps ?: mutableSetOf(
|
||||||
|
// recipeStep(id = 0L, position = 1),
|
||||||
|
// recipeStep(id = 1L, position = 2),
|
||||||
|
// recipeStep(id = 2L, position = 3),
|
||||||
|
// recipeStep(id = 3L, position = 4)
|
||||||
|
// )
|
||||||
|
// ) {
|
||||||
|
// test()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun assertInvalidStepsPositionsException(steps: MutableSet<RecipeStep>, errorType: String) {
|
||||||
|
// val exception = assertThrows<InvalidStepsPositionsException> {
|
||||||
|
// logic.validateSteps(steps)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// assertTrue { exception.errors.size == 1 }
|
||||||
|
// assertTrue { exception.errors.first().type == errorType }
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,109 +0,0 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.*
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.RecipeGroupInformation
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.account.group
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.recipeGroupInformation
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.recipeStep
|
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.TestInstance
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
||||||
class RecipeStepLogicTest :
|
|
||||||
AbstractModelServiceTest<RecipeStep, RecipeStepLogic, RecipeStepRepository>() {
|
|
||||||
override val repository: RecipeStepRepository = mock()
|
|
||||||
override val logic: RecipeStepLogic = spy(DefaultRecipeStepLogic(repository))
|
|
||||||
|
|
||||||
override val entity: RecipeStep = recipeStep(id = 0L, message = "message")
|
|
||||||
override val anotherEntity: RecipeStep = recipeStep(id = 1L, message = "another message")
|
|
||||||
|
|
||||||
// validateGroupInformationSteps()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `validateGroupInformationSteps() calls validateSteps() with the given RecipeGroupInformation steps`() {
|
|
||||||
withGroupInformation {
|
|
||||||
logic.validateGroupInformationSteps(this)
|
|
||||||
|
|
||||||
verify(logic).validateSteps(this.steps!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `validateGroupInformationSteps() throws InvalidGroupStepsPositionsException when validateSteps() throws an InvalidStepsPositionsException`() {
|
|
||||||
withGroupInformation {
|
|
||||||
doAnswer { throw InvalidStepsPositionsException(setOf()) }.whenever(logic).validateSteps(this.steps!!)
|
|
||||||
|
|
||||||
assertThrows<InvalidGroupStepsPositionsException> {
|
|
||||||
logic.validateGroupInformationSteps(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateSteps()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `validateSteps() throws an InvalidStepsPositionsException when the position of the first step of the given groupInformation is not 1`() {
|
|
||||||
assertInvalidStepsPositionsException(
|
|
||||||
mutableSetOf(
|
|
||||||
recipeStep(id = 0L, position = 0),
|
|
||||||
recipeStep(id = 1L, position = 1),
|
|
||||||
recipeStep(id = 2L, position = 2),
|
|
||||||
recipeStep(id = 3L, position = 3)
|
|
||||||
),
|
|
||||||
INVALID_FIRST_STEP_POSITION_ERROR_CODE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `validateSteps() throws an InvalidStepsPositionsException when steps positions are duplicated in the given groupInformation`() {
|
|
||||||
assertInvalidStepsPositionsException(
|
|
||||||
mutableSetOf(
|
|
||||||
recipeStep(id = 0L, position = 1),
|
|
||||||
recipeStep(id = 1L, position = 2),
|
|
||||||
recipeStep(id = 2L, position = 2),
|
|
||||||
recipeStep(id = 3L, position = 3)
|
|
||||||
),
|
|
||||||
DUPLICATED_STEPS_POSITIONS_ERROR_CODE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `validateSteps() throws an InvalidStepsPositionsException when there is a gap between steps positions in the given groupInformation`() {
|
|
||||||
assertInvalidStepsPositionsException(
|
|
||||||
mutableSetOf(
|
|
||||||
recipeStep(id = 0L, position = 1),
|
|
||||||
recipeStep(id = 1L, position = 2),
|
|
||||||
recipeStep(id = 2L, position = 4),
|
|
||||||
recipeStep(id = 3L, position = 5)
|
|
||||||
),
|
|
||||||
GAP_BETWEEN_STEPS_POSITIONS_ERROR_CODE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun withGroupInformation(steps: MutableSet<RecipeStep>? = null, test: RecipeGroupInformation.() -> Unit) {
|
|
||||||
recipeGroupInformation(
|
|
||||||
group = group(id = 0L),
|
|
||||||
steps = steps ?: mutableSetOf(
|
|
||||||
recipeStep(id = 0L, position = 1),
|
|
||||||
recipeStep(id = 1L, position = 2),
|
|
||||||
recipeStep(id = 2L, position = 3),
|
|
||||||
recipeStep(id = 3L, position = 4)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
test()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertInvalidStepsPositionsException(steps: MutableSet<RecipeStep>, errorType: String) {
|
|
||||||
val exception = assertThrows<InvalidStepsPositionsException> {
|
|
||||||
logic.validateSteps(steps)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue { exception.errors.size == 1 }
|
|
||||||
assertTrue { exception.errors.first().type == errorType }
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue