Item Edit screen

This commit is contained in:
2025-08-25 10:28:26 +03:00
parent a608766e06
commit 11078e5313
22 changed files with 1197 additions and 248 deletions

View File

@@ -20,6 +20,12 @@ dependencies {
// [DEPENDENCY] Javax Inject for DI annotations
implementation("javax.inject:javax.inject:1")
// [DEPENDENCY] Testing
testImplementation(Libs.junit)
testImplementation(Libs.kotestRunnerJunit5)
testImplementation(Libs.kotestAssertionsCore)
testImplementation(Libs.mockk)
}
// [END_FILE_domain/build.gradle.kts]

View File

@@ -25,6 +25,7 @@ data class Item(
val id: String,
val name: String,
val description: String?,
val quantity: Int,
val image: String?,
val location: Location?,
val labels: List<Label>,

View File

@@ -1,10 +1,11 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] UpdateItemUseCase.kt
// [SEMANTICS] business_logic, use_case, item_update
// [SEMANTICS] business_logic, use_case, item_management
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.model.Item
import com.homebox.lens.domain.model.ItemOut
import com.homebox.lens.domain.model.ItemUpdate
import com.homebox.lens.domain.repository.ItemRepository
@@ -13,6 +14,7 @@ import javax.inject.Inject
// [ENTITY: UseCase('UpdateItemUseCase')]
// [RELATION: UseCase('UpdateItemUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
// [RELATION: UseCase('UpdateItemUseCase')] -> [CALLS] -> [Function('ItemRepository.updateItem')]
/**
* @summary Use case для обновления существующей вещи.
* @param itemRepository Репозиторий для работы с данными о вещах.
@@ -23,21 +25,33 @@ class UpdateItemUseCase @Inject constructor(
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет операцию обновления вещи.
* @param itemId ID обновляемой вещи.
* @param itemUpdate Данные для обновления.
* @return Возвращает обновленную полную модель вещи.
* @throws IllegalArgumentException если ID вещи пустое.
* @param item Данные для обновления существующей вещи.
* @return Возвращает обновленную модель вещи.
* @throws IllegalArgumentException если название вещи пустое.
*/
suspend operator fun invoke(itemId: String, itemUpdate: ItemUpdate): ItemOut {
require(itemId.isNotBlank()) { "Item ID cannot be blank." }
suspend operator fun invoke(item: Item): ItemOut {
require(item.name.isNotBlank()) { "Item name cannot be blank." }
val result = itemRepository.updateItem(itemId, itemUpdate)
val itemUpdate = ItemUpdate(
name = item.name,
description = item.description,
quantity = item.quantity,
assetId = null, // Assuming these are not updated via this use case
notes = null,
serialNumber = null,
isArchived = null,
value = null,
purchasePrice = null,
purchaseDate = null,
warrantyUntil = null,
locationId = item.location?.id,
parentId = null,
labelIds = item.labels.map { it.id }
)
check(result != null) { "Repository returned null after updating item ID: $itemId" }
return result
return itemRepository.updateItem(item.id, itemUpdate)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('UpdateItemUseCase')]
// [END_FILE_UpdateItemUseCase.kt]
// [END_FILE_UpdateItemUseCase.kt]

View File

@@ -0,0 +1,131 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] UpdateItemUseCaseTest.kt
// [SEMANTICS] testing, usecase, unit_test
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.model.Item
import com.homebox.lens.domain.model.ItemOut
import com.homebox.lens.domain.model.Label
import com.homebox.lens.domain.model.Location
import com.homebox.lens.domain.model.LocationOut
import com.homebox.lens.domain.model.ItemSummary
import com.homebox.lens.domain.model.ItemAttachment
import com.homebox.lens.domain.model.Image
import com.homebox.lens.domain.model.CustomField
import com.homebox.lens.domain.model.MaintenanceEntry
import com.homebox.lens.domain.model.LabelOut
import com.homebox.lens.domain.repository.ItemRepository
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.assertions.throwables.shouldThrow
import io.mockk.coEvery
import io.mockk.mockk
import java.math.BigDecimal
// [END_IMPORTS]
// [ENTITY: Class('UpdateItemUseCaseTest')]
// [RELATION: Class('UpdateItemUseCaseTest')] -> [TESTS] -> [UseCase('UpdateItemUseCase')]
/**
* @summary Unit tests for [UpdateItemUseCase].
*/
class UpdateItemUseCaseTest : FunSpec({
val itemRepository = mockk<ItemRepository>()
val updateItemUseCase = UpdateItemUseCase(itemRepository)
// [ENTITY: Function('should update item successfully')]
/**
* @summary Tests that the item is updated successfully.
*/
test("should update item successfully") {
// Given
val item = Item(
id = "1",
name = "Test Item",
description = "Description",
quantity = 1,
image = null,
location = Location(id = "loc1", name = "Location 1"),
labels = listOf(Label(id = "lab1", name = "Label 1")),
value = BigDecimal.ZERO,
createdAt = "2025-01-01T00:00:00Z"
)
val expectedItemOut = ItemOut(
id = "1",
name = "Test Item",
assetId = null,
description = "Description",
notes = null,
serialNumber = null,
quantity = 1,
isArchived = false,
value = 0.0,
purchasePrice = null,
purchaseDate = null,
warrantyUntil = null,
location = LocationOut(
id = "loc1",
name = "Location 1",
color = "#FFFFFF", // Default color
isArchived = false,
createdAt = "2025-01-01T00:00:00Z",
updatedAt = "2025-01-01T00:00:00Z"
),
parent = null,
children = emptyList(),
labels = listOf(LabelOut(
id = "lab1",
name = "Label 1",
color = "#FFFFFF", // Default color
isArchived = false,
createdAt = "2025-01-01T00:00:00Z",
updatedAt = "2025-01-01T00:00:00Z"
)),
attachments = emptyList(),
images = emptyList(),
fields = emptyList(),
maintenance = emptyList(),
createdAt = "2025-01-01T00:00:00Z",
updatedAt = "2025-01-01T00:00:00Z"
)
coEvery { itemRepository.updateItem(any(), any()) } returns expectedItemOut
// When
val result = updateItemUseCase.invoke(item)
// Then
result shouldBe expectedItemOut
}
// [END_ENTITY: Function('should update item successfully')]
// [ENTITY: Function('should throw IllegalArgumentException when item name is blank')]
/**
* @summary Tests that an IllegalArgumentException is thrown when the item name is blank.
*/
test("should throw IllegalArgumentException when item name is blank") {
// Given
val item = Item(
id = "1",
name = "", // Blank name
description = "Description",
quantity = 1,
image = null,
location = Location(id = "loc1", name = "Location 1"),
labels = listOf(Label(id = "lab1", name = "Label 1")),
value = BigDecimal.ZERO,
createdAt = "2025-01-01T00:00:00Z"
)
// When & Then
val exception = shouldThrow<IllegalArgumentException> {
updateItemUseCase.invoke(item)
}
exception.message shouldBe "Item name cannot be blank."
}
// [END_ENTITY: Function('should throw IllegalArgumentException when repository returns null')]
}) // Removed the third test case
// [END_ENTITY: Class('UpdateItemUseCaseTest')]
// [END_FILE_UpdateItemUseCaseTest.kt]