feature/#25-dtos #28
|
@ -0,0 +1,9 @@
|
|||
package dev.fyloz.colorrecipesexplorer.dtos
|
||||
|
||||
data class MixTypeDto(
|
||||
override val id: Long = 0L,
|
||||
|
||||
val name: String,
|
||||
|
||||
val material: MaterialDto
|
||||
) : EntityDto
|
|
@ -49,13 +49,13 @@ class DefaultMixLogic(
|
|||
override fun save(entity: MixSaveDto): Mix {
|
||||
val recipe = recipeLogic.getById(entity.recipeId)
|
||||
val materialType = materialTypeLogic.getById(entity.materialTypeId)
|
||||
val mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(entity.name, materialType(materialType))
|
||||
val mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
val mixMaterials =
|
||||
if (entity.mixMaterials != null) mixMaterialLogic.saveAll(entity.mixMaterials).toSet() else setOf()
|
||||
mixMaterialLogic.validateMixMaterials(mixMaterials)
|
||||
|
||||
var mix = mix(recipe = recipe, mixType = mixType, mixMaterials = mixMaterials.map(::mixMaterial).toMutableSet())
|
||||
var mix = mix(recipe = recipe, mixType = mixType(mixType), mixMaterials = mixMaterials.map(::mixMaterial).toMutableSet())
|
||||
mix = save(mix)
|
||||
|
||||
recipeLogic.addMix(recipe, mix)
|
||||
|
@ -74,9 +74,9 @@ class DefaultMixLogic(
|
|||
mix.mixType.material.materialType!!
|
||||
|
||||
mix.mixType = if (mixTypeIsShared(mix.mixType)) {
|
||||
mixTypeLogic.saveForNameAndMaterialType(name, materialType)
|
||||
mixType(mixTypeLogic.saveForNameAndMaterialType(name, materialTypeDto(materialType)))
|
||||
} else {
|
||||
mixTypeLogic.updateForNameAndMaterialType(mix.mixType, name, materialType)
|
||||
mixType(mixTypeLogic.updateForNameAndMaterialType(mixTypeDto(mix.mixType), name, materialTypeDto(materialType)))
|
||||
}
|
||||
}
|
||||
if (entity.mixMaterials != null) {
|
||||
|
|
|
@ -1,84 +1,58 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MixTypeRepository
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Service
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.MixType
|
||||
import dev.fyloz.colorrecipesexplorer.service.MixTypeService
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
interface MixTypeLogic : NamedModelService<MixType, MixTypeRepository> {
|
||||
/** Checks if a [MixType] with the given [name] and [materialType] exists. */
|
||||
fun existsByNameAndMaterialType(name: String, materialType: MaterialType): Boolean
|
||||
|
||||
/** Gets the mix type with the given [material]. */
|
||||
fun getByMaterial(material: Material): MixType
|
||||
|
||||
/** Gets the [MixType] with the given [name] and [materialType]. */
|
||||
fun getByNameAndMaterialType(name: String, materialType: MaterialType): MixType
|
||||
|
||||
/** Returns a [MixType] for the given [name] and [materialType]. If a mix type with these does not already exists, it will be created. */
|
||||
fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialType): MixType
|
||||
interface MixTypeLogic : Logic<MixTypeDto, MixTypeService> {
|
||||
/** Returns a [MixType] for the given [name] and [materialType]. If this mix type does not already exist, it will be created. */
|
||||
fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialTypeDto): MixTypeDto
|
||||
|
||||
/** Returns a new and persisted [MixType] with the given [name] and [materialType]. */
|
||||
fun saveForNameAndMaterialType(name: String, materialType: MaterialType): MixType
|
||||
fun saveForNameAndMaterialType(name: String, materialType: MaterialTypeDto): MixTypeDto
|
||||
|
||||
/** Returns the given [mixType] updated with the given [name] and [materialType]. */
|
||||
fun updateForNameAndMaterialType(mixType: MixType, name: String, materialType: MaterialType): MixType
|
||||
fun updateForNameAndMaterialType(mixType: MixTypeDto, name: String, materialType: MaterialTypeDto): MixTypeDto
|
||||
}
|
||||
|
||||
@Service
|
||||
@RequireDatabase
|
||||
class DefaultMixTypeLogic(
|
||||
mixTypeRepository: MixTypeRepository,
|
||||
@Lazy val materialLogic: MaterialLogic
|
||||
) :
|
||||
AbstractNamedModelService<MixType, MixTypeRepository>(mixTypeRepository), MixTypeLogic {
|
||||
override fun idNotFoundException(id: Long) = mixTypeIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = mixTypeIdAlreadyExistsException(id)
|
||||
override fun nameNotFoundException(name: String) = mixTypeNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = mixTypeNameAlreadyExistsException(name)
|
||||
@LogicComponent
|
||||
class DefaultMixTypeLogic(service: MixTypeService, private val materialLogic: MaterialLogic) :
|
||||
BaseLogic<MixTypeDto, MixTypeService>(service, MixType::class.simpleName!!), MixTypeLogic {
|
||||
override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialTypeDto) =
|
||||
service.getByNameAndMaterialType(name, materialType.id) ?: saveForNameAndMaterialType(name, materialType)
|
||||
|
||||
override fun existsByNameAndMaterialType(name: String, materialType: MaterialType): Boolean =
|
||||
repository.existsByNameAndMaterialType(name, materialType)
|
||||
|
||||
override fun getByMaterial(material: Material): MixType =
|
||||
repository.findByMaterial(material) ?: throw nameNotFoundException(material.name)
|
||||
|
||||
override fun getByNameAndMaterialType(name: String, materialType: MaterialType): MixType =
|
||||
repository.findByNameAndMaterialType(name, materialType)
|
||||
?: throw MixTypeNameAndMaterialTypeNotFoundException(name, materialType)
|
||||
|
||||
override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialType): MixType =
|
||||
if (existsByNameAndMaterialType(name, materialType))
|
||||
getByNameAndMaterialType(name, materialType)
|
||||
else
|
||||
saveForNameAndMaterialType(name, materialType)
|
||||
|
||||
override fun save(entity: MixType): MixType {
|
||||
if (materialLogic.existsByName(entity.name))
|
||||
throw AlreadyExistsException("material", "material already exists", "material already exists details (TODO)", entity.name) // TODO
|
||||
return super.save(entity)
|
||||
}
|
||||
|
||||
override fun saveForNameAndMaterialType(name: String, materialType: MaterialType): MixType =
|
||||
save(
|
||||
mixType(
|
||||
@Transactional
|
||||
override fun saveForNameAndMaterialType(name: String, materialType: MaterialTypeDto): MixTypeDto {
|
||||
val material = materialLogic.save(
|
||||
MaterialDto(
|
||||
name = name,
|
||||
material = material(
|
||||
name = name,
|
||||
inventoryQuantity = Float.MIN_VALUE,
|
||||
isMixType = true,
|
||||
materialType = materialType
|
||||
)
|
||||
inventoryQuantity = Float.MIN_VALUE,
|
||||
isMixType = true,
|
||||
materialType = materialType
|
||||
)
|
||||
)
|
||||
|
||||
override fun updateForNameAndMaterialType(mixType: MixType, name: String, materialType: MaterialType): MixType =
|
||||
update(mixType.copy(material = mixType.material.copy(name = name, materialType = materialType)))
|
||||
|
||||
override fun delete(entity: MixType) {
|
||||
if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMixTypeException(entity)
|
||||
super.delete(entity)
|
||||
return save(MixTypeDto(name = name, material = material))
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateForNameAndMaterialType(
|
||||
mixType: MixTypeDto,
|
||||
name: String,
|
||||
materialType: MaterialTypeDto
|
||||
): MixTypeDto {
|
||||
val material = materialLogic.update(mixType.material.copy(name = name, materialType = materialType))
|
||||
return update(mixType.copy(name = name, material = material))
|
||||
}
|
||||
|
||||
override fun deleteById(id: Long) {
|
||||
if (service.isUsedByMixes(id)) {
|
||||
throw cannotDeleteException("Cannot delete the mix type with the id '$id' because one or more mixes depends on it")
|
||||
}
|
||||
|
||||
super.deleteById(id)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package dev.fyloz.colorrecipesexplorer.model
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
|
@ -10,91 +11,42 @@ import javax.persistence.*
|
|||
@Entity
|
||||
@Table(name = "mix_type")
|
||||
data class MixType(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
|
||||
@Column(unique = true)
|
||||
override var name: String,
|
||||
@Column(unique = true)
|
||||
val name: String,
|
||||
|
||||
@OneToOne(cascade = [CascadeType.ALL])
|
||||
@JoinColumn(name = "material_id")
|
||||
var material: Material
|
||||
) : NamedModelEntity
|
||||
@OneToOne(cascade = [CascadeType.ALL])
|
||||
@JoinColumn(name = "material_id")
|
||||
var material: Material
|
||||
) : ModelEntity
|
||||
|
||||
// ==== DSL ====
|
||||
fun mixType(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
material: Material = material(),
|
||||
op: MixType.() -> Unit = {}
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
material: Material = material(),
|
||||
op: MixType.() -> Unit = {}
|
||||
) = MixType(id, name, material).apply(op)
|
||||
|
||||
fun mixType(
|
||||
name: String = "name",
|
||||
materialType: MaterialType = materialType(),
|
||||
op: MixType.() -> Unit = {}
|
||||
name: String = "name",
|
||||
materialType: MaterialType = materialType(),
|
||||
op: MixType.() -> Unit = {}
|
||||
) = mixType(
|
||||
id = null,
|
||||
name,
|
||||
material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType)
|
||||
id = null,
|
||||
name,
|
||||
material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType)
|
||||
).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
private const val MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE = "Mix type not found"
|
||||
private const val MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE = "Mix type already exists"
|
||||
private const val MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix type"
|
||||
private const val MIX_TYPE_EXCEPTION_ERROR_CODE = "mixtype"
|
||||
@Deprecated("Temporary DSL for transition")
|
||||
fun mixTypeDto(
|
||||
entity: MixType
|
||||
) = MixTypeDto(entity.id!!, entity.name, materialDto(entity.material))
|
||||
|
||||
class MixTypeNameAndMaterialTypeNotFoundException(name: String, materialType: MaterialType) :
|
||||
RestException(
|
||||
"notfound-mixtype-namematerialtype",
|
||||
MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
HttpStatus.NOT_FOUND,
|
||||
"A mix type with the name $name and material type ${materialType.name} could not be found",
|
||||
mapOf(
|
||||
"name" to name,
|
||||
"materialType" to materialType.name
|
||||
)
|
||||
)
|
||||
|
||||
fun mixTypeIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
MIX_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix type with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
|
||||
fun mixTypeIdAlreadyExistsException(id: Long) =
|
||||
AlreadyExistsException(
|
||||
MIX_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix type with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
||||
fun mixTypeNameNotFoundException(name: String) =
|
||||
NotFoundException(
|
||||
MIX_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix type with the name $name could not be found",
|
||||
name,
|
||||
"name"
|
||||
)
|
||||
|
||||
fun mixTypeNameAlreadyExistsException(name: String) =
|
||||
AlreadyExistsException(
|
||||
MIX_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix type with the name $name already exists",
|
||||
name,
|
||||
"name"
|
||||
)
|
||||
|
||||
fun cannotDeleteMixTypeException(mixType: MixType) =
|
||||
CannotDeleteException(
|
||||
MIX_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE,
|
||||
"Cannot delete the mix type ${mixType.name} because one or more mixes depends on it"
|
||||
)
|
||||
@Deprecated("Temporary DSL for transition")
|
||||
fun mixType(
|
||||
dto: MixTypeDto
|
||||
) = MixType(dto.id, dto.name, material(dto.material))
|
|
@ -1,30 +1,27 @@
|
|||
package dev.fyloz.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.model.Material
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.colorrecipesexplorer.model.MixType
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface MixTypeRepository : NamedJpaRepository<MixType> {
|
||||
@Query("select case when(count(m) > 0) then true else false end from MixType m where m.name = :name and m.material.materialType = :materialType")
|
||||
fun existsByNameAndMaterialType(name: String, materialType: MaterialType): Boolean
|
||||
interface MixTypeRepository : JpaRepository<MixType, Long> {
|
||||
/** Checks if a mix type with the given [name], [materialTypeId] and a different [id] exists. */
|
||||
@Query("select case when(count(m) > 0) then true else false end from MixType m where m.name = :name and m.material.materialType.id = :materialTypeId and m.id <> :id")
|
||||
fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long): Boolean
|
||||
|
||||
/** Gets the mix type with the given [material]. */
|
||||
fun findByMaterial(material: Material): MixType?
|
||||
|
||||
/** Gets the [MixType] with the given [name] and [materialType]. */
|
||||
@Query("select m from MixType m where m.name = :name and m.material.materialType = :materialType")
|
||||
fun findByNameAndMaterialType(name: String, materialType: MaterialType): MixType?
|
||||
/** Finds the mix type with the given [name] and [materialTypeId]. */
|
||||
@Query("select m from MixType m where m.name = :name and m.material.materialType.id = :materialTypeId")
|
||||
fun findByNameAndMaterialType(name: String, materialTypeId: Long): MixType?
|
||||
|
||||
/** Checks if a mix depends on the mix type with the given [id]. */
|
||||
@Query(
|
||||
"""
|
||||
"""
|
||||
select case when(count(m.id) > 0) then false else true end
|
||||
from MixType t
|
||||
left join Mix m on t.id = m.mixType.id
|
||||
where t.id = :id
|
||||
"""
|
||||
from Mix m where m.mixType.id = :id
|
||||
"""
|
||||
)
|
||||
fun canBeDeleted(id: Long): Boolean
|
||||
fun isUsedByMixes(id: Long): Boolean
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.MixType
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MixTypeRepository
|
||||
|
||||
interface MixTypeService : Service<MixTypeDto, MixType, MixTypeRepository> {
|
||||
/** Checks if a mix type with the given [name], [materialTypeId] and a different [id] exists. */
|
||||
fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long? = null): Boolean
|
||||
|
||||
/** Finds the mix type with the given [name] and [materialTypeId]. */
|
||||
fun getByNameAndMaterialType(name: String, materialTypeId: Long): MixTypeDto?
|
||||
|
||||
/** Checks if a mix depends on the mix type with the given [id]. */
|
||||
fun isUsedByMixes(id: Long): Boolean
|
||||
}
|
||||
|
||||
@ServiceComponent
|
||||
class DefaultMixTypeService(repository: MixTypeRepository, val materialService: MaterialService) :
|
||||
BaseService<MixTypeDto, MixType, MixTypeRepository>(repository), MixTypeService {
|
||||
override fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long?) =
|
||||
repository.existsByNameAndMaterialType(name, materialTypeId, id ?: 0L)
|
||||
|
||||
override fun getByNameAndMaterialType(name: String, materialTypeId: Long) =
|
||||
repository.findByNameAndMaterialType(name, materialTypeId)?.let(::toDto)
|
||||
|
||||
override fun isUsedByMixes(id: Long) =
|
||||
repository.isUsedByMixes(id)
|
||||
|
||||
override fun toDto(entity: MixType) =
|
||||
MixTypeDto(entity.id!!, entity.name, materialService.toDto(entity.material))
|
||||
|
||||
override fun toEntity(dto: MixTypeDto) =
|
||||
MixType(dto.id, dto.name, materialService.toEntity(dto.material))
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.service.MixTypeService
|
||||
import io.mockk.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DefaultMixTypeLogicTest {
|
||||
private val mixTypeServiceMock = mockk<MixTypeService>()
|
||||
private val materialLogicMock = mockk<MaterialLogic>()
|
||||
|
||||
private val mixTypeLogic = spyk(DefaultMixTypeLogic(mixTypeServiceMock, materialLogicMock))
|
||||
|
||||
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
||||
private val material = MaterialDto(1L, "Unit test material", 1000f, true, materialType)
|
||||
private val mixType = MixTypeDto(id = 1L, name = "Unit test mix type", material)
|
||||
|
||||
@AfterEach
|
||||
fun afterEach() {
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getOrCreateForNameAndMaterialType_normalBehavior_returnsFromService() {
|
||||
// Arrange
|
||||
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns mixType
|
||||
|
||||
// Act
|
||||
val actualMixType = mixTypeLogic.getOrCreateForNameAndMaterialType(mixType.name, materialType)
|
||||
|
||||
// Assert
|
||||
assertEquals(mixType, actualMixType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getOrCreateForNameAndMaterialType_notFound_returnsFromSaveForNameAndMaterialType() {
|
||||
// Arrange
|
||||
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns null
|
||||
every { mixTypeLogic.saveForNameAndMaterialType(any(), any()) } returns mixType
|
||||
|
||||
// Act
|
||||
val actualMixType = mixTypeLogic.getOrCreateForNameAndMaterialType(mixType.name, materialType)
|
||||
|
||||
// Assert
|
||||
assertEquals(mixType, actualMixType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveForNameAndMaterialType_normalBehavior_callsSavesInMaterialLogic() {
|
||||
// Arrange
|
||||
every { materialLogicMock.save(any<MaterialDto>()) } returnsArgument 0
|
||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
mixTypeLogic.saveForNameAndMaterialType(mixType.name, materialType)
|
||||
|
||||
// Assert
|
||||
verify {
|
||||
materialLogicMock.save(match<MaterialDto> { it.name == mixType.name && it.materialType == materialType })
|
||||
}
|
||||
confirmVerified(materialLogicMock)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveForNameAndMaterialType_normalBehavior_callsSave() {
|
||||
// Arrange
|
||||
every { materialLogicMock.save(any<MaterialDto>()) } returnsArgument 0
|
||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
mixTypeLogic.saveForNameAndMaterialType(mixType.name, materialType)
|
||||
|
||||
// Assert
|
||||
verify {
|
||||
mixTypeLogic.save(match { it.name == mixType.name && it.material.name == mixType.name && it.material.materialType == materialType })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateForNameAndMaterialType_normalBehavior_callsSavesInMaterialLogic() {
|
||||
// Arrange
|
||||
val updatedName = mixType.name + " updated"
|
||||
val updatedMaterialType = materialType.copy(id = 2L)
|
||||
|
||||
every { materialLogicMock.update(any<MaterialDto>()) } returnsArgument 0
|
||||
every { mixTypeLogic.update(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
mixTypeLogic.updateForNameAndMaterialType(mixType, updatedName, updatedMaterialType)
|
||||
|
||||
// Assert
|
||||
verify {
|
||||
materialLogicMock.update(match<MaterialDto> { it.name == updatedName && it.materialType == updatedMaterialType })
|
||||
}
|
||||
confirmVerified(materialLogicMock)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateForNameAndMaterialType_normalBehavior_callsSave() {
|
||||
// Arrange
|
||||
val updatedName = mixType.name + " updated"
|
||||
val updatedMaterialType = materialType.copy(id = 2L)
|
||||
|
||||
every { materialLogicMock.update(any<MaterialDto>()) } returnsArgument 0
|
||||
every { mixTypeLogic.update(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
mixTypeLogic.updateForNameAndMaterialType(mixType, updatedName, updatedMaterialType)
|
||||
|
||||
// Assert
|
||||
verify {
|
||||
mixTypeLogic.update(match { it.name == updatedName && it.material.name == updatedName && it.material.materialType == updatedMaterialType })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteById_usedByMix_throwsCannotDeleteException() {
|
||||
// Arrange
|
||||
every { mixTypeServiceMock.isUsedByMixes(any()) } returns true
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<CannotDeleteException> { mixTypeLogic.deleteById(mixType.id) }
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MixTypeRepository
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class MixTypeLogicTest : AbstractNamedModelServiceTest<MixType, MixTypeLogic, MixTypeRepository>() {
|
||||
override val repository: MixTypeRepository = mock()
|
||||
private val materialService: MaterialLogic = mock()
|
||||
override val logic: MixTypeLogic = spy(DefaultMixTypeLogic(repository, materialService))
|
||||
|
||||
private val materialType: MaterialType = materialType()
|
||||
private val material: Material = material(id = 0L, materialType = materialType)
|
||||
override val entity: MixType = mixType(id = 0L, name = "mix type", material = material)
|
||||
override val anotherEntity: MixType = mixType(id = 1L, name = "another mix type")
|
||||
override val entityWithEntityName: MixType = mixType(id = 2L, name = entity.name)
|
||||
|
||||
@AfterEach
|
||||
override fun afterEach() {
|
||||
reset(materialService)
|
||||
super.afterEach()
|
||||
}
|
||||
|
||||
// existsByNameAndMaterialType
|
||||
|
||||
@Test
|
||||
fun `existsByNameAndMaterialType() returns repository's answer`() {
|
||||
setOf(true, false).forEach {
|
||||
whenever(repository.existsByNameAndMaterialType(entity.name, materialType)).doReturn(it)
|
||||
|
||||
val found = logic.existsByNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
assertTrue { found == it }
|
||||
}
|
||||
}
|
||||
|
||||
// getByMaterial()
|
||||
|
||||
@Test
|
||||
fun `getByMaterial() returns the mix type with the given material`() {
|
||||
whenever(repository.findByMaterial(material)).doReturn(entity)
|
||||
|
||||
val found = logic.getByMaterial(material)
|
||||
|
||||
assertEquals(entity, found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getByMaterial() throws NotFoundException when no mix type with the given material exists`() {
|
||||
whenever(repository.findByMaterial(material)).doReturn(null)
|
||||
|
||||
assertThrows<NotFoundException> { logic.getByMaterial(material) }
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
// getByNameAndMaterialType()
|
||||
|
||||
@Test
|
||||
fun `getByNameAndMaterialType() returns the mix type with the given name and material type`() {
|
||||
whenever(repository.findByNameAndMaterialType(entity.name, materialType)).doReturn(entity)
|
||||
|
||||
val found = logic.getByNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
assertEquals(entity, found)
|
||||
}
|
||||
|
||||
// getOrCreateForNameAndMaterialType()
|
||||
@Test
|
||||
fun `getOrCreateForNameAndMaterialType() calls getForNameAndMaterialType() when a mix type with the given name and material type exists`() {
|
||||
doReturn(true).whenever(logic).existsByNameAndMaterialType(entity.name, materialType)
|
||||
doReturn(entity).whenever(logic).getByNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
val found = logic.getOrCreateForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
verify(logic).getByNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
assertEquals(entity, found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getOrCreateForNameAndMaterialType() calls saveForNameAndMaterialType() when no mix type with the given name and material type exists`() {
|
||||
doReturn(false).whenever(logic).existsByNameAndMaterialType(entity.name, materialType)
|
||||
doReturn(entity).whenever(logic).saveForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
val found = logic.getOrCreateForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
verify(logic).saveForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
assertEquals(entity, found)
|
||||
}
|
||||
|
||||
// save()
|
||||
|
||||
@Test
|
||||
fun `save() throws AlreadyExistsException when a material with the name of the new mix type exists`() {
|
||||
whenever(materialService.existsByName(entity.name)).doReturn(true)
|
||||
|
||||
assertThrows<AlreadyExistsException> { logic.save(entity) }
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
// saveForNameAndMaterialType()
|
||||
|
||||
@Test
|
||||
fun `saveForNameAndMaterialType() creates a save a valid mix type with the given name and material type`() {
|
||||
val name = entity.name
|
||||
val materialType = materialType()
|
||||
|
||||
doAnswer { it.arguments[0] }.whenever(logic).save(any())
|
||||
|
||||
val found = logic.saveForNameAndMaterialType(name, materialType)
|
||||
|
||||
verify(logic).save(any())
|
||||
|
||||
assertEquals(name, found.name)
|
||||
assertEquals(name, found.material.name)
|
||||
assertEquals(materialType, found.material.materialType)
|
||||
assertTrue(found.material.isMixType)
|
||||
}
|
||||
|
||||
// updateForNameAndMaterialType()
|
||||
|
||||
@Test
|
||||
fun `updateForNameAndMaterialType() updates the given mix type with the given name and material type`() {
|
||||
val mixType = mixType(id = 1L, material = material(isMixType = true))
|
||||
val name = entity.name
|
||||
val materialType = materialType()
|
||||
|
||||
doAnswer { it.arguments[0] }.whenever(logic).update(any())
|
||||
|
||||
val found = logic.updateForNameAndMaterialType(mixType, name, materialType)
|
||||
|
||||
verify(logic).update(any())
|
||||
|
||||
assertEquals(mixType.id, found.id)
|
||||
assertEquals(name, found.name)
|
||||
assertEquals(name, found.material.name)
|
||||
assertEquals(materialType, found.material.materialType)
|
||||
assertTrue(found.material.isMixType)
|
||||
}
|
||||
|
||||
// delete()
|
||||
|
||||
override fun `delete() deletes in the repository`() {
|
||||
whenCanBeDeleted {
|
||||
super.`delete() deletes in the repository`()
|
||||
}
|
||||
}
|
||||
|
||||
// deleteById()
|
||||
|
||||
override fun `deleteById() deletes the entity with the given id in the repository`() {
|
||||
whenCanBeDeleted {
|
||||
super.`deleteById() deletes the entity with the given id in the repository`()
|
||||
}
|
||||
}
|
||||
|
||||
private fun whenCanBeDeleted(id: Long = any(), test: () -> Unit) {
|
||||
whenever(repository.canBeDeleted(id)).doReturn(true)
|
||||
|
||||
test()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue