Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/kotlin/logic/generateIdHelper/DefaultIdGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.berlin.logic.generateIdHelper

class DefaultIdGenerator : IdGenerator {
override fun generateId(
input: String, padChar: Char, padCharLength: Int): String {
input.trim()
.replace(" ", "")
.ifEmpty { throw IllegalArgumentException("String must not be empty") }
.padEnd(padCharLength, padChar).let { paddedNumber ->
"${paddedNumber}_${System.currentTimeMillis() % 100000}"
}
return input
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/logic/generateIdHelper/IdGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.berlin.logic.generateIdHelper

import kotlin.random.Random

interface IdGenerator {
fun generateId(
input: String,
padChar: Char = input[input.length / 2],
padCharLength: Int = input.length + Random.nextInt(1, 5)
): String
}
11 changes: 11 additions & 0 deletions src/main/kotlin/logic/repositories/ProjectRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.berlin.logic.repositories

import com.berlin.model.Project

interface ProjectRepository {
fun createProject(project:Project): Result<String>
fun getProjectById(projectId:String): Project?
fun getAllProjects(): List<Project>?
fun updateProject(project: Project): Result<String>
fun deleteProject(projectId: String): Result<String>
}
31 changes: 31 additions & 0 deletions src/main/kotlin/logic/usecase/project/CreateProjectUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.berlin.logic.usecase.project

import com.berlin.logic.generateIdHelper.IdGenerator
import com.berlin.logic.repositories.ProjectRepository
import com.berlin.model.Project

class CreateProjectUseCase(
private val projectRepository: ProjectRepository,
private val idGenerator: IdGenerator,
) {
fun createNewProject(projectName: String, description: String?, stateId: List<String>?, taskId: List<String>?):
Result<String> {
if (validateProjectName(projectName)) {
val newProject = Project(
id = idGenerator.generateId(projectName),
name = projectName,
description = description,
statesId = stateId,
tasksId = taskId
)
return projectRepository.createProject(newProject)
.map { "Creation Successfully" }
.recover { "Creation Failed" }
} else {
throw Exception("Project Name must not be empty or blank")
}
}

private fun validateProjectName(projectName: String): Boolean =
projectName.isNotBlank() && !(projectName.all { it.isDigit() })
}
29 changes: 29 additions & 0 deletions src/main/kotlin/logic/usecase/project/DeleteProjectUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.berlin.logic.usecase.project

import com.berlin.logic.repositories.ProjectRepository

class DeleteProjectUseCase (
private val projectRepository: ProjectRepository
) {
fun deleteProject(projectId: String): Result<String> {

if(!validateProjectId(projectId))
throw Exception("Project ID must not be empty or blank")

if (!checkProjectExists(projectId)) {
return Result.failure(
Exception("Project with ID $projectId does not exist")
)
}

return projectRepository.deleteProject(projectId)
.map { "Deleted Successfully" }
.recover { "Deletion Failed" }
}

private fun validateProjectId(projectId: String): Boolean =
projectId.isNotBlank() && !(projectId.all { it.isDigit() })

private fun checkProjectExists(projectId: String): Boolean =
projectRepository.getProjectById(projectId) != null
}
13 changes: 13 additions & 0 deletions src/main/kotlin/logic/usecase/project/GetAllProjectsUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.berlin.logic.usecase.project

import com.berlin.logic.repositories.ProjectRepository
import com.berlin.model.Project

class GetAllProjectsUseCase(
private val projectRepository: ProjectRepository
) {
fun getAllProjects(): List<Project> {
return projectRepository.getAllProjects()
?: throw Exception("No projects found")
}
}
21 changes: 21 additions & 0 deletions src/main/kotlin/logic/usecase/project/GetProjectByIdUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.berlin.logic.usecase.project

import com.berlin.logic.repositories.ProjectRepository
import com.berlin.model.Project

class GetProjectByIdUseCase (
private val projectRepository: ProjectRepository
) {

fun getProjectById(projectId: String): Project {
if(!validateProjectId(projectId))
throw Exception("Project ID must not be empty or blank")

return projectRepository.getProjectById(projectId)
?: throw Exception("Project with ID $projectId does not exist")
}

private fun validateProjectId(projectId: String): Boolean =
projectId.isNotBlank() && !(projectId.all { it.isDigit() })

}
21 changes: 21 additions & 0 deletions src/main/kotlin/logic/usecase/project/UpdateProjectUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.berlin.logic.usecase.project

import com.berlin.logic.repositories.ProjectRepository
import com.berlin.model.Project

class UpdateProjectUseCase (
private val projectRepository: ProjectRepository
) {
fun updateProject(project: Project): Result<String> {
if(!validateProjectName(project.name))
throw Exception("Project Name must not be empty or blank")

return projectRepository.updateProject(project)
.map { "Updated Successfully" }
.recover { "Update Failed" }
}


private fun validateProjectName(projectName: String): Boolean =
projectName.isNotBlank() && !(projectName.all { it.isDigit() })
}
9 changes: 9 additions & 0 deletions src/main/kotlin/model/Project.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.berlin.model

data class Project(
val id:String,
val name:String,
val description:String?,
val statesId:List<String>?,
val tasksId:List<String>?
)
24 changes: 10 additions & 14 deletions src/main/kotlin/presentation/CategoryUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.berlin.presentation
import com.berlin.presentation.io.Reader
import com.berlin.presentation.io.Viewer
import data.UserCache
import java.util.*

class CategoryUI(
override val id: Int,
Expand All @@ -12,30 +11,27 @@ class CategoryUI(
private val viewer: Viewer,
private val reader: Reader,
private val userCache: UserCache
) : UiRunner {

): UiRunner {
private val allowedActions: List<PermissionedUiRunner> by lazy {
children.filter { it.isAllowed(userCache.currentPermission) }
}

override fun run() {
val permission = userCache.currentPermission
val allowed = children.filter { it.isAllowed(permission) }

if (allowed.isEmpty()) {
if (allowedActions.isEmpty()) {
viewer.show("No available actions in $label.")
return
}

while (true) {
viewer.show("=== $label ===")
allowed.sortedBy { it.id }
.forEach { viewer.show("${it.id} – ${it.label}") }
allowedActions.sortedBy { it.id }.forEach { viewer.show("${it.id} – ${it.label}") }
viewer.show("X – Back")

when (val input = reader.read()?.trim()?.uppercase(Locale.getDefault())) {
null,"X" -> return
else -> allowed
.firstOrNull { it.id == input.toIntOrNull() }
?.run()
?: viewer.show("Invalid choice")
when (val input = reader.read()?.trim()?.uppercase()) {
null, "X" -> return
else -> allowedActions.firstOrNull { it.id == input.toIntOrNull() }
?.run() ?: viewer.show("Invalid option: $input. Please select a valid action.")
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/helper/projectHelper.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.berlin.helper

import com.berlin.domain.model.Project
import com.berlin.model.Project

fun projectHelper(
id: String = "123",
Expand Down
82 changes: 82 additions & 0 deletions src/test/kotlin/logic/usecase/project/CreateProjectUseCaseTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package logic.usecase.project;

import com.berlin.helper.projectHelper
import com.berlin.logic.generateIdHelper.IdGenerator
import com.berlin.logic.repositories.ProjectRepository
import com.berlin.logic.usecase.project.CreateProjectUseCase
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 CreateProjectUseCaseTest {

private lateinit var createProjectUseCase: CreateProjectUseCase
private val projectRepository: ProjectRepository = mockk(relaxed = true)


@BeforeEach
fun setup() {
val idGenerator: IdGenerator = mockk(relaxed = true)
createProjectUseCase = CreateProjectUseCase(projectRepository, idGenerator)
}

@Test
fun `createNewProject should return success when project created successfully`() {
// Given
val validProject = projectHelper()
every { projectRepository.createProject(any()) } returns Result.success("Creation Successfully")

// When
val result = createProjectUseCase.createNewProject(
validProject.name,
validProject.description,
validProject.statesId,
validProject.tasksId
)

// Then
assertThat(result).isEqualTo(Result.success("Creation Successfully"))
}

@Test
fun `createNewProject should return failure when project creation fails`() {
// Given
val validProject = projectHelper()
every { projectRepository.createProject(any()) } returns Result.failure(Exception())

// When
val result = createProjectUseCase.createNewProject(
validProject.name,
validProject.description,
validProject.statesId,
validProject.tasksId
)

// Then
result.onFailure { exception ->
assertThat(exception.message).isEqualTo("Creation Failed")
}
}


@ParameterizedTest
@ValueSource(strings = ["", " ", "123"])
fun `validateProjectName should throw exception when project name is invalid`(
invalidName: String
) {
// When && Then
assertThrows<Exception> {
createProjectUseCase.createNewProject(invalidName,
null,
null,
null
)
}
}

}
74 changes: 74 additions & 0 deletions src/test/kotlin/logic/usecase/project/DeleteProjectUseCaseTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package logic.usecase.project;

import com.berlin.logic.repositories.ProjectRepository
import com.berlin.logic.usecase.project.DeleteProjectUseCase
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.CsvSource
import org.junit.jupiter.params.provider.ValueSource
import kotlin.test.Test

class DeleteProjectUseCaseTest {

private lateinit var deleteProjectUseCase: DeleteProjectUseCase
private val projectRepository: ProjectRepository = mockk(relaxed = true)

@BeforeEach
fun setup() {
deleteProjectUseCase = DeleteProjectUseCase(projectRepository)
}

@Test
fun `should return success when project deleted successfully`() {
// Given
every { projectRepository.deleteProject(any()) } returns Result.success("")

// When
val result = deleteProjectUseCase.deleteProject("project_1")

// Then
assertThat(result).isEqualTo(Result.success("Deleted Successfully"))
}

@Test
fun `should return failure when project deletion fails`() {
// Given
every { projectRepository.deleteProject("P1") } returns Result.failure(Exception())

// When
val result = deleteProjectUseCase.deleteProject("P1")

// Then
result.onFailure { exception ->
assertThat(exception.message).isEqualTo("Deletion Failed")
}
}

@Test
fun `should throw exception when project id does not exists`() {
// Given
every { projectRepository.getProjectById(any()) } returns null

// When
val result = deleteProjectUseCase.deleteProject("P2")

// Then
result.onFailure { exception ->
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> {
deleteProjectUseCase.deleteProject(projectId)
}
}

}
Loading
Loading