Repository: team-berlin/Task-Manager
Pull request #6: feature/state
Author: login: Ahmed7osny1
Created: 2025-05-01T03:16:15Z | Merged: 2025-05-01T12:45:16Z



Diff:
diff --git a/src/main/kotlin/logic/generateIdHelper/IdGenerator.kt b/src/main/kotlin/logic/generateIdHelper/IdGenerator.kt
new file mode 100644
index 0000000..42c07e0
--- /dev/null
+++ b/src/main/kotlin/logic/generateIdHelper/IdGenerator.kt
@@ -0,0 +1,11 @@
+package com.berlin.logic.generateIdHelper
+
+import kotlin.random.Random
+
+interface IdGenerator {
+    fun generateId(
+        prefix: String,
+        padChar: Char = prefix[prefix.length / 2],
+        padCharLength: Int  = prefix.length + Random.Default.nextInt(1, 5)
+        ): String
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/generateIdHelper/generateId.kt b/src/main/kotlin/logic/generateIdHelper/generateId.kt
new file mode 100644
index 0000000..eccb920
--- /dev/null
+++ b/src/main/kotlin/logic/generateIdHelper/generateId.kt
@@ -0,0 +1,13 @@
+package com.berlin.logic.generateIdHelper
+
+class IdGeneratorImplementation : IdGenerator {
+    override fun generateId(
+        prefix: String, padChar: Char, padCharLength: Int): String {
+        val cleanedInput = prefix.trim().replace(" ", "")
+            .ifEmpty { throw IllegalArgumentException("String must not be empty") }
+
+        val paddedNumber = cleanedInput.padEnd(padCharLength, padChar)
+
+        return "${paddedNumber}_${System.currentTimeMillis() % 100000}"
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/repositories/ProjectRepository.kt b/src/main/kotlin/logic/repositories/ProjectRepository.kt
index 17fbc66..f003a63 100644
--- a/src/main/kotlin/logic/repositories/ProjectRepository.kt
+++ b/src/main/kotlin/logic/repositories/ProjectRepository.kt
@@ -4,7 +4,7 @@ import com.berlin.model.Project
 
 interface ProjectRepository {
     fun createProject(project:Project):Boolean
-    fun getProjectById(projectId:String):Project?
+    fun getProjectById(projectId:String): Project?
     fun getAllProjects():List<Project>
     fun updateProject(project: Project):Boolean
     fun deleteProject(projectId: String):Boolean
diff --git a/src/main/kotlin/logic/repositories/StateRepository.kt b/src/main/kotlin/logic/repositories/StateRepository.kt
index e0589d3..fed13eb 100644
--- a/src/main/kotlin/logic/repositories/StateRepository.kt
+++ b/src/main/kotlin/logic/repositories/StateRepository.kt
@@ -1,11 +1,14 @@
 package com.berlin.logic.repositories
 
 import com.berlin.model.State
+import com.berlin.model.Task
 
 interface StateRepository {
-    fun createState(state: State):Boolean
-    fun getStatesByProjectId(projectId:String):List<State>
-    fun deleteState(stateId:String):Boolean
-    fun updateState(state: State):Boolean
-    fun getStateByTaskId(taskId:String):State?
+    fun addState(state: State): Result<String>
+    fun getStatesByProjectId(projectId: String):List<State>?
+    fun getTasksByStateId(stateId: String):List<Task>?
+    fun deleteState(stateId: String): Result<String>
+    fun updateState(state: State): Result<String>
+    fun getStateByTaskId(taskId: String): State?
+    fun getStateById(stateId: String): State?
 }
\ No newline at end of file
diff --git a/src/main/kotlin/logic/usecase/state/CreationStateUseCase.kt b/src/main/kotlin/logic/usecase/state/CreationStateUseCase.kt
new file mode 100644
index 0000000..c9b29b8
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/CreationStateUseCase.kt
@@ -0,0 +1,29 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.generateIdHelper.IdGenerator
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+
+class CreationStateUseCase(
+    private val stateRepository: StateRepository,
+    private val idGenerator: IdGenerator,
+) {
+    fun createNewState(stateName: String, projectId: String): Result<String> {
+        if (validateStateName(stateName)) {
+            val newState = State(
+                id = idGenerator.generateId(stateName),
+                name = stateName,
+                projectId = projectId
+            )
+            return stateRepository.addState(newState)
+                .map { "State created successfully" }
+                .recover { "Creation Failed" }
+        } else {
+            throw Exception("State Name must not be empty or blank")
+        }
+    }
+
+    private fun validateStateName(stateName: String): Boolean =
+        stateName.isNotBlank() && !(stateName.all { it.isDigit() })
+
+}
diff --git a/src/main/kotlin/logic/usecase/state/DeletionStateUseCase.kt b/src/main/kotlin/logic/usecase/state/DeletionStateUseCase.kt
new file mode 100644
index 0000000..abad131
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/DeletionStateUseCase.kt
@@ -0,0 +1,29 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+
+class DeletionStateUseCase(
+    private val stateRepository: StateRepository
+) {
+    fun deleteState(stateId: String): Result<String> {
+
+        if(!validateStateId(stateId))
+            throw Exception("State ID must not be empty or blank")
+
+        if (!checkStateExists(stateId)) {
+            return Result.failure(
+                Exception("State with ID $stateId does not exist")
+            )
+        }
+
+        return stateRepository.deleteState(stateId)
+            .map { "Deleted Successfully" }
+            .recover { "Deletion Failed" }
+    }
+
+    private fun validateStateId(stateId: String): Boolean =
+        stateId.isNotBlank() || !(stateId.all { it.isDigit() })
+
+    fun checkStateExists(stateId: String): Boolean =
+        stateRepository.getStateById(stateId) != null
+}
diff --git a/src/main/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCase.kt b/src/main/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCase.kt
new file mode 100644
index 0000000..1cfae55
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCase.kt
@@ -0,0 +1,31 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.ProjectRepository
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+
+class GetAllStatesByProjectIdUseCase(
+    private val stateRepository: StateRepository,
+    private val projectRepository: ProjectRepository
+) {
+
+    fun getAllStatesByProjectId(projectId: String): List<State>? {
+
+        if (!validateProjectId(projectId))
+            throw Exception("Project ID must not be empty or blank")
+
+        if (checkProjectExists(projectId)) {
+            return stateRepository.getStatesByProjectId(projectId)
+                ?: throw Exception("No tasks found for state ID $projectId")
+        } else {
+            throw Exception("Project with ID $projectId does not exist")
+        }
+    }
+
+    private fun checkProjectExists(projectId: String): Boolean =
+        projectRepository.getProjectById(projectId) != null
+
+    private fun validateProjectId(projectId: String): Boolean =
+        projectId.isNotBlank() && !(projectId.all { it.isDigit() })
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/usecase/state/GetStateByIdUseCase.kt b/src/main/kotlin/logic/usecase/state/GetStateByIdUseCase.kt
new file mode 100644
index 0000000..6836c90
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/GetStateByIdUseCase.kt
@@ -0,0 +1,21 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+
+class GetStateByIdUseCase(
+    private val stateRepository: StateRepository
+) {
+
+    fun getStateById(stateId: String): State {
+        if(!validateStateId(stateId))
+            throw Exception("State ID must not be empty or blank")
+
+        return stateRepository.getStateById(stateId)
+            ?: throw Exception("State with ID $stateId does not exist")
+    }
+
+    private fun validateStateId(stateId: String): Boolean =
+        stateId.isNotBlank() && !(stateId.all { it.isDigit() })
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/usecase/state/GetStateByTaskIdUseCase.kt b/src/main/kotlin/logic/usecase/state/GetStateByTaskIdUseCase.kt
new file mode 100644
index 0000000..31fdc6f
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/GetStateByTaskIdUseCase.kt
@@ -0,0 +1,27 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.logic.repositories.TaskRepository
+import com.berlin.model.State
+
+class GetStateByTaskIdUseCase(
+    private val stateRepository: StateRepository,
+    private val taskRepository: TaskRepository
+) {
+
+    fun getStateByTaskId(taskId: String): State? {
+        if (!validateTaskId(taskId)) throw Exception("Task ID must not be empty or blank")
+
+        if (checkTaskExists(taskId)) {
+            return stateRepository.getStateByTaskId(taskId)
+        } else {
+            throw Exception("State with ID $taskId does not exist")
+        }
+    }
+
+
+    private fun checkTaskExists(taskId: String): Boolean = taskRepository.getTaskById(taskId) != null
+
+    private fun validateTaskId(taskId: String): Boolean = taskId.isNotBlank() && !(taskId.all { it.isDigit() })
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/usecase/state/GetTasksByStateIdUseCase.kt b/src/main/kotlin/logic/usecase/state/GetTasksByStateIdUseCase.kt
new file mode 100644
index 0000000..da53c71
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/GetTasksByStateIdUseCase.kt
@@ -0,0 +1,25 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.Task
+
+class GetTasksByStateIdUseCase (
+    private val stateRepository: StateRepository
+) {
+
+    fun getAllTasksByStateId(stateId: String): List<Task>? {
+        if (!validateStateId(stateId)) throw Exception("State ID must not be empty or blank")
+
+        if (checkStateExists(stateId)) {
+            return stateRepository.getTasksByStateId(stateId)
+                ?: throw Exception("No tasks found for state ID $stateId")
+        } else {
+            throw Exception("State with ID $stateId does not exist")
+        }
+    }
+
+
+    private fun checkStateExists(stateId: String): Boolean = stateRepository.getStateById(stateId) != null
+
+    private fun validateStateId(stateId: String): Boolean = stateId.isNotBlank() && !(stateId.all { it.isDigit() })
+}
\ No newline at end of file
diff --git a/src/main/kotlin/logic/usecase/state/UpdateStateUseCase.kt b/src/main/kotlin/logic/usecase/state/UpdateStateUseCase.kt
new file mode 100644
index 0000000..b1a52af
--- /dev/null
+++ b/src/main/kotlin/logic/usecase/state/UpdateStateUseCase.kt
@@ -0,0 +1,21 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+
+class UpdateStateUseCase(
+    private val stateRepository: StateRepository
+) {
+    fun updateState(state: State): Result<String> {
+        if(!validateStateName(state.name))
+            throw Exception("State Name must not be empty or blank")
+
+        return stateRepository.updateState(state)
+            .map { "Updated Successfully" }
+            .recover { "Update Failed" }
+    }
+
+
+    private fun validateStateName(stateName: String): Boolean =
+        stateName.isNotBlank() && !(stateName.all { it.isDigit() })
+}
\ No newline at end of file
diff --git a/src/main/kotlin/model/State.kt b/src/main/kotlin/model/State.kt
index a0a3580..97cee5b 100644
--- a/src/main/kotlin/model/State.kt
+++ b/src/main/kotlin/model/State.kt
@@ -1,7 +1,7 @@
 package com.berlin.model
 
 data class State(
-    val id:String,
-    val name:String,
-    val projectId:String,
+    val id: String,
+    val name: String,
+    val projectId: String,
 )
diff --git a/src/main/kotlin/model/Task.kt b/src/main/kotlin/model/Task.kt
index 1c576a3..22d385c 100644
--- a/src/main/kotlin/model/Task.kt
+++ b/src/main/kotlin/model/Task.kt
@@ -7,7 +7,6 @@ data class Task(
     val title:String,
     val description:String?,
     val stateId:String,
-    val assignedTo:User,
-    val createBy:User,
-    val auditLogs:List<AuditLog>
-    )
+    val assignedTo: String,
+    val createBy: String
+)
diff --git a/src/main/kotlin/presentation/UiRunner.kt b/src/main/kotlin/presentation/UiRunner.kt
new file mode 100644
index 0000000..128d40e
--- /dev/null
+++ b/src/main/kotlin/presentation/UiRunner.kt
@@ -0,0 +1,7 @@
+package com.berlin.presentation
+
+interface UiRunner {
+    val id: Int
+    val label: String
+    fun run()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/presentation/input_output/ConsoleReader.kt b/src/main/kotlin/presentation/input_output/ConsoleReader.kt
new file mode 100644
index 0000000..b6bde10
--- /dev/null
+++ b/src/main/kotlin/presentation/input_output/ConsoleReader.kt
@@ -0,0 +1,7 @@
+package com.berlin.presentation.input_output
+
+class ConsoleReader: Reader {
+    override fun getUserInput(): String? {
+        return readlnOrNull()
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/presentation/input_output/ConsoleViewer.kt b/src/main/kotlin/presentation/input_output/ConsoleViewer.kt
new file mode 100644
index 0000000..466656d
--- /dev/null
+++ b/src/main/kotlin/presentation/input_output/ConsoleViewer.kt
@@ -0,0 +1,7 @@
+package com.berlin.presentation.input_output
+
+class ConsoleViewer: Viewer {
+    override fun display(message: String) {
+        println(message)
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/presentation/input_output/Reader.kt b/src/main/kotlin/presentation/input_output/Reader.kt
new file mode 100644
index 0000000..34fbc40
--- /dev/null
+++ b/src/main/kotlin/presentation/input_output/Reader.kt
@@ -0,0 +1,5 @@
+package com.berlin.presentation.input_output
+
+interface Reader {
+    fun getUserInput(): String?
+}
\ No newline at end of file
diff --git a/src/main/kotlin/presentation/input_output/Viewer.kt b/src/main/kotlin/presentation/input_output/Viewer.kt
new file mode 100644
index 0000000..a10f463
--- /dev/null
+++ b/src/main/kotlin/presentation/input_output/Viewer.kt
@@ -0,0 +1,5 @@
+package com.berlin.presentation.input_output
+
+interface Viewer {
+    fun display(message: String)
+}
\ No newline at end of file
diff --git a/src/test/kotlin/logic/generateIdHelper/DefaultIdGeneratorTest.kt b/src/test/kotlin/logic/generateIdHelper/DefaultIdGeneratorTest.kt
new file mode 100644
index 0000000..7ce6013
--- /dev/null
+++ b/src/test/kotlin/logic/generateIdHelper/DefaultIdGeneratorTest.kt
@@ -0,0 +1,73 @@
+package com.berlin.logic.generateIdHelper
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import kotlin.test.Test
+
+class DefaultIdGeneratorTest {
+
+    private lateinit var idGeneratorImplementation:
+            IdGeneratorImplementation
+
+    @BeforeEach
+    fun setup() {
+        idGeneratorImplementation = IdGeneratorImplementation()
+    }
+
+    @Test
+    fun `should generate ID with correct padding and timestamp`() {
+        // Given
+        val input = "Test"
+        val padChar = 'X'
+        val padCharLength = 8
+
+        // When
+        val generatedId = idGeneratorImplementation.generateId(input, padChar, padCharLength)
+
+        // Then
+        assertThat(generatedId.startsWith("TestXXXX_")).isTrue()
+        assertThat(generatedId.substringAfter("_").toIntOrNull()).isNotNull()
+    }
+
+    @Test
+    fun `should throw exception when input is empty`() {
+        // Given
+        val emptyInput = " "
+
+        // When & Then
+        val exception = assertThrows<IllegalArgumentException> {
+            idGeneratorImplementation.generateId(emptyInput, 'X', 8)
+        }
+        assertThat(exception.message).isEqualTo("String must not be empty")
+    }
+
+    @Test
+    fun `should pad input correctly when length is shorter than pad size`() {
+        // Given
+        val input = "AB"
+        val padChar = 'Y'
+        val padCharLength = 6
+
+        // When
+        val generatedId = idGeneratorImplementation.generateId(input, padChar, padCharLength)
+
+        // Then
+        assertThat(generatedId.startsWith("ABYYYY_")).isTrue()
+    }
+
+    @Test
+    fun `should not modify input if it already matches pad length`() {
+        // Given
+        val input = "ValidStr"
+        val padChar = 'Z'
+        val padCharLength = 9 // One extra padding
+
+        // When
+        val generatedId = idGeneratorImplementation.generateId(input, padChar, padCharLength)
+
+        // Then
+        assertThat(generatedId.startsWith("ValidStrZ_")).isTrue()
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/logic/usecase/state/CreationStateUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/CreationStateUseCaseTest.kt
new file mode 100644
index 0000000..d4b7be2
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/CreationStateUseCaseTest.kt
@@ -0,0 +1,77 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.generateIdHelper.IdGenerator
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class CreationStateUseCaseTest {
+
+    private lateinit var createStateUseCase: CreationStateUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        val idGenerator: IdGenerator = mockk(relaxed = true)
+        createStateUseCase = CreationStateUseCase(stateRepository,
+            idGenerator)
+    }
+
+    @Test
+    fun `createNewState should return success when state created successfully`() {
+        // Given
+        val validState = State(id = "S1", name = "TODO", projectId = "P1")
+        every { stateRepository.addState(any()) } returns Result.success("State created successfully")
+        every { stateRepository.getStateById(any()) } returns mockk()
+
+        // When
+        val result = createStateUseCase.createNewState(validState.name,
+            validState.projectId)
+
+        // Then
+        assertThat(result).isEqualTo(
+            Result.success("State created successfully")
+        )
+    }
+
+    @Test
+    fun `createNewState should return failure when state creation fails`() {
+        // Given
+        val validState = State(id = "S1", name = "S1", projectId = "1")
+        every { stateRepository.addState(any()) } returns Result.failure(Exception())
+        every { stateRepository.getStateById(any()) } returns mockk()
+
+        // When
+        val result = createStateUseCase.createNewState(validState.name,
+            validState.projectId)
+
+        // Then
+        result.onFailure { exception ->
+            assertThat(exception.message).isEqualTo("Creation Failed")
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `validateStateName should throw exception when state name is invalid`(
+        invalidName: String,
+    ) {
+        // Given
+        val stateInput = State(id = "S1", name = invalidName, projectId = "P1")
+
+        // When && Then
+        assertThrows<Exception> {
+            createStateUseCase.createNewState(
+                stateInput.name,
+                stateInput.projectId
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/logic/usecase/state/DeletionStateUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/DeletionStateUseCaseTest.kt
new file mode 100644
index 0000000..8f1a5b8
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/DeletionStateUseCaseTest.kt
@@ -0,0 +1,72 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class DeletionStateUseCaseTest {
+    private lateinit var deleteStateUseCase: DeletionStateUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        deleteStateUseCase = DeletionStateUseCase(stateRepository)
+    }
+
+    @Test
+    fun `should return success when state is deleted successfully`() {
+        // Given
+        every { stateRepository.deleteState(any()) } returns Result.success("Deleted Successfully")
+        every { stateRepository.getStateById(any()) } returns mockk()
+
+        // When
+        val result = deleteStateUseCase.deleteState("state_1")
+
+        // Then
+        assertThat(result).isEqualTo(Result.success("Deleted Successfully"))
+    }
+
+    @Test
+    fun `should return failure when state deletion fails`() {
+        // Given
+        every { stateRepository.deleteState(any()) } returns Result.failure(Exception("Deletion Failed"))
+        every { stateRepository.getStateById(any()) } returns mockk()
+
+        // When
+        val result = deleteStateUseCase.deleteState("state_2")
+
+        // Then
+        result.onFailure { exception ->
+            assertThat(exception.message).isEqualTo("Deletion Failed")
+        }
+    }
+
+    @Test
+    fun `should throw exception when state does not exist`() {
+        // Given
+        every { stateRepository.getStateById(any()) } returns null
+
+        // When
+        val result = deleteStateUseCase.deleteState("S2")
+
+        // Then
+        result.onFailure { exception ->
+            assertThat(exception.message).isEqualTo("State with ID S2 does not exist")
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `should throw exception when state ID is invalid`(stateId: String) {
+        // When & Then
+        assertThrows<Exception> {
+            deleteStateUseCase.deleteState(stateId)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCaseTest.kt
new file mode 100644
index 0000000..ceb5157
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/GetAllStatesByProjectIdUseCaseTest.kt
@@ -0,0 +1,74 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.ProjectRepository
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class GetAllStatesByProjectIdUseCaseTest {
+
+    private lateinit var getAllStatesByProjectIdUseCase: GetAllStatesByProjectIdUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+    private val projectRepository: ProjectRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        getAllStatesByProjectIdUseCase = GetAllStatesByProjectIdUseCase(stateRepository,
+            projectRepository)
+    }
+
+    @Test
+    fun `should return states when states are found for the project`() {
+        // Given
+        val expectedStates = listOf(
+            State(id = "S1", name = "Active", projectId = "P1"),
+            State(id = "S2", name = "Inactive", projectId = "P1")
+        )
+        every { projectRepository.getProjectById("P1") } returns mockk()
+        every { stateRepository.getStatesByProjectId("P1") } returns expectedStates
+
+        // When
+        val result = getAllStatesByProjectIdUseCase.getAllStatesByProjectId("P1")
+
+        // Then
+        assertThat(result).isEqualTo(expectedStates)
+    }
+
+    @Test
+    fun `should throw exception when no states are found for the project`() {
+        // Given
+        every { projectRepository.getProjectById("P1") } returns mockk()
+        every { stateRepository.getStatesByProjectId("P3") } returns null
+
+        // When & Then
+        val exception = assertThrows<Exception> { getAllStatesByProjectIdUseCase.getAllStatesByProjectId("P3") }
+        assertThat(exception.message).isEqualTo("No tasks found for state ID P3")
+    }
+
+    @Test
+    fun `should throw exception when project ID does not exist`() {
+        // Given
+        every { projectRepository.getProjectById("P2") } returns null
+
+        // When & Then
+        val exception = assertThrows<Exception> { getAllStatesByProjectIdUseCase.getAllStatesByProjectId("P2") }
+        assertThat(exception.message).isEqualTo("Project with ID P2 does not exist")
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `should throw exception when project id is invalid`(projectId: String) {
+        // When && Then
+        assertThrows<Exception> {
+            getAllStatesByProjectIdUseCase.getAllStatesByProjectId(projectId)
+        }
+    }
+
+}
diff --git a/src/test/kotlin/logic/usecase/state/GetStateByIdUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/GetStateByIdUseCaseTest.kt
new file mode 100644
index 0000000..f6fd705
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/GetStateByIdUseCaseTest.kt
@@ -0,0 +1,59 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class GetStateByIdUseCaseTest {
+
+    private lateinit var getStateByIdUseCase: GetStateByIdUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        getStateByIdUseCase = GetStateByIdUseCase(stateRepository)
+    }
+
+    @Test
+    fun `should return state when valid state id exists`() {
+        // Given
+        val expectedState = State(id = "S1", name = "Active", projectId = "P1")
+        every { stateRepository.getStateById("S1") } returns expectedState
+
+        // When
+        val result = getStateByIdUseCase.getStateById("S1")
+
+        // Then
+        assertThat(result).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun `should throw exception when state id does not exist`() {
+        // Given
+        val input = "S2"
+        every { stateRepository.getStateById(any()) } returns null
+
+        // When
+        val exception = assertThrows<Exception> { getStateByIdUseCase.getStateById("S2") }
+
+        // Then
+        assertThat(exception.message).isEqualTo("State with ID $input does not exist")
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `should throw exception when state id is invalid`(stateId: String) {
+        // When && Then
+        assertThrows<Exception> {
+            getStateByIdUseCase.getStateById(stateId)
+        }
+    }
+
+}
diff --git a/src/test/kotlin/logic/usecase/state/GetStateByTaskIdUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/GetStateByTaskIdUseCaseTest.kt
new file mode 100644
index 0000000..bfd30a7
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/GetStateByTaskIdUseCaseTest.kt
@@ -0,0 +1,59 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.logic.repositories.TaskRepository
+import com.berlin.model.State
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class GetStateByTaskIdUseCaseTest {
+
+    private lateinit var getStateByTaskIdUseCase: GetStateByTaskIdUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+    private val taskRepository: TaskRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        getStateByTaskIdUseCase = GetStateByTaskIdUseCase(stateRepository, taskRepository)
+    }
+
+    @Test
+    fun `should return state when task id exists`() {
+        // Given
+        val expectedState = State(id = "S1", name = "Active", projectId = "P1")
+        every { taskRepository.getTaskById("T1") } returns mockk()
+        every { stateRepository.getStateByTaskId("T1") } returns expectedState
+
+        // When
+        val result = getStateByTaskIdUseCase.getStateByTaskId("T1")
+
+        // Then
+        assertThat(result).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun `should throw exception when task id does not exist`() {
+        // Given
+        every { taskRepository.getTaskById("T2") } returns null
+
+        // When & Then
+        val exception = assertThrows<Exception> { getStateByTaskIdUseCase.getStateByTaskId("T2") }
+        assertThat(exception.message).isEqualTo("State with ID T2 does not exist")
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `should throw exception when state id is invalid`(taskId: String) {
+        // When && Then
+        assertThrows<Exception> {
+            getStateByTaskIdUseCase.getStateByTaskId(taskId)
+        }
+    }
+
+}
diff --git a/src/test/kotlin/logic/usecase/state/GetTasksByStateIdUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/GetTasksByStateIdUseCaseTest.kt
new file mode 100644
index 0000000..52e183a
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/GetTasksByStateIdUseCaseTest.kt
@@ -0,0 +1,78 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.Task
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class GetTasksByStateIdUseCaseTest {
+
+    private lateinit var getTasksByStateIdUseCase: GetTasksByStateIdUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+
+    private val task = Task(
+        id = "T1",
+        projectId = "P1",
+        title = "Demo",
+        description = null,
+        stateId = "S5",
+        assignedTo = "1",
+        createBy = "2"
+    )
+
+    @BeforeEach
+    fun setup() {
+        getTasksByStateIdUseCase = GetTasksByStateIdUseCase(stateRepository)
+    }
+
+    @Test
+    fun `should return tasks when tasks are found for the state`() {
+        // Given
+        val expectedTasks = listOf(task)
+        every { stateRepository.getTasksByStateId("S1") } returns expectedTasks
+        every { stateRepository.getStateById("S1") } returns mockk()
+
+        // When
+        val result = getTasksByStateIdUseCase.getAllTasksByStateId("S1")
+
+        // Then
+        assertThat(result).isEqualTo(expectedTasks)
+    }
+
+    @Test
+    fun `should throw exception when no tasks are found for the state`() {
+        // Given
+        every { stateRepository.getTasksByStateId("S2") } returns null
+        every { stateRepository.getStateById("S2") } returns mockk()
+
+        // When & Then
+        val exception = assertThrows<Exception> { getTasksByStateIdUseCase.getAllTasksByStateId("S2") }
+        assertThat(exception.message).isEqualTo("No tasks found for state ID S2")
+    }
+
+    @Test
+    fun `should throw exception when state id does not exist`() {
+        // Given
+        every { stateRepository.getStateById("S2") } returns null
+
+        // When & Then
+        val exception = assertThrows<Exception> { getTasksByStateIdUseCase.getAllTasksByStateId("S2") }
+        assertThat(exception.message).isEqualTo("State with ID S2 does not exist")
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ", "123"])
+    fun `should throw exception when state id is invalid`(stateId: String) {
+        // When && Then
+        assertThrows<Exception> {
+            getTasksByStateIdUseCase.getAllTasksByStateId(stateId)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/logic/usecase/state/UpdateStateUseCaseTest.kt b/src/test/kotlin/logic/usecase/state/UpdateStateUseCaseTest.kt
new file mode 100644
index 0000000..78b3a08
--- /dev/null
+++ b/src/test/kotlin/logic/usecase/state/UpdateStateUseCaseTest.kt
@@ -0,0 +1,68 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.State
+import com.google.common.truth.Truth.assertThat
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.Test
+
+class UpdateStateUseCaseTest {
+
+    private lateinit var updateStateUseCase: UpdateStateUseCase
+    private val stateRepository: StateRepository = mockk(relaxed = true)
+
+    @BeforeEach
+    fun setup() {
+        updateStateUseCase = UpdateStateUseCase(stateRepository)
+    }
+
+    @Test
+    fun `should return success when state update succeeds`() {
+        // Given
+        val state = State(id = "S1", name = "Active", projectId = "P1")
+        every { stateRepository.updateState(state) } returns Result.success("Updated Successfully")
+
+        // When
+        val result = updateStateUseCase.updateState(state)
+
+        // Then
+        assertThat(result).isEqualTo(Result.success("Updated Successfully"))
+    }
+
+    @Test
+    fun `should return failure when state update fails`() {
+        // Given
+        val state = State(id = "S2", name = "Inactive", projectId = "P2")
+        every { stateRepository.updateState(state) } returns Result.failure(Exception())
+
+        // When
+        val result = updateStateUseCase.updateState(state)
+
+        // Then
+        result.onFailure { exception ->
+            assertThat(exception.message).isEqualTo("Update Failed")
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["", " ","123"])
+    fun `should throw exception when state ID is invalid`(stateName: String) {
+        // Given
+        val input = State(
+            "S1",
+            stateName,
+            "P1"
+        )
+
+        // When && Then
+        assertThrows<Exception> {
+            updateStateUseCase.updateState(input)
+        }
+    }
+
+}
\ No newline at end of file


Reviews:
user: login: mosayed01
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-05-01T05:11:33Z
user: login: Ahmed7osny1
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T08:33:56Z
user: login: mosayed01
state: APPROVED
author_association: NONE
submitted_at: 2025-05-01T09:18:59Z

Review comments:
diff_hunk: @@ -0,0 +1,11 @@
+package com.berlin.logic.generateIdHelper
+
+import kotlin.random.Random
+
+interface IdGenerator {
+    fun generateId(
+        input: String,
path: src/main/kotlin/logic/generateIdHelper/IdGenerator.kt
user: login: mosayed01
body: ```suggestion
        prefix: String,
```

created_at: 2025-05-01T05:04:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1,13 @@
+package com.berlin.logic.generateIdHelper
+
+class DefaultIdGenerator : IdGenerator {
path: src/main/kotlin/logic/generateIdHelper/generateId.kt
user: login: mosayed01
body: ```suggestion
class IdGeneratorIpml : IdGenerator {
```

created_at: 2025-05-01T05:04:57Z
author_association: NONE
diff_hunk: @@ -0,0 +1,31 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.generateIdHelper.DefaultIdGenerator
+import com.berlin.logic.repositories.AuditRepository
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.AuditLog
+import com.berlin.model.State
+
+class CreationStateUseCase(
+    private val stateRepository: StateRepository,
+    private val defaultIdGenerator: DefaultIdGenerator,
path: src/main/kotlin/logic/usecase/state/CreationStateUseCase.kt
user: login: mosayed01
body: Why u depend on implementation instead of abstraction?
created_at: 2025-05-01T05:06:11Z
author_association: NONE
diff_hunk: @@ -0,0 +1,31 @@
+package com.berlin.logic.usecase.state
+
+import com.berlin.logic.generateIdHelper.DefaultIdGenerator
+import com.berlin.logic.repositories.AuditRepository
+import com.berlin.logic.repositories.StateRepository
+import com.berlin.model.AuditLog
+import com.berlin.model.State
+
+class CreationStateUseCase(
+    private val stateRepository: StateRepository,
+    private val defaultIdGenerator: DefaultIdGenerator,
path: src/main/kotlin/logic/usecase/state/CreationStateUseCase.kt
user: login: Ahmed7osny1
body: you are right this mistake from me, Depending on an implementation violates (DIP)
created_at: 2025-05-01T08:33:55Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-01T03:17:16Z
body: ### Code Coverage Summary
> There is no coverage information present for the Files changed
author_association: NONE
user: login: mosayed01
created_at: 2025-05-01T08:52:02Z
body: @Ahmed7osny1 please check why there is a compile error
author_association: NONE