From d785cfdbe764c60048674e7ef23ed6bf9a686b9e Mon Sep 17 00:00:00 2001 From: FyloZ Date: Tue, 1 Mar 2022 22:47:15 -0500 Subject: [PATCH] #25 Migrate material types to new logic --- .../fyloz/colorrecipesexplorer/Constants.kt | 9 +- .../initializers/MaterialTypeInitializer.kt | 23 +- .../properties/MaterialTypeProperties.kt | 5 +- .../colorrecipesexplorer/dtos/MaterialDto.kt | 3 +- .../dtos/MaterialTypeDto.kt | 13 + .../fyloz/colorrecipesexplorer/logic/Logic.kt | 4 +- .../logic/MaterialTypeLogic.kt | 131 +++---- .../colorrecipesexplorer/logic/MixLogic.kt | 4 +- .../colorrecipesexplorer/model/Material.kt | 6 +- .../model/MaterialType.kt | 185 ++------- .../colorrecipesexplorer/model/Recipe.kt | 2 +- .../repository/MaterialRepository.kt | 2 +- .../repository/MaterialTypeRepository.kt | 35 +- .../rest/FileController.kt | 4 +- .../rest/MaterialController.kt | 4 +- .../rest/MaterialTypeController.kt | 35 +- .../service/MaterialService.kt | 20 +- .../service/MaterialTypeService.kt | 44 +++ .../colorrecipesexplorer/service/Service.kt | 9 +- .../logic/DefaultMaterialLogicTest.kt | 9 +- .../logic/DefaultMaterialTypeLogicTest.kt | 146 +++++++ .../logic/MaterialTypeLogicTest.kt | 364 +++++++++--------- .../logic/MixLogicTest.kt | 4 +- 23 files changed, 575 insertions(+), 486 deletions(-) create mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialTypeDto.kt create mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt create mode 100644 src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/DefaultMaterialTypeLogicTest.kt diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/Constants.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/Constants.kt index 6694fed..a1bf68c 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/Constants.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/Constants.kt @@ -2,13 +2,14 @@ package dev.fyloz.colorrecipesexplorer object Constants { object ControllerPaths { - const val file = "/api/file" - const val material = "/api/material" + const val FILE = "/api/file" + const val MATERIAL = "/api/material" + const val MATERIAL_TYPE = "/api/materialtype" } object FilePaths { - const val pdfs = "pdf" + const val PDF = "pdf" - const val simdut = "$pdfs/simdut" + const val SIMDUT = "$PDF/simdut" } } \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/initializers/MaterialTypeInitializer.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/initializers/MaterialTypeInitializer.kt index 99f0a3f..8431c25 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/initializers/MaterialTypeInitializer.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/initializers/MaterialTypeInitializer.kt @@ -2,8 +2,8 @@ package dev.fyloz.colorrecipesexplorer.config.initializers import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto import dev.fyloz.colorrecipesexplorer.logic.MaterialTypeLogic -import dev.fyloz.colorrecipesexplorer.model.MaterialType import mu.KotlinLogging import org.springframework.context.annotation.Configuration @@ -23,17 +23,16 @@ class MaterialTypeInitializer( private fun ensureSystemMaterialTypesExists() { val systemTypes = materialTypeProperties.systemTypes.map { it.toMaterialType() } - val oldSystemTypes = materialTypeLogic.getAllSystemTypes().toMutableSet() + val oldSystemTypes = materialTypeLogic.getAll(true).toMutableSet() - fun saveOrUpdateSystemType(type: MaterialType) { - if (materialTypeLogic.existsByName(type.name)) { - with(materialTypeLogic.getByName(type.name)) { - if (!this.systemType) { - logger.info("Material type '${type.name}' already exists and will be flagged as a system type") - materialTypeLogic.update(this.copy(systemType = true)) - } else { - logger.debug("System material type '${type.name}' already exists") - } + fun saveOrUpdateSystemType(type: MaterialTypeDto) { + val storedMaterialType = materialTypeLogic.getByName(type.name) + if (storedMaterialType != null) { + if (!storedMaterialType.systemType) { + logger.info("Material type '${type.name}' already exists and will be flagged as a system type") + materialTypeLogic.update(storedMaterialType.copy(systemType = true)) + } else { + logger.debug("System material type '${type.name}' already exists") } } else { logger.info("System material type '${type.name}' will be created") @@ -50,7 +49,7 @@ class MaterialTypeInitializer( // Remove old system types oldSystemTypes.forEach { logger.info("Material type '${it.name}' is not a system type anymore") - materialTypeLogic.updateSystemType(it.copy(systemType = false)) + materialTypeLogic.updateNonSystemType(it.copy(systemType = false)) } } } \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt index 226e9d3..736cd91 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.config.properties +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto import dev.fyloz.colorrecipesexplorer.model.MaterialType import dev.fyloz.colorrecipesexplorer.model.materialType import org.springframework.boot.context.properties.ConfigurationProperties @@ -16,9 +17,9 @@ class MaterialTypeProperties { var prefix: String = "", var usePercentages: Boolean = false ) { - fun toMaterialType(): MaterialType { + fun toMaterialType(): MaterialTypeDto { Assert.hasText(name, "A system material type has an empty name") - return materialType(name = name, prefix = prefix, usePercentages = usePercentages, systemType = true) + return MaterialTypeDto(name = name, prefix = prefix, usePercentages = usePercentages, systemType = true) } } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialDto.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialDto.kt index 45c6b15..251d71b 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialDto.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialDto.kt @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.dtos -import dev.fyloz.colorrecipesexplorer.model.MaterialType import org.springframework.web.multipart.MultipartFile import javax.validation.constraints.Min import javax.validation.constraints.NotBlank @@ -14,7 +13,7 @@ data class MaterialDto( val isMixType: Boolean, - val materialType: MaterialType, + val materialType: MaterialTypeDto, val simdutUrl: String? = null ) : EntityDto diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialTypeDto.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialTypeDto.kt new file mode 100644 index 0000000..eb2b197 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/dtos/MaterialTypeDto.kt @@ -0,0 +1,13 @@ +package dev.fyloz.colorrecipesexplorer.dtos + +data class MaterialTypeDto( + override val id: Long = 0L, + + val name: String, + + val prefix: String, + + val usePercentages: Boolean, + + val systemType: Boolean = false +) : EntityDto \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/Logic.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/Logic.kt index 3578c0f..4b1971e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/Logic.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/Logic.kt @@ -19,13 +19,13 @@ interface Logic> { /** Get all DTOs. */ fun getAll(): Collection - /** Get the DTO for the given [id]. */ + /** Get the DTO for the given [id]. Throws if no DTO were found. */ fun getById(id: Long): D /** Saves the given [dto]. */ fun save(dto: D): D - /** Updates the given [dto]. */ + /** Updates the given [dto]. Throws if no DTO with the same id exists. */ fun update(dto: D): D /** Deletes the dto with the given [id]. */ diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogic.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogic.kt index 833afc6..e4d5c46 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogic.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogic.kt @@ -1,88 +1,75 @@ package dev.fyloz.colorrecipesexplorer.logic -import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase -import dev.fyloz.colorrecipesexplorer.model.* -import dev.fyloz.colorrecipesexplorer.model.validation.isNotNullAndNotBlank -import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository -import org.springframework.stereotype.Service +import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto +import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException +import dev.fyloz.colorrecipesexplorer.model.MaterialType +import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService -interface MaterialTypeLogic : - ExternalNamedModelService { - /** Checks if a material type with the given [prefix] exists. */ - fun existsByPrefix(prefix: String): Boolean +interface MaterialTypeLogic : Logic { + /** Gets all material types which are or not [systemType]s. */ + fun getAll(systemType: Boolean): Collection - /** Gets all system material types. */ - fun getAllSystemTypes(): Collection + /** Gets the material type with the given [name]. */ + fun getByName(name: String): MaterialTypeDto? - /** Gets all material types who are not a system type. */ - fun getAllNonSystemType(): Collection - - /** Allows to update the given system [materialType], should not be exposed to users. */ - fun updateSystemType(materialType: MaterialType): MaterialType + /** Updates the given [dto], and throws if it is a system types. */ + fun updateNonSystemType(dto: MaterialTypeDto) } -@Service -@RequireDatabase -class DefaultMaterialTypeLogic(repository: MaterialTypeRepository) : - AbstractExternalNamedModelService( - repository - ), MaterialTypeLogic { - override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id) - override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id) - override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name) - override fun nameAlreadyExistsException(name: String) = materialTypeNameAlreadyExistsException(name) +@LogicComponent +class DefaultMaterialTypeLogic(service: MaterialTypeService) : + BaseLogic(service, MaterialType::class.simpleName!!), MaterialTypeLogic { + override fun getAll(systemType: Boolean) = service.getAll(systemType) + override fun getByName(name: String) = service.getByName(name) - override fun MaterialType.toOutput() = this - - override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix) - - override fun getAllSystemTypes(): Collection = repository.findAllBySystemTypeIs(true) - override fun getAllNonSystemType(): Collection = repository.findAllBySystemTypeIs(false) - - override fun save(entity: MaterialType): MaterialType { - if (existsByPrefix(entity.prefix)) - throw materialTypePrefixAlreadyExistsException(entity.prefix) - return super.save(entity) - } - - override fun update(entity: MaterialTypeUpdateDto): MaterialType { - val persistedMaterialType by lazy { getById(entity.id) } - - return update(with(entity) { - MaterialType( - id = id, - name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name, - prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix, - systemType = false + override fun updateNonSystemType(dto: MaterialTypeDto) { + if (service.existsById(dto.id, true)) { + throw CannotUpdateException( + typeNameLowerCase, + "Cannot update $typeNameLowerCase", + "Cannot update material type '${dto.name}' because it is a system material type" ) - }) - } - - override fun updateSystemType(materialType: MaterialType) = - update(materialType, true) - - override fun update(entity: MaterialType) = - update(entity, false) - - private fun update(entity: MaterialType, allowSystemTypes: Boolean): MaterialType { - if (!allowSystemTypes && repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) { - throw cannotUpdateSystemMaterialTypeException(entity) } - with(repository.findByPrefix(entity.prefix)) { - if (this != null && id != entity.id) - throw materialTypePrefixAlreadyExistsException(entity.prefix) - } - - return super.update(entity) + update(dto) } - override fun delete(entity: MaterialType) { - if (repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) { - throw cannotDeleteSystemMaterialTypeException(entity) + override fun save(dto: MaterialTypeDto): MaterialTypeDto { + throwIfNameAlreadyExists(dto.name) + throwIfPrefixAlreadyExists(dto.prefix) + + return super.save(dto) + } + + override fun update(dto: MaterialTypeDto): MaterialTypeDto { + throwIfNameAlreadyExists(dto.name, dto.id) + throwIfPrefixAlreadyExists(dto.prefix, dto.id) + + return super.update(dto) + } + + override fun deleteById(id: Long) { + if (service.isUsedByMaterial(id)) { + throw cannotDeleteException("Cannot delete material type with the id '$id' because one or more materials depends on it") } - if (!repository.canBeDeleted(entity.id)) throw cannotDeleteMaterialTypeException(entity) - super.delete(entity) + super.deleteById(id) } -} + + private fun throwIfNameAlreadyExists(name: String, id: Long? = null) { + if (service.existsByName(name, id)) { + throw alreadyExistsException(value = name) + } + } + + private fun throwIfPrefixAlreadyExists(prefix: String, id: Long? = null) { + if (service.existsByPrefix(prefix, id)) { + throw alreadyExistsException(PREFIX_IDENTIFIER_NAME, prefix) + } + } + + companion object { + const val PREFIX_IDENTIFIER_NAME = "prefix" + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogic.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogic.kt index 8de0da0..8e843cf 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogic.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogic.kt @@ -53,7 +53,7 @@ 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) + val mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(entity.name, materialType(materialType)) val mixMaterials = if (entity.mixMaterials != null) mixMaterialLogic.create(entity.mixMaterials) else setOf() mixMaterialLogic.validateMixMaterials(mixMaterials) @@ -72,7 +72,7 @@ class DefaultMixLogic( if (entity.name != null || entity.materialTypeId != null) { val name = entity.name ?: mix.mixType.name val materialType = if (entity.materialTypeId != null) - materialTypeLogic.getById(entity.materialTypeId) + materialType(materialTypeLogic.getById(entity.materialTypeId)) else mix.mixType.material.materialType!! diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt index bab6b2c..02bfce6 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt @@ -32,7 +32,7 @@ data class Material( ) : ModelEntity { companion object { fun getSimdutFilePath(name: String) = - "${Constants.FilePaths.simdut}/$name.pdf" + "${Constants.FilePaths.SIMDUT}/$name.pdf" } } @@ -66,12 +66,12 @@ fun material( @Deprecated("Temporary DSL for transition") fun material( dto: MaterialDto -) = Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, dto.materialType) +) = Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialType(dto.materialType)) @Deprecated("Temporary DSL for transition") fun materialDto( entity: Material -) = MaterialDto(entity.id!!, entity.name, entity.inventoryQuantity, entity.isMixType, entity.materialType!!) +) = MaterialDto(entity.id!!, entity.name, entity.inventoryQuantity, entity.isMixType, materialTypeDto(entity.materialType!!)) fun materialQuantityDto( materialId: Long, diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt index a99033f..279c9c4 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt @@ -1,171 +1,62 @@ package dev.fyloz.colorrecipesexplorer.model -import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException -import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException -import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto import org.hibernate.annotations.ColumnDefault import javax.persistence.* -import javax.validation.constraints.NotBlank -import javax.validation.constraints.Size - -private const val VALIDATION_PREFIX_SIZE = "Must contains exactly 3 characters" @Entity @Table(name = "material_type") data class MaterialType( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long? = null, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long? = null, - @Column(unique = true) - override val name: String = "", + @Column(unique = true) + val name: String = "", - @Column(unique = true) - val prefix: String = "", + @Column(unique = true) + val prefix: String = "", - @Column(name = "use_percentages") - @ColumnDefault("false") - val usePercentages: Boolean = false, + @Column(name = "use_percentages") + @ColumnDefault("false") + val usePercentages: Boolean = false, - @Column(name = "system_type") - @ColumnDefault("false") - val systemType: Boolean = false -) : NamedModelEntity - -open class MaterialTypeSaveDto( - @field:NotBlank - val name: String, - - @field:NotBlank - @field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE) - val prefix: String, - - val usePercentages: Boolean = false -) : EntityDto { - override fun toEntity(): MaterialType = - MaterialType(null, name, prefix, usePercentages) -} - -open class MaterialTypeUpdateDto( - val id: Long, - - @field:NotBlank - val name: String?, - - @field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE) - val prefix: String? -) : EntityDto { - override fun toEntity(): MaterialType = - MaterialType(id, name ?: "", prefix ?: "") -} + @Column(name = "system_type") + @ColumnDefault("false") + val systemType: Boolean = false +) : ModelEntity // ==== DSL ==== fun materialType( - id: Long? = null, - name: String = "name", - prefix: String = "PRE", - usePercentages: Boolean = false, - systemType: Boolean = false, - op: MaterialType.() -> Unit = {} + id: Long? = null, + name: String = "name", + prefix: String = "PRE", + usePercentages: Boolean = false, + systemType: Boolean = false, + op: MaterialType.() -> Unit = {} ) = MaterialType(id, name, prefix, usePercentages, systemType).apply(op) fun materialType( - materialType: MaterialType, - newId: Long? = null, - newName: String? = null, - newSystemType: Boolean? = null + materialType: MaterialType, + newId: Long? = null, + newName: String? = null, + newSystemType: Boolean? = null ) = with(materialType) { MaterialType( - newId ?: id, - newName ?: name, - prefix, - usePercentages, - newSystemType ?: systemType + newId ?: id, + newName ?: name, + prefix, + usePercentages, + newSystemType ?: systemType ) } -fun materialTypeSaveDto( - name: String = "name", - prefix: String = "PRE", - usePercentages: Boolean = false, - op: MaterialTypeSaveDto.() -> Unit = {} -) = MaterialTypeSaveDto(name, prefix, usePercentages).apply(op) +@Deprecated("Temporary DSL for transition") +fun materialType( + dto: MaterialTypeDto +) = MaterialType(dto.id, dto.name, dto.prefix, dto.usePercentages, dto.systemType) -fun materialTypeUpdateDto( - id: Long = 0L, - name: String? = null, - prefix: String? = null, - op: MaterialTypeUpdateDto.() -> Unit = {} -) = MaterialTypeUpdateDto(id, name, prefix).apply(op) - -// ==== Exceptions ==== -private const val MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE = "Material type not found" -private const val MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE = "Material type already exists" -private const val MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete material type" -private const val MATERIAL_TYPE_CANNOT_UPDATE_EXCEPTION_TITLE = "Cannot update material type" -private const val MATERIAL_TYPE_EXCEPTION_ERROR_CODE = "materialtype" - -fun materialTypeIdNotFoundException(id: Long) = - NotFoundException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE, - "A material type with the id $id could not be found", - id - ) - -fun materialTypeNameNotFoundException(name: String) = - NotFoundException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE, - "A material type with the name $name could not be found", - name, - "name" - ) - -fun materialTypeIdAlreadyExistsException(id: Long) = - AlreadyExistsException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, - "A material type with the id $id already exists", - id - ) - -fun materialTypeNameAlreadyExistsException(name: String) = - AlreadyExistsException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, - "A material type with the name $name already exists", - name, - "name" - ) - -fun materialTypePrefixAlreadyExistsException(prefix: String) = - AlreadyExistsException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, - "A material type with the prefix $prefix already exists", - prefix, - "prefix" - ) - -fun cannotUpdateSystemMaterialTypeException(materialType: MaterialType) = - CannotUpdateException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_CANNOT_UPDATE_EXCEPTION_TITLE, - "Cannot update material type ${materialType.name} because it is a system material type" - ) - -fun cannotDeleteMaterialTypeException(materialType: MaterialType) = - CannotDeleteException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete material type ${materialType.name} because one or more materials depends on it" - ) - -fun cannotDeleteSystemMaterialTypeException(materialType: MaterialType) = - CannotDeleteException( - MATERIAL_TYPE_EXCEPTION_ERROR_CODE, - MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete material type ${materialType.name} because it is a system material type" - ) +@Deprecated("Temporary DSL for transition") +fun materialTypeDto( + entity: MaterialType +) = MaterialTypeDto(entity.id!!, entity.name, entity.prefix, entity.usePercentages, entity.systemType) \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt index dfdee70..b9cb4d3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt @@ -70,7 +70,7 @@ data class Recipe( groupsInformation.firstOrNull { it.group.id == groupId } fun imageUrl(deploymentUrl: String, name: String) = - "$deploymentUrl${Constants.ControllerPaths.file}?path=${ + "$deploymentUrl${Constants.ControllerPaths.FILE}?path=${ URLEncoder.encode( "${this.imagesDirectoryPath}/$name", StandardCharsets.UTF_8 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt index 5125087..2cc5ada 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt @@ -12,7 +12,7 @@ interface MaterialRepository : JpaRepository { fun existsByNameAndIdNot(name: String, id: Long): Boolean /** Gets all non mix type materials. */ - fun getAllByIsMixTypeIsFalse(): Collection + fun findAllByIsMixTypeIsFalse(): Collection /** Updates the [inventoryQuantity] of the [Material] with the given [id]. */ @Modifying diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt index d90b51e..c2d4a22 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt @@ -1,30 +1,33 @@ package dev.fyloz.colorrecipesexplorer.repository import dev.fyloz.colorrecipesexplorer.model.MaterialType +import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository @Repository -interface MaterialTypeRepository : NamedJpaRepository { - /** Checks if a material type exists with the given [prefix]. */ - fun existsByPrefix(prefix: String): Boolean - +interface MaterialTypeRepository : JpaRepository { /** Checks if a system material type with the given [id] exists. */ - fun existsByIdAndSystemTypeIsTrue(id: Long): Boolean + fun existsByIdAndSystemTypeIs(id: Long, systemType: Boolean): Boolean - /** Gets all material types which are not system types. */ - fun findAllBySystemTypeIs(value: Boolean): Collection + /** Checks if a material type with the given [name] and a different [id] exists. */ + fun existsByNameAndIdNot(name: String, id: Long): Boolean - /** Gets the material type with the given [prefix]. */ - fun findByPrefix(prefix: String): MaterialType? + /** Checks if a material type with the given [prefix] and a different [id] exists. */ + fun existsByPrefixAndIdNot(prefix: String, id: Long): Boolean + /** Find all material types which are or not [systemType]s. */ + fun findAllBySystemTypeIs(systemType: Boolean): Collection + + /** Find the material type with the given [name]. */ + fun findByName(name: String): MaterialType? + + /** Checks if a material depends on the material type with the given [id]. */ @Query( - """ - select case when(count(m.id) > 0) then false else true end - from MaterialType t - left join Material m on t.id = m.materialType.id - where t.id = :id - """ + """ + select case when(count(m) > 0) then true else false end + from Material m where m.materialType.id = :id + """ ) - fun canBeDeleted(id: Long): Boolean + fun isUsedByMaterial(id: Long): Boolean } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/FileController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/FileController.kt index 346b195..579bf39 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/FileController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/FileController.kt @@ -12,7 +12,7 @@ import org.springframework.web.multipart.MultipartFile import java.net.URI @RestController -@RequestMapping(Constants.ControllerPaths.file) +@RequestMapping(Constants.ControllerPaths.FILE) class FileController( private val fileLogic: WriteableFileLogic, private val configurationLogic: ConfigurationLogic @@ -43,6 +43,6 @@ class FileController( private fun created(path: String): ResponseEntity = ResponseEntity - .created(URI.create("${configurationLogic.get(ConfigurationType.INSTANCE_URL)}${Constants.ControllerPaths.file}?path=$path")) + .created(URI.create("${configurationLogic.get(ConfigurationType.INSTANCE_URL)}${Constants.ControllerPaths.FILE}?path=$path")) .build() } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt index eb29623..5674227 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt @@ -13,7 +13,7 @@ import org.springframework.web.multipart.MultipartFile import javax.validation.Valid @RestController -@RequestMapping(Constants.ControllerPaths.material) +@RequestMapping(Constants.ControllerPaths.MATERIAL) @Profile("!emergency") @PreAuthorizeViewCatalog class MaterialController( @@ -34,7 +34,7 @@ class MaterialController( @PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) @PreAuthorize("hasAuthority('EDIT_MATERIALS')") fun save(@Valid material: MaterialSaveDto, simdutFile: MultipartFile?) = - created(Constants.ControllerPaths.material) { + created(Constants.ControllerPaths.MATERIAL) { materialLogic.save(material.copy(simdutFile = simdutFile)) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt index f0d5e90..4e40cf6 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt @@ -1,49 +1,46 @@ package dev.fyloz.colorrecipesexplorer.rest +import dev.fyloz.colorrecipesexplorer.Constants import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewCatalog +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto import dev.fyloz.colorrecipesexplorer.logic.MaterialTypeLogic -import dev.fyloz.colorrecipesexplorer.model.MaterialType -import dev.fyloz.colorrecipesexplorer.model.MaterialTypeSaveDto -import dev.fyloz.colorrecipesexplorer.model.MaterialTypeUpdateDto import org.springframework.context.annotation.Profile import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.* import javax.validation.Valid -private const val MATERIAL_TYPE_CONTROLLER_PATH = "api/materialtype" - @RestController -@RequestMapping(MATERIAL_TYPE_CONTROLLER_PATH) +@RequestMapping(Constants.ControllerPaths.MATERIAL_TYPE) @Profile("!emergency") @PreAuthorizeViewCatalog class MaterialTypeController(private val materialTypeLogic: MaterialTypeLogic) { @GetMapping fun getAll() = - ok(materialTypeLogic.getAllForOutput()) + ok(materialTypeLogic.getAll()) @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(materialTypeLogic.getByIdForOutput(id)) + ok(materialTypeLogic.getById(id)) @PostMapping @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") - fun save(@Valid @RequestBody materialType: MaterialTypeSaveDto) = - created(MATERIAL_TYPE_CONTROLLER_PATH) { - materialTypeLogic.save(materialType) - } + fun save(@Valid @RequestBody materialType: MaterialTypeDto) = + created(Constants.ControllerPaths.MATERIAL_TYPE) { + materialTypeLogic.save(materialType) + } @PutMapping @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") - fun update(@Valid @RequestBody materialType: MaterialTypeUpdateDto) = - noContent { - materialTypeLogic.update(materialType) - } + fun update(@Valid @RequestBody materialType: MaterialTypeDto) = + noContent { + materialTypeLogic.updateNonSystemType(materialType) + } @DeleteMapping("{id}") @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") fun deleteById(@PathVariable id: Long) = - noContent { - materialTypeLogic.deleteById(id) - } + noContent { + materialTypeLogic.deleteById(id) + } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt index 1f3af3b..8930bf9 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt @@ -25,11 +25,17 @@ interface MaterialService : Service { } @ServiceComponent -class DefaultMaterialService(repository: MaterialRepository, @Qualifier("defaultFileLogic") val fileLogic: FileLogic) : +class DefaultMaterialService( + repository: MaterialRepository, + private val materialTypeService: MaterialTypeService, + @Qualifier("defaultFileLogic") val fileLogic: FileLogic +) : BaseService(repository), MaterialService { override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0) - override fun getAllNotMixType() = repository.getAllByIsMixTypeIsFalse().map(this::toDto) - override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) = repository.updateInventoryQuantityById(id, inventoryQuantity) + override fun getAllNotMixType() = repository.findAllByIsMixTypeIsFalse().map(::toDto) + override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) = + repository.updateInventoryQuantityById(id, inventoryQuantity) + override fun isUsedByMixMaterialOrMixType(id: Long) = repository.isUsedByMixMaterialOrMixType(id) override fun toDto(entity: Material) = @@ -38,21 +44,21 @@ class DefaultMaterialService(repository: MaterialRepository, @Qualifier("default entity.name, entity.inventoryQuantity, entity.isMixType, - entity.materialType!!, + materialTypeService.toDto(entity.materialType!!), getSimdutUrl(entity) ) override fun toEntity(dto: MaterialDto) = - Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, dto.materialType) + Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialTypeService.toEntity(dto.materialType)) private fun getSimdutUrl(material: Material): String? { - val filePath = "${Constants.FilePaths.simdut}/${material.name}.pdf" + val filePath = "${Constants.FilePaths.SIMDUT}/${material.name}.pdf" if (!fileLogic.exists(filePath)) { return null } val encodedPath = URLEncoder.encode(filePath, StandardCharsets.UTF_8) - return "${Constants.ControllerPaths.file}?path=$encodedPath" + return "${Constants.ControllerPaths.FILE}?path=$encodedPath" } } \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt new file mode 100644 index 0000000..74820c4 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt @@ -0,0 +1,44 @@ +package dev.fyloz.colorrecipesexplorer.service + +import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent +import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto +import dev.fyloz.colorrecipesexplorer.model.MaterialType +import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository + +interface MaterialTypeService : Service { + /** Checks if a system material type with the given [id] exists. */ + fun existsById(id: Long, systemType: Boolean): Boolean + + /** Checks if a material type with the given [name] and a different [id] exists. */ + fun existsByName(name: String, id: Long?): Boolean + + /** Checks if a material type with the given [prefix] and a different [id] exists. */ + fun existsByPrefix(prefix: String, id: Long?): Boolean + + /** Gets all material types which are or not a [systemType]. */ + fun getAll(systemType: Boolean): Collection + + /** Gets the material type with the given [name]. */ + fun getByName(name: String): MaterialTypeDto? + + /** Checks if a material depends on the material type with the given [id]. */ + fun isUsedByMaterial(id: Long): Boolean +} + +@ServiceComponent +class DefaultMaterialTypeService(repository: MaterialTypeRepository) : + BaseService(repository), MaterialTypeService { + override fun existsById(id: Long, systemType: Boolean) = repository.existsByIdAndSystemTypeIs(id, systemType) + override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0) + override fun existsByPrefix(prefix: String, id: Long?) = repository.existsByPrefixAndIdNot(prefix, id ?: 0) + override fun getAll(systemType: Boolean) = repository.findAllBySystemTypeIs(systemType).map(::toDto) + override fun getByName(name: String) = repository.findByName(name)?.let(::toDto) + + override fun isUsedByMaterial(id: Long) = repository.isUsedByMaterial(id) + + override fun toDto(entity: MaterialType) = + MaterialTypeDto(entity.id!!, entity.name, entity.prefix, entity.usePercentages, entity.systemType) + + override fun toEntity(dto: MaterialTypeDto) = + MaterialType(dto.id, dto.name, dto.prefix, dto.usePercentages, dto.systemType) +} \ No newline at end of file diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt index bfcc7c6..cfd846a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt @@ -31,6 +31,12 @@ interface Service> { /** Deletes the entity with the given [id]. */ fun deleteById(id: Long) + + /** Converts the given [entity] to a DTO. */ + fun toDto(entity: E): D + + /** Converts the given [dto] to an entity. */ + fun toEntity(dto: D): E } abstract class BaseService>(protected val repository: R) : @@ -58,7 +64,4 @@ abstract class BaseService() + + private val materialTypeLogic = spyk(DefaultMaterialTypeLogic(materialTypeServiceMock)) + + private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false) + private val systemMaterialType = + MaterialTypeDto(2L, "Unit test system material type", "UTSMT", false, systemType = true) + + @AfterEach + internal fun afterEach() { + clearAllMocks() + } + + @Test + fun getAll_normalBehavior_returnsFromService() { + // Arrange + val expectedMaterialTypes = listOf(materialType, systemMaterialType) + + every { materialTypeServiceMock.getAll(any()) } returns expectedMaterialTypes + + // Act + val actualMaterialTypes = materialTypeLogic.getAll(true) + + // Assert + assertEquals(expectedMaterialTypes, actualMaterialTypes) + } + + @Test + fun getByName_normalBehavior_returnsMaterialType() { + // Arrange + every { materialTypeServiceMock.getByName(any()) } returns materialType + + // Act + val actualMaterialType = materialTypeLogic.getByName(materialType.name) + + // Assert + assertEquals(materialType, actualMaterialType) + } + + @Test + fun getByName_nameNotFound_returnsNull() { + // Arrange + every { materialTypeServiceMock.getByName(any()) } returns null + + // Act + val actualMaterialType = materialTypeLogic.getByName(materialType.name) + + // Assert + assertNull(actualMaterialType) + } + + @Test + fun updateNonSystemType_normalBehavior_callsUpdate() { + // Arrange + every { materialTypeServiceMock.existsById(any(), any()) } returns false + every { materialTypeLogic.update(any()) } returnsArgument 0 + + // Act + materialTypeLogic.updateNonSystemType(materialType) + + // Assert + verify { + materialTypeLogic.update(materialType) + } + } + + @Test + fun updateNonSystemType_isSystemType_throwsCannotUpdateException() { + // Arrange + every { materialTypeServiceMock.existsById(any(), any()) } returns true + every { materialTypeLogic.update(any()) } returnsArgument 0 + + // Act + // Assert + assertThrows { materialTypeLogic.updateNonSystemType(materialType) } + } + + @Test + fun save_nameExists_throwsAlreadyExistsException() { + // Arrange + every { materialTypeServiceMock.existsByName(any(), any()) } returns true + every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns false + + // Act + // Assert + assertThrows { materialTypeLogic.save(materialType) } + } + + @Test + fun save_prefixExists_throwsAlreadyExistsException() { + // Arrange + every { materialTypeServiceMock.existsByName(any(), any()) } returns false + every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns true + + // Act + // Assert + assertThrows { materialTypeLogic.save(materialType) } + } + + @Test + fun update_nameExists_throwsAlreadyExistsException() { + // Arrange + every { materialTypeServiceMock.existsByName(any(), any()) } returns true + every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns false + + // Act + // Assert + assertThrows { materialTypeLogic.update(materialType) } + } + + @Test + fun update_prefixExists_throwsAlreadyExistsException() { + // Arrange + every { materialTypeServiceMock.existsByName(any(), any()) } returns false + every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns true + + // Act + // Assert + assertThrows { materialTypeLogic.update(materialType) } + } + + @Test + fun deleteById_usedByMaterial_throwsCannotDeleteException() { + // Arrange + every { materialTypeServiceMock.isUsedByMaterial(any()) } returns true + + // Act + // Assert + assertThrows { materialTypeLogic.deleteById(materialType.id) } + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogicTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogicTest.kt index b1e84d3..d9b69e5 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogicTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MaterialTypeLogicTest.kt @@ -1,182 +1,182 @@ -package dev.fyloz.colorrecipesexplorer.logic - -import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException -import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException -import dev.fyloz.colorrecipesexplorer.exception.NotFoundException -import dev.fyloz.colorrecipesexplorer.model.* -import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository -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.assertFalse -import kotlin.test.assertTrue - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class MaterialTypeLogicTest : - AbstractExternalNamedModelServiceTest() { - override val repository: MaterialTypeRepository = mock() - private val materialService: MaterialLogic = mock() - override val logic: MaterialTypeLogic = spy(DefaultMaterialTypeLogic(repository)) - override val entity: MaterialType = materialType(id = 0L, name = "material type", prefix = "MAT") - override val anotherEntity: MaterialType = materialType(id = 1L, name = "another material type", prefix = "AMT") - override val entityWithEntityName: MaterialType = materialType(2L, name = entity.name, prefix = "EEN") - private val systemType = materialType(id = 3L, name = "systype", prefix = "SYS", systemType = true) - private val anotherSystemType = materialType(id = 4L, name = "another systype", prefix = "ASY", systemType = true) - override val entitySaveDto: MaterialTypeSaveDto = spy(materialTypeSaveDto(name = "material type", prefix = "MAT")) - override val entityUpdateDto: MaterialTypeUpdateDto = - spy(materialTypeUpdateDto(id = 0L, name = "material type", prefix = "MAT")) - - @AfterEach - override fun afterEach() { - reset(materialService) - super.afterEach() - } - - // existsByPrefix() - - @Test - fun `existsByPrefix() returns true when a material type with the given prefix exists`() { - whenever(repository.existsByPrefix(entity.prefix)).doReturn(true) - - val found = logic.existsByPrefix(entity.prefix) - - assertTrue(found) - } - - @Test - fun `existsByPrefix() returns false when no material type with the given prefix exists`() { - whenever(repository.existsByPrefix(entity.prefix)).doReturn(false) - - val found = logic.existsByPrefix(entity.prefix) - - assertFalse(found) - } - - // getAllSystemTypes() - - @Test - fun `getAllSystemTypes() returns all system types`() { - whenever(repository.findAllBySystemTypeIs(true)).doReturn(listOf(systemType, anotherSystemType)) - - val found = logic.getAllSystemTypes() - - assertTrue(found.contains(systemType)) - assertTrue(found.contains(anotherSystemType)) - } - - // getAllNonSystemTypes() - - @Test - fun `getAllNonSystemTypes() returns all non system types`() { - whenever(repository.findAllBySystemTypeIs(false)).doReturn(listOf(entity, anotherEntity)) - - val found = logic.getAllNonSystemType() - - assertTrue(found.contains(entity)) - assertTrue(found.contains(anotherEntity)) - } - - // save() - - @Test - override fun `save(dto) calls and returns save() with the created entity`() { - withBaseSaveDtoTest(entity, entitySaveDto, logic) - } - - // saveMaterialType() - - @Test - fun `saveMaterialType() throws AlreadyExistsException when a material type with the given prefix already exists`() { - doReturn(true).whenever(logic).existsByPrefix(entity.prefix) - - assertThrows { logic.save(entity) } - .assertErrorCode("prefix") - } - - // update() - - @Test - override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, logic, { any() }) - - override fun `update() saves in the repository and returns the updated value`() { - whenever(repository.save(entity)).doReturn(entity) - whenever(repository.findByName(entity.name)).doReturn(null) - whenever(repository.findByPrefix(entity.prefix)).doReturn(null) - doReturn(true).whenever(logic).existsById(entity.id!!) - doReturn(entity).whenever(logic).getById(entity.id!!) - - val found = logic.update(entity) - - verify(repository).save(entity) - assertEquals(entity, found) - } - - override fun `update() throws NotFoundException when no entity with the given id exists in the repository`() { - whenever(repository.findByName(entity.name)).doReturn(null) - whenever(repository.findByPrefix(entity.prefix)).doReturn(null) - doReturn(false).whenever(logic).existsById(entity.id!!) - doReturn(null).whenever(logic).getById(entity.id!!) - - assertThrows { logic.update(entity) } - .assertErrorCode() - } - - override fun `update() throws AlreadyExistsException when an entity with the updated name exists`() { - whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName) - whenever(repository.findByPrefix(entity.prefix)).doReturn(null) - doReturn(true).whenever(logic).existsById(entity.id!!) - doReturn(entity).whenever(logic).getById(entity.id!!) - - assertThrows { logic.update(entity) } - .assertErrorCode("name") - } - - @Test - fun `update() throws AlreadyExistsException when an entity with the updated prefix exists`() { - val anotherMaterialType = materialType(prefix = entity.prefix) - whenever(repository.findByPrefix(entity.prefix)).doReturn(anotherMaterialType) - doReturn(entity).whenever(logic).getById(entity.id!!) - - assertThrows { logic.update(entity) } - .assertErrorCode("prefix") - } - - @Test - fun `update() throws CannotUpdateException when updating a system material type`() { - whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true) - - assertThrows { logic.update(systemType) } - } - - // delete() - - @Test - fun `delete() throws CannotDeleteException when deleting a system material type`() { - whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true) - - assertThrows { logic.delete(systemType) } - } - - override fun `delete() deletes in the repository`() { - whenCanBeDeleted { - super.`delete() deletes in the repository`() - } - } - - 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() - } -} +//package dev.fyloz.colorrecipesexplorer.logic +// +//import com.nhaarman.mockitokotlin2.* +//import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +//import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +//import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException +//import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +//import dev.fyloz.colorrecipesexplorer.model.* +//import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository +//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.assertFalse +//import kotlin.test.assertTrue +// +//@TestInstance(TestInstance.Lifecycle.PER_CLASS) +//class MaterialTypeLogicTest : +// AbstractExternalNamedModelServiceTest() { +// override val repository: MaterialTypeRepository = mock() +// private val materialService: MaterialLogic = mock() +// override val logic: MaterialTypeLogic = spy(DefaultMaterialTypeLogic(repository)) +// override val entity: MaterialType = materialType(id = 0L, name = "material type", prefix = "MAT") +// override val anotherEntity: MaterialType = materialType(id = 1L, name = "another material type", prefix = "AMT") +// override val entityWithEntityName: MaterialType = materialType(2L, name = entity.name, prefix = "EEN") +// private val systemType = materialType(id = 3L, name = "systype", prefix = "SYS", systemType = true) +// private val anotherSystemType = materialType(id = 4L, name = "another systype", prefix = "ASY", systemType = true) +// override val entitySaveDto: MaterialTypeSaveDto = spy(materialTypeSaveDto(name = "material type", prefix = "MAT")) +// override val entityUpdateDto: MaterialTypeUpdateDto = +// spy(materialTypeUpdateDto(id = 0L, name = "material type", prefix = "MAT")) +// +// @AfterEach +// override fun afterEach() { +// reset(materialService) +// super.afterEach() +// } +// +// // existsByPrefix() +// +// @Test +// fun `existsByPrefix() returns true when a material type with the given prefix exists`() { +// whenever(repository.existsByPrefix(entity.prefix)).doReturn(true) +// +// val found = logic.existsByPrefix(entity.prefix) +// +// assertTrue(found) +// } +// +// @Test +// fun `existsByPrefix() returns false when no material type with the given prefix exists`() { +// whenever(repository.existsByPrefix(entity.prefix)).doReturn(false) +// +// val found = logic.existsByPrefix(entity.prefix) +// +// assertFalse(found) +// } +// +// // getAllSystemTypes() +// +// @Test +// fun `getAllSystemTypes() returns all system types`() { +// whenever(repository.findAllBySystemTypeIs(true)).doReturn(listOf(systemType, anotherSystemType)) +// +// val found = logic.getAllSystemTypes() +// +// assertTrue(found.contains(systemType)) +// assertTrue(found.contains(anotherSystemType)) +// } +// +// // getAllNonSystemTypes() +// +// @Test +// fun `getAllNonSystemTypes() returns all non system types`() { +// whenever(repository.findAllBySystemTypeIs(false)).doReturn(listOf(entity, anotherEntity)) +// +// val found = logic.getAllNonSystemType() +// +// assertTrue(found.contains(entity)) +// assertTrue(found.contains(anotherEntity)) +// } +// +// // save() +// +// @Test +// override fun `save(dto) calls and returns save() with the created entity`() { +// withBaseSaveDtoTest(entity, entitySaveDto, logic) +// } +// +// // saveMaterialType() +// +// @Test +// fun `saveMaterialType() throws AlreadyExistsException when a material type with the given prefix already exists`() { +// doReturn(true).whenever(logic).existsByPrefix(entity.prefix) +// +// assertThrows { logic.save(entity) } +// .assertErrorCode("prefix") +// } +// +// // update() +// +// @Test +// override fun `update(dto) calls and returns update() with the created entity`() = +// withBaseUpdateDtoTest(entity, entityUpdateDto, logic, { any() }) +// +// override fun `update() saves in the repository and returns the updated value`() { +// whenever(repository.save(entity)).doReturn(entity) +// whenever(repository.findByName(entity.name)).doReturn(null) +// whenever(repository.findByPrefix(entity.prefix)).doReturn(null) +// doReturn(true).whenever(logic).existsById(entity.id!!) +// doReturn(entity).whenever(logic).getById(entity.id!!) +// +// val found = logic.update(entity) +// +// verify(repository).save(entity) +// assertEquals(entity, found) +// } +// +// override fun `update() throws NotFoundException when no entity with the given id exists in the repository`() { +// whenever(repository.findByName(entity.name)).doReturn(null) +// whenever(repository.findByPrefix(entity.prefix)).doReturn(null) +// doReturn(false).whenever(logic).existsById(entity.id!!) +// doReturn(null).whenever(logic).getById(entity.id!!) +// +// assertThrows { logic.update(entity) } +// .assertErrorCode() +// } +// +// override fun `update() throws AlreadyExistsException when an entity with the updated name exists`() { +// whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName) +// whenever(repository.findByPrefix(entity.prefix)).doReturn(null) +// doReturn(true).whenever(logic).existsById(entity.id!!) +// doReturn(entity).whenever(logic).getById(entity.id!!) +// +// assertThrows { logic.update(entity) } +// .assertErrorCode("name") +// } +// +// @Test +// fun `update() throws AlreadyExistsException when an entity with the updated prefix exists`() { +// val anotherMaterialType = materialType(prefix = entity.prefix) +// whenever(repository.findByPrefix(entity.prefix)).doReturn(anotherMaterialType) +// doReturn(entity).whenever(logic).getById(entity.id!!) +// +// assertThrows { logic.update(entity) } +// .assertErrorCode("prefix") +// } +// +// @Test +// fun `update() throws CannotUpdateException when updating a system material type`() { +// whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true) +// +// assertThrows { logic.update(systemType) } +// } +// +// // delete() +// +// @Test +// fun `delete() throws CannotDeleteException when deleting a system material type`() { +// whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true) +// +// assertThrows { logic.delete(systemType) } +// } +// +// override fun `delete() deletes in the repository`() { +// whenCanBeDeleted { +// super.`delete() deletes in the repository`() +// } +// } +// +// 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() +// } +//} diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogicTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogicTest.kt index fba155e..d611a92 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogicTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/logic/MixLogicTest.kt @@ -62,7 +62,7 @@ class MixLogicTest : AbstractExternalModelServiceTest()) if (mixUpdateDto.materialTypeId != null) { - whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType) + whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialTypeDto(materialType)) } op()