Ajout du support pour les mélanges dans l'API REST.

This commit is contained in:
FyloZ 2021-01-28 22:45:56 -05:00
parent c2c58be6bf
commit ca5d489cc4
14 changed files with 348 additions and 65 deletions

View File

@ -1,10 +1,19 @@
package dev.fyloz.trial.colorrecipesexplorer.model
import com.fasterxml.jackson.annotation.JsonIgnore
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
import java.util.*
import javax.persistence.*
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull
const val IDENTIFIER_MIX_TYPE_NAME = "mixType"
private const val MIX_ID_NULL_MESSAGE = "Un identifiant est requis"
private const val MIX_NAME_NULL_MESSAGE = "Un nom est requis"
private const val MIX_RECIPE_NULL_MESSAGE = "Un recette est requise"
private const val MIX_MATERIAL_TYPE_NULL_MESSAGE = "Un type de prodsuit est requis"
@Entity
data class Mix(
@Id
@ -13,27 +22,73 @@ data class Mix(
var location: String?,
@JsonIgnore
@ManyToOne
val recipe: Recipe,
@ManyToOne
val mixType: MixType,
@ManyToOne(cascade = [CascadeType.PERSIST])
var mixType: MixType,
@OneToMany
val mixMaterials: Collection<MixMaterial>,
@OneToMany(cascade = [CascadeType.ALL])
var mixMaterials: MutableCollection<MixMaterial>,
) : Model {
constructor(recipe: Recipe, mixType: MixType) : this(null, null, recipe, mixType, listOf())
constructor(recipe: Recipe, mixType: MixType) : this(null, null, recipe, mixType, mutableListOf())
override fun equals(other: Any?): Boolean = other is Mix && recipe == other.recipe && mixType == other.mixType
override fun hashCode(): Int = Objects.hash(recipe, mixType)
}
open class MixSaveDto(
@field:NotBlank(message = MIX_NAME_NULL_MESSAGE)
val name: String,
@field:NotNull(message = MIX_RECIPE_NULL_MESSAGE)
val recipeId: Long,
@field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE)
val materialTypeId: Long,
val mixMaterials: Map<Long, Float>?
) : EntityDto<Mix> {
override fun toEntity(): Mix = throw UnsupportedOperationException()
}
open class MixUpdateDto(
@field:NotNull(message = MIX_ID_NULL_MESSAGE)
val id: Long,
@field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE)
val name: String?,
val materialType: MaterialType?,
val mixMaterials: Map<Long, Float>?
) : EntityDto<Mix> {
override fun toEntity(): Mix = throw UnsupportedOperationException()
}
// ==== DSL ====
fun mix(
id: Long? = null,
location: String = "location",
location: String? = "location",
recipe: Recipe = recipe(),
mixType: MixType = mixType(),
mixMaterials: Collection<MixMaterial> = listOf(),
mixMaterials: MutableCollection<MixMaterial> = mutableListOf(),
op: Mix.() -> Unit = {}
) = Mix(id, location, recipe, mixType, mixMaterials).apply(op)
fun mixSaveDto(
name: String = "name",
recipeId: Long = 0L,
materialTypeId: Long = 0L,
mixMaterials: Map<Long, Float>? = mapOf(),
op: MixSaveDto.() -> Unit = {}
) = MixSaveDto(name, recipeId, materialTypeId, mixMaterials).apply(op)
fun mixUpdateDto(
id: Long = 0L,
name: String? = "name",
materialType: MaterialType? = materialType(),
mixMaterials: Map<Long, Float>? = mapOf(),
op: MixUpdateDto.() -> Unit = {}
) = MixUpdateDto(id, name, materialType, mixMaterials).apply(op)

View File

@ -14,10 +14,15 @@ data class MixType(
@Column(unique = true)
override var name: String,
@ManyToOne
var material: Material?
@OneToOne(cascade = [CascadeType.ALL])
var material: Material
) : NamedModel {
constructor(name: String, material: Material) : this(null, name, material)
constructor(name: String, materialType: MaterialType) : this(
null,
name,
material(name = name, inventoryQuantity = Float.MIN_VALUE, isMixType = true, materialType = materialType)
)
override fun equals(other: Any?): Boolean = other is MixType && other.name == name && other.material == material
override fun hashCode(): Int = Objects.hash(name, material)

View File

@ -42,7 +42,6 @@ data class Recipe(
@ManyToOne
val company: Company,
@JsonIgnore
@OneToMany
val mixes: MutableCollection<Mix>,

View File

@ -1,6 +1,10 @@
package dev.fyloz.trial.colorrecipesexplorer.repository
import dev.fyloz.trial.colorrecipesexplorer.model.Mix
import dev.fyloz.trial.colorrecipesexplorer.model.MixType
import org.springframework.data.jpa.repository.JpaRepository
interface MixRepository : JpaRepository<Mix, Long>
interface MixRepository : JpaRepository<Mix, Long> {
/** Finds all mixes with the given [mixType]. */
fun findAllByMixType(mixType: MixType): Collection<Mix>
}

View File

@ -1,19 +1,16 @@
package dev.fyloz.trial.colorrecipesexplorer.rest
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe
import dev.fyloz.trial.colorrecipesexplorer.model.RecipePublicDataDto
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeSaveDto
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeUpdateDto
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.service.MixService
import dev.fyloz.trial.colorrecipesexplorer.service.RecipeService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import java.net.URI
import javax.validation.Valid
private const val RECIPE_CONTROLLER_PATH = "api/recipe"
private const val MIX_CONTROLLER_PATH = "api/recipe/mix"
@RestController
@RequestMapping(RECIPE_CONTROLLER_PATH)
@ -28,3 +25,23 @@ class RecipeController(recipeService: RecipeService) :
return ResponseEntity.noContent().build()
}
}
@RestController
@RequestMapping(MIX_CONTROLLER_PATH)
class MixController(val mixService: MixService) {
@PostMapping
fun save(@Valid @RequestBody entity: MixSaveDto): ResponseEntity<Mix> {
val saved = mixService.save(entity)
return ResponseEntity
.created(URI("$MIX_CONTROLLER_PATH/${saved.id}"))
.body(saved)
}
@PutMapping
fun update(@Valid @RequestBody entity: MixUpdateDto): ResponseEntity<Void> {
mixService.update(entity)
return ResponseEntity
.noContent()
.build()
}
}

View File

@ -11,9 +11,6 @@ interface MaterialService :
/** Checks if a material with the given [materialType] exists. */
fun existsByMaterialType(materialType: MaterialType): Boolean
/** Checks if the given [material] is used by one or more mixes. */
fun isLinkedToMixes(material: Material): Boolean
/** Checks if the material with the given [id] has a SIMDUT file. */
fun hasSimdut(id: Long): Boolean
@ -27,7 +24,6 @@ interface MaterialService :
@Service
class MaterialServiceImpl(
materialRepository: MaterialRepository,
val mixQuantityService: MixMaterialService,
val simdutService: SimdutService
) :
AbstractExternalNamedModelService<Material, MaterialSaveDto, MaterialUpdateDto, MaterialRepository>(
@ -37,7 +33,6 @@ class MaterialServiceImpl(
override fun existsByMaterialType(materialType: MaterialType): Boolean =
repository.existsByMaterialType(materialType)
override fun isLinkedToMixes(material: Material): Boolean = mixQuantityService.existsByMaterial(material)
override fun hasSimdut(id: Long): Boolean = simdutService.exists(getById(id))
override fun getSimdut(id: Long): ByteArray = simdutService.read(getById(id))
override fun getAllNotMixType(): Collection<Material> = getAll().filter { !it.isMixType }

View File

@ -1,17 +1,35 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.model.Material
import dev.fyloz.trial.colorrecipesexplorer.model.Mix
import dev.fyloz.trial.colorrecipesexplorer.model.MixMaterial
import dev.fyloz.trial.colorrecipesexplorer.model.mixMaterial
import dev.fyloz.trial.colorrecipesexplorer.repository.MixMaterialRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
interface MixMaterialService : ModelService<MixMaterial, MixMaterialRepository> {
/** Checks if one or more mix materials have the given [material]. */
fun existsByMaterial(material: Material): Boolean
/** Creates [MixMaterial]s from the given [map]. The [map] must have the format <Material ID, Quantity>. */
fun createFromMap(mix: Mix, map: Map<Long, Float>): Collection<MixMaterial>
/** Creates a [MixMaterial] from the given [pair]. The [pair] must have the format <Material ID, Quantity>. */
fun createFromPair(mix: Mix, pair: Pair<Long, Float>): MixMaterial
}
@Service
class MixMaterialServiceImpl(mixMaterialRepository: MixMaterialRepository) :
AbstractModelService<MixMaterial, MixMaterialRepository>(mixMaterialRepository), MixMaterialService {
@Autowired
lateinit var materialService: MaterialService
override fun existsByMaterial(material: Material): Boolean = repository.existsByMaterial(material)
override fun createFromMap(mix: Mix, map: Map<Long, Float>): Collection<MixMaterial> = map.map { createFromPair(mix, it.toPair()) }
override fun createFromPair(mix: Mix, pair: Pair<Long, Float>): MixMaterial {
val material = materialService.getById(pair.first)
return mixMaterial(mix = mix, material = material, quantity = pair.second)
}
}

View File

@ -1,17 +1,80 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.model.Mix
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixRepository
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
import javax.transaction.Transactional
interface MixService : ExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixRepository> {
/** Gets all mixes with the given [mixType]. */
fun getAllByMixType(mixType: MixType): Collection<Mix>
/** Checks if a [MixType] is shared by several [Mix]es or not. */
fun mixTypeIsShared(mixType: MixType): Boolean
interface MixService : ModelService<Mix, MixRepository> {
/** Updates the location of the given [mix] to the given [location]. */
fun updateLocation(mix: Mix, location: String)
}
@Service
class MixServiceImpl(mixRepository: MixRepository) : AbstractModelService<Mix, MixRepository>(mixRepository),
class MixServiceImpl(
mixRepository: MixRepository,
@Lazy val recipeService: RecipeService,
val materialTypeService: MaterialTypeService,
val mixMaterialService: MixMaterialService,
val mixTypeService: MixTypeService
) : AbstractModelService<Mix, MixRepository>(mixRepository),
MixService {
override fun getAllByMixType(mixType: MixType): Collection<Mix> = repository.findAllByMixType(mixType)
override fun mixTypeIsShared(mixType: MixType): Boolean = getAllByMixType(mixType).count() > 1
@Transactional
override fun save(entity: MixSaveDto): Mix {
val recipe = recipeService.getById(entity.recipeId)
val materialType = materialTypeService.getById(entity.materialTypeId)
val mixType = MixType(entity.name, materialType)
var mix = save(mix(recipe = recipe, mixType = mixType))
val mixMaterials =
if (entity.mixMaterials != null) mixMaterialService.createFromMap(mix, entity.mixMaterials) else listOf()
mix = update(
mix(
id = mix.id,
location = null,
recipe = recipe,
mixType = mixType,
mixMaterials = mixMaterials.toMutableList()
)
)
recipeService.addMix(recipe, mix)
return mix
}
override fun update(entity: MixUpdateDto): Mix {
val mix = getById(entity.id)
if (entity.name != null || entity.materialType != null) {
mix.mixType = if (mixTypeIsShared(mix.mixType)) {
mixTypeService.createForNameAndMaterialType(
entity.name ?: mix.mixType.name,
entity.materialType ?: mix.mixType.material.materialType!!
)
} else {
mixTypeService.updateForNameAndMaterialType(
mix.mixType,
entity.name ?: mix.mixType.name,
entity.materialType ?: mix.mixType.material.materialType!!
)
}
}
if (entity.mixMaterials != null) {
mix.mixMaterials = mixMaterialService.createFromMap(mix, entity.mixMaterials).toMutableList()
}
return update(mix)
}
override fun updateLocation(mix: Mix, location: String) {
update(mix.apply { this.location = location })
}

View File

@ -2,18 +2,28 @@ package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsRestException
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityNotFoundRestException
import dev.fyloz.trial.colorrecipesexplorer.model.Material
import dev.fyloz.trial.colorrecipesexplorer.model.MixType
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixTypeRepository
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
interface MixTypeService : NamedModelService<MixType, MixTypeRepository> {
/** Gets the mix type with the given [material]. */
fun getByMaterial(material: Material): MixType
/** Returns a new and persisted [MixType] with the given [name] and [materialType]. */
fun createForNameAndMaterialType(name: String, materialType: MaterialType): MixType
/** Returns the given [mixType] updated with the given [name] and [materialType]. */
fun updateForNameAndMaterialType(mixType: MixType, name: String, materialType: MaterialType): MixType
}
@Service
class MixTypeServiceImpl(mixTypeRepository: MixTypeRepository, private val materialService: MaterialService) :
class MixTypeServiceImpl(
mixTypeRepository: MixTypeRepository,
val materialService: MaterialService,
@Lazy val mixService: MixService
) :
AbstractNamedModelService<MixType, MixTypeRepository>(mixTypeRepository), MixTypeService {
override fun getByMaterial(material: Material): MixType =
repository.findByMaterial(material) ?: throw EntityNotFoundRestException(material.name)
@ -23,4 +33,24 @@ class MixTypeServiceImpl(mixTypeRepository: MixTypeRepository, private val mater
throw EntityAlreadyExistsRestException(entity.name)
return super.save(entity)
}
override fun createForNameAndMaterialType(name: String, materialType: MaterialType): MixType =
save(
mixType(
name = name,
material = material(
name = name,
inventoryQuantity = Float.MIN_VALUE,
isMixType = true,
materialType = materialType
)
)
)
override fun updateForNameAndMaterialType(mixType: MixType, name: String, materialType: MaterialType): MixType =
update(mixType.apply {
this.name = name
material.name = name
material.materialType = materialType
})
}

View File

@ -2,12 +2,10 @@ package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.model.validation.isNotNullAndNotBlank
import dev.fyloz.trial.colorrecipesexplorer.model.validation.isNullOrNotBlank
import dev.fyloz.trial.colorrecipesexplorer.model.validation.or
import dev.fyloz.trial.colorrecipesexplorer.repository.RecipeRepository
import org.springframework.stereotype.Service
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
interface RecipeService : ExternalModelService<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeRepository> {
/** Checks if one or more recipes have the given [company]. */
@ -18,6 +16,9 @@ interface RecipeService : ExternalModelService<Recipe, RecipeSaveDto, RecipeUpda
/** Updates the public data of a recipe with the given [publicDateDto]. */
fun updatePublicData(publicDataDto: RecipePublicDataDto)
/** Adds the given [mix] to the given [recipe]. */
fun addMix(recipe: Recipe, mix: Mix): Recipe
}
@Service
@ -83,4 +84,7 @@ class RecipeServiceImpl(
}
}
}
override fun addMix(recipe: Recipe, mix: Mix) =
update(recipe.apply { mixes.add(mix) })
}

View File

@ -15,10 +15,9 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue
class MaterialServiceTest : AbstractExternalNamedModelServiceTest<Material, MaterialSaveDto, MaterialUpdateDto, MaterialService, MaterialRepository>() {
private val mixQuantityService: MixMaterialService = mock()
private val simdutService: SimdutService = mock()
override val repository: MaterialRepository = mock()
override val service: MaterialService = spy(MaterialServiceImpl(repository, mixQuantityService, simdutService))
override val service: MaterialService = spy(MaterialServiceImpl(repository, simdutService))
override val entity: Material = material(id = 0L, name = "material")
override val anotherEntity: Material = material(id = 1L, name = "another material")
@ -28,7 +27,7 @@ class MaterialServiceTest : AbstractExternalNamedModelServiceTest<Material, Mate
@AfterEach
override fun afterEach() {
reset(mixQuantityService, simdutService, entitySaveDto, entityUpdateDto)
reset(simdutService)
super.afterEach()
}
@ -55,27 +54,6 @@ class MaterialServiceTest : AbstractExternalNamedModelServiceTest<Material, Mate
}
}
@Nested
inner class IsLinkedToMixes {
@Test
fun `returns true when a mix contains the given material in the repository`() {
whenever(mixQuantityService.existsByMaterial(entity)).doReturn(true)
val found = service.isLinkedToMixes(entity)
assertTrue(found)
}
@Test
fun `returns false when no mix contains the given material in the repository`() {
whenever(mixQuantityService.existsByMaterial(entity)).doReturn(false)
val found = service.isLinkedToMixes(entity)
assertFalse(found)
}
}
@Nested
inner class HasSimdut {
@Test

View File

@ -1,18 +1,74 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.trial.colorrecipesexplorer.model.Mix
import dev.fyloz.trial.colorrecipesexplorer.model.mix
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixRepository
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class MixServiceTest : AbstractModelServiceTest<Mix, MixService, MixRepository>() {
class MixServiceTest : AbstractExternalModelServiceTest<Mix, MixSaveDto, MixUpdateDto, MixService, MixRepository>() {
override val repository: MixRepository = mock()
override val service: MixService = spy(MixServiceImpl(repository))
private val recipeService: RecipeService = mock()
private val materialTypeService: MaterialTypeService = mock()
private val mixMaterialService: MixMaterialService = mock()
private val mixTypeService: MixTypeService = mock()
override val service: MixService =
spy(MixServiceImpl(repository, recipeService, materialTypeService, mixMaterialService, mixTypeService))
override val entity: Mix = mix(id = 0L, location = "location")
override val anotherEntity: Mix = mix(id = 1L)
override val entitySaveDto: MixSaveDto = spy(mixSaveDto(mixMaterials = mapOf(1L to 1000f)))
override val entityUpdateDto: MixUpdateDto = spy(mixUpdateDto(id = entity.id!!))
@Nested
inner class GetAllByMixType {
@Test
fun `returns all mixes with the given mix type`() {
val mixType = mixType(id = 0L)
whenever(repository.findAllByMixType(mixType)).doReturn(entityList)
val found = service.getAllByMixType(mixType)
assertEquals(entityList, found)
}
}
@Nested
inner class SaveDto {
@Test
fun `calls and returns save() with the created entity`() {
val recipe = recipe(id = entitySaveDto.recipeId)
val materialType = materialType(id = entitySaveDto.materialTypeId)
val material = material(
name = entitySaveDto.name,
inventoryQuantity = Float.MIN_VALUE,
isMixType = true,
materialType = materialType
)
val mixType = mixType(name = entitySaveDto.name, material = material)
val mix = mix(recipe = recipe, mixType = mixType)
val mixWithId = mix(id = 0L, recipe = recipe, mixType = mixType)
val mixMaterials = listOf(mixMaterial(mix = mixWithId, material = material(id = 1L), quantity = 1000f))
val mixWithMaterials = mix.apply { this.mixMaterials.addAll(mixMaterials) }
whenever(recipeService.getById(recipe.id!!)).doReturn(recipe)
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType)
whenever(mixMaterialService.createFromMap(mixWithId, entitySaveDto.mixMaterials!!)).doReturn(mixMaterials)
doReturn(true).whenever(service).existsById(mixWithId.id!!)
doReturn(mixWithId).whenever(service).save(mix)
doReturn(mixWithMaterials).whenever(service).update(mixWithMaterials)
val found = service.save(entitySaveDto)
verify(service).save(mix)
verify(service).update(mixWithMaterials)
verify(recipeService).addMix(recipe, mix)
assertEquals(mixWithMaterials, found)
}
}
@Nested
inner class UpdateLocation {

View File

@ -3,21 +3,20 @@ package dev.fyloz.trial.colorrecipesexplorer.service
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsRestException
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityNotFoundRestException
import dev.fyloz.trial.colorrecipesexplorer.model.Material
import dev.fyloz.trial.colorrecipesexplorer.model.MixType
import dev.fyloz.trial.colorrecipesexplorer.model.material
import dev.fyloz.trial.colorrecipesexplorer.model.mixType
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MixTypeRepository
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class MixTypeServiceTest : AbstractNamedModelServiceTest<MixType, MixTypeService, MixTypeRepository>() {
override val repository: MixTypeRepository = mock()
private val materialService: MaterialService = mock()
override val service: MixTypeService = spy(MixTypeServiceImpl(repository, materialService))
private val mixService: MixService = mock()
override val service: MixTypeService = spy(MixTypeServiceImpl(repository, materialService, mixService))
private val material: Material = material(id = 0L)
override val entity: MixType = mixType(id = 0L, name = "mix type", material = material)
@ -60,4 +59,46 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest<MixType, MixTypeService
assertEquals(entity.name, exception.value)
}
}
@Nested
inner class CreateForNameAndMaterialType {
@Test
fun `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(service).save(any())
val found = service.createForNameAndMaterialType(name, materialType)
verify(service).save(any())
assertEquals(name, found.name)
assertEquals(name, found.material.name)
assertEquals(materialType, found.material.materialType)
assertTrue(found.material.isMixType)
}
}
@Nested
inner class UpdateForNameAndMaterialType {
@Test
fun `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(service).update(any())
val found = service.updateForNameAndMaterialType(mixType, name, materialType)
verify(service).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)
}
}
}

View File

@ -93,4 +93,22 @@ class RecipeServiceTest :
verify(mixService).updateLocation(mix, location)
}
}
@Nested
inner class AddMix {
@Test
fun `add the given mix to the given recipe and updates it`() {
val mix = mix(id = 0L)
val recipe = recipe(id = 0L, mixes = mutableListOf())
doAnswer { it.arguments[0] }.whenever(service).update(any<Recipe>())
val found = service.addMix(recipe, mix)
verify(service).update(any<Recipe>())
assertEquals(recipe.id, found.id)
assertTrue(found.mixes.contains(mix))
}
}
}