From 8bee9ef35aee3cd4f73e2e14056070505bf7a268 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Tue, 29 Apr 2025 05:33:57 +0300 Subject: [PATCH 01/15] add generic interface base repository,generic csv data source and add unit test for method read all,implement it --- build.gradle.kts | 2 +- src/main/kotlin/data/BaseRepository.kt | 9 ++ src/main/kotlin/data/CsvDataSource.kt | 50 +++++++ src/test/kotlin/data/CsvDataSourceTest.kt | 162 ++++++++++++++++++++++ 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/data/BaseRepository.kt create mode 100644 src/main/kotlin/data/CsvDataSource.kt create mode 100644 src/test/kotlin/data/CsvDataSourceTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 489419d..1ec43bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ repositories { dependencies { implementation("io.insert-koin:koin-core:4.0.4") implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.6.2") - + implementation("com.opencsv:opencsv:5.7.1") testImplementation(kotlin("test")) testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("io.mockk:mockk:1.14.0") diff --git a/src/main/kotlin/data/BaseRepository.kt b/src/main/kotlin/data/BaseRepository.kt new file mode 100644 index 0000000..63d608b --- /dev/null +++ b/src/main/kotlin/data/BaseRepository.kt @@ -0,0 +1,9 @@ +package com.berlin.data + +interface BaseRepository { + fun write(row:T):Boolean + fun writeAll(rows:List):Boolean + fun readAll():List + fun update(id:String,row: T):Boolean + fun deleteById(id: String):Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/data/CsvDataSource.kt b/src/main/kotlin/data/CsvDataSource.kt new file mode 100644 index 0000000..32222e7 --- /dev/null +++ b/src/main/kotlin/data/CsvDataSource.kt @@ -0,0 +1,50 @@ +package com.berlin.data + +import com.opencsv.CSVReaderBuilder +import java.io.File +import java.io.FileNotFoundException +import java.io.FileReader + + +abstract class CsvDataSource( + private val filePath: String, + private val headers: List, + private val toRow: (T) -> List, + private val fromRow: (List) -> T? +) : BaseRepository { + override fun write(row: T): Boolean { + TODO("Not yet implemented") + } + + override fun writeAll(rows: List): Boolean { + TODO("Not yet implemented") + } + + override fun readAll(): List { + val file = File(filePath) + if (!file.exists()) + throw FileNotFoundException("File not found: $filePath") + + return FileReader(file).use { reader -> + CSVReaderBuilder(reader) + .withSkipLines(1) + .build() + .use { csvReader -> + csvReader + .asSequence() + .filter { row->row.isNotEmpty() } + .mapNotNull { row -> fromRow(row.toList()) } + .toList() + } + } + } + + override fun deleteById(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun update(id: String, row: T): Boolean { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/test/kotlin/data/CsvDataSourceTest.kt b/src/test/kotlin/data/CsvDataSourceTest.kt new file mode 100644 index 0000000..617a9d2 --- /dev/null +++ b/src/test/kotlin/data/CsvDataSourceTest.kt @@ -0,0 +1,162 @@ +package com.berlin.data + +import com.berlin.model.Project +import com.google.common.truth.Truth.assertThat +import com.opencsv.CSVWriter +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.assertThrows +import java.io.File +import java.io.FileNotFoundException +import java.io.FileWriter +import kotlin.test.Test + + +class CsvDataSourceTest { + + private lateinit var dataSource: CsvDataSource + private lateinit var testFile: File + + + @BeforeEach + fun setUp() { + testFile = File("test_projects.csv") + dataSource = createDataSource(testFile.path) + } + + @AfterEach + fun clear() { + if (testFile.exists()) testFile.delete() + } + + //region readAll test + @Test + fun `readAll should return throw exception when file is not exist`() { + // Given + dataSource = createDataSource("path") + // When // then + assertThrows { dataSource.readAll() } + } + + @Test + fun `readAll should return list of projects when valid file`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + ) + ) + // When + val result = dataSource.readAll() + // Then + assertThat(result.size).isEqualTo(1) + } + + @Test + fun `readAll should return empty list when file is empty`() { + // Given + writeInCsv(testFile, emptyList()) + // When + val result = dataSource.readAll() + // Then + assertThat(result).isEmpty() + } + + @Test + fun `readAll should return empty list when file is only have invalid row`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Description1"), + ) + ) + // When + val result = dataSource.readAll() + // Then + assertThat(result).isEmpty() + } + + @Test + fun `readAll should return empty list when file is only have the header row`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId") + ) + ) + // When + val result = dataSource.readAll() + // Then + assertThat(result).isEmpty() + } + + @Test + fun `readAll should return valid rows when file is have valid rows`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]") + ) + ) + // When + val result = dataSource.readAll() + // Then + assertThat(result.size).isEqualTo(2) + } + + @Test + fun `readAll should return empty list when file have only spaces`() { + // Given + writeInCsv( + testFile, listOf( + listOf("", "", "", "", ""), + listOf("", "", "", "", ""), + listOf("", "", "", "", "") + ) + ) + // When + val result = dataSource.readAll() + // Then + assertThat(result).isEmpty() + } + //endregion + + private fun writeInCsv(file: File, rows: List>) { + FileWriter(file).use { writer -> + CSVWriter(writer).use { csvWriter -> + rows.forEach { row -> csvWriter.writeNext(row.toTypedArray()) } + } + } + } + + private fun testFromRowMethodForProject(row:List):Project?{ + return row.takeIf { row.size == 5 && row.all { cell -> cell.isNotBlank() } + }?.let { + Project( + id = row[0], + name = row[1], + description = row[2], + statesId = row[3] + .removeSurrounding("[", "]") + .split(","), + tasksId = row[4] + .removeSurrounding("[", "]") + .split(",") + ) + } + } + + private fun createDataSource(filePath: String): CsvDataSource { + return object : CsvDataSource( + filePath = filePath, + headers = listOf("id", "name", "description", "statesId", "tasksId"), + toRow = { emptyList() }, + fromRow = ::testFromRowMethodForProject + ) {} + } + +} \ No newline at end of file From 06e4e90d2e83d67e2772a35cb77815b205610250 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Tue, 29 Apr 2025 08:27:43 +0300 Subject: [PATCH 02/15] add unit test of method update --- src/test/kotlin/data/CsvDataSourceTest.kt | 235 +++++++++++++++++++++- 1 file changed, 233 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/data/CsvDataSourceTest.kt b/src/test/kotlin/data/CsvDataSourceTest.kt index 617a9d2..94e260f 100644 --- a/src/test/kotlin/data/CsvDataSourceTest.kt +++ b/src/test/kotlin/data/CsvDataSourceTest.kt @@ -31,7 +31,7 @@ class CsvDataSourceTest { //region readAll test @Test - fun `readAll should return throw exception when file is not exist`() { + fun `readAll should return throw FileNotFoundException when file is not exist`() { // Given dataSource = createDataSource("path") // When // then @@ -124,6 +124,219 @@ class CsvDataSourceTest { assertThat(result).isEmpty() } //endregion + // region update test + @Test + fun `update should throw FileNotFoundException when file does not exist`() { + // Given + dataSource = createDataSource("path") + val updatedProject = Project( + id = "1", + name = "UpdatedProject", + description = "UpdatedDescription", + statesId = listOf("state4", "state5"), + tasksId = listOf("task4") + ) + // When // Then + assertThrows { dataSource.update("1", updatedProject) } + } + + @Test + fun `update should replace project with matching id when valid file`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll().find { it.id == "1" }).isEqualTo(UPDATED_PROJECT) + } + + @Test + fun `update should keep other row unchanged when replacing row with matching id`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll().find { it.id == "2" }?.name).isEqualTo("Project2") + } + + @Test + fun `update should do nothing when id does not exist`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + dataSource.update("3", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll().find { it.id == "3" }).isNull() + } + + @Test + fun `update should return false id does not exist`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + val result=dataSource.update("3", UPDATED_PROJECT) + // Then + assertThat(result).isFalse() + } + + @Test + fun `update should do nothing when file is empty`() { + // Given + writeInCsv(testFile, emptyList()) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll()).isEmpty() + } + + @Test + fun `update should return false when file is empty`() { + // Given + writeInCsv(testFile, emptyList()) + // When + val result=dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(result).isFalse() + } + + @Test + fun `update should do nothing when file has only header row`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId") + ) + ) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll()).isEmpty() + } + + @Test + fun `update should return false when file has only header row`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId") + ) + ) + // When + val result=dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(result).isFalse() + } + + @Test + fun `update should replace project with matching id when multiple valid rows`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project123", "Description16", "[state1,state23]", "[task133,task352]"), + listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll().find { it.id == "1" }).isEqualTo(UPDATED_PROJECT) + } + + @Test + fun `update should return true when when multiple valid rows`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project123", "Description16", "[state1,state23]", "[task133,task352]"), + listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + val result=dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(result).isTrue() + } + + @Test + fun `update should keep other rows unchanged when multiple valid rows`() { + // Given + writeInCsv( + testFile, listOf( + listOf("id", "name", "description", "statesId", "tasksId"), + listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), + listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), + listOf("2", "Project2", "Description2", "[state3]", "[task3]") + ) + ) + // When + dataSource.update("165", UPDATED_PROJECT) + + // Then + assertThat(dataSource.readAll().find { it.id == "1" }?.name).isEqualTo("Project1") + } + + @Test + fun `update should do nothing when file has only blank cells`() { + // Given + writeInCsv( + testFile, listOf( + listOf("", "", "", "", ""), + listOf("", "", "", "", ""), + listOf("", "", "", "", "") + ) + ) + // When + dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(dataSource.readAll()).isEmpty() + } + + @Test + fun `update should return false when file has only blank cells`() { + // Given + writeInCsv( + testFile, listOf( + listOf("", "", "", "", ""), + listOf("", "", "", "", ""), + listOf("", "", "", "", "") + ) + ) + // When + val result=dataSource.update("1", UPDATED_PROJECT) + // Then + assertThat(result).isFalse() + } + //endregion + private fun writeInCsv(file: File, rows: List>) { FileWriter(file).use { writer -> @@ -150,13 +363,31 @@ class CsvDataSourceTest { } } + private fun testToRowMethodForProject(project: Project):List { + return listOf( + project.id, + project.name, + project.description.toString(), + "[${project.statesId.joinToString(",")}]", + "[${project.tasksId.joinToString(",")}]") + } + private fun createDataSource(filePath: String): CsvDataSource { return object : CsvDataSource( filePath = filePath, headers = listOf("id", "name", "description", "statesId", "tasksId"), - toRow = { emptyList() }, + toRow = ::testToRowMethodForProject , fromRow = ::testFromRowMethodForProject ) {} } + companion object{ + val UPDATED_PROJECT=Project( + id = "1", + name = "UpdatedProject", + description = "UpdatedDescription", + statesId = listOf("state4", "state5"), + tasksId = listOf("task4") + ) + } } \ No newline at end of file From dc2ab1651a459e760e7d24c90c96b26282ba12cb Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 00:12:40 +0300 Subject: [PATCH 03/15] change structure of data layer --- src/main/kotlin/data/BaseDataSource.kt | 17 + src/main/kotlin/data/BaseRepository.kt | 9 - src/main/kotlin/data/BaseSchema.kt | 10 + src/main/kotlin/data/CsvDataSource.kt | 50 --- .../data/csv_data_source/CsvDataSource.kt | 36 ++ src/main/kotlin/data/schema/AuditSchema.kt | 22 + src/main/kotlin/data/schema/ProjectSchema.kt | 22 + src/main/kotlin/data/schema/StateSchema.kt | 22 + src/main/kotlin/data/schema/TaskSchema.kt | 22 + src/main/kotlin/data/schema/UserSchema.kt | 22 + src/test/kotlin/data/CsvDataSourceTest.kt | 393 ------------------ 11 files changed, 173 insertions(+), 452 deletions(-) create mode 100644 src/main/kotlin/data/BaseDataSource.kt delete mode 100644 src/main/kotlin/data/BaseRepository.kt create mode 100644 src/main/kotlin/data/BaseSchema.kt delete mode 100644 src/main/kotlin/data/CsvDataSource.kt create mode 100644 src/main/kotlin/data/csv_data_source/CsvDataSource.kt create mode 100644 src/main/kotlin/data/schema/AuditSchema.kt create mode 100644 src/main/kotlin/data/schema/ProjectSchema.kt create mode 100644 src/main/kotlin/data/schema/StateSchema.kt create mode 100644 src/main/kotlin/data/schema/TaskSchema.kt create mode 100644 src/main/kotlin/data/schema/UserSchema.kt delete mode 100644 src/test/kotlin/data/CsvDataSourceTest.kt diff --git a/src/main/kotlin/data/BaseDataSource.kt b/src/main/kotlin/data/BaseDataSource.kt new file mode 100644 index 0000000..8ec4838 --- /dev/null +++ b/src/main/kotlin/data/BaseDataSource.kt @@ -0,0 +1,17 @@ +package com.berlin.data + +interface BaseDataSource { + + fun getAll(): List + + fun getById(id: String): T? + + fun update(id: String, entity: T): Boolean + + fun delete(id: String): Boolean + + fun write(entity: T): Boolean + + fun writeAll(entities: List): Boolean + +} \ No newline at end of file diff --git a/src/main/kotlin/data/BaseRepository.kt b/src/main/kotlin/data/BaseRepository.kt deleted file mode 100644 index 63d608b..0000000 --- a/src/main/kotlin/data/BaseRepository.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.berlin.data - -interface BaseRepository { - fun write(row:T):Boolean - fun writeAll(rows:List):Boolean - fun readAll():List - fun update(id:String,row: T):Boolean - fun deleteById(id: String):Boolean -} \ No newline at end of file diff --git a/src/main/kotlin/data/BaseSchema.kt b/src/main/kotlin/data/BaseSchema.kt new file mode 100644 index 0000000..b3f867c --- /dev/null +++ b/src/main/kotlin/data/BaseSchema.kt @@ -0,0 +1,10 @@ +package com.berlin.data + +interface BaseSchema { + val fileName: String + val header: List + + fun toRow(entity: T): List + fun fromRow(row: List): T + fun getId(entity: T): String +} \ No newline at end of file diff --git a/src/main/kotlin/data/CsvDataSource.kt b/src/main/kotlin/data/CsvDataSource.kt deleted file mode 100644 index 32222e7..0000000 --- a/src/main/kotlin/data/CsvDataSource.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.berlin.data - -import com.opencsv.CSVReaderBuilder -import java.io.File -import java.io.FileNotFoundException -import java.io.FileReader - - -abstract class CsvDataSource( - private val filePath: String, - private val headers: List, - private val toRow: (T) -> List, - private val fromRow: (List) -> T? -) : BaseRepository { - override fun write(row: T): Boolean { - TODO("Not yet implemented") - } - - override fun writeAll(rows: List): Boolean { - TODO("Not yet implemented") - } - - override fun readAll(): List { - val file = File(filePath) - if (!file.exists()) - throw FileNotFoundException("File not found: $filePath") - - return FileReader(file).use { reader -> - CSVReaderBuilder(reader) - .withSkipLines(1) - .build() - .use { csvReader -> - csvReader - .asSequence() - .filter { row->row.isNotEmpty() } - .mapNotNull { row -> fromRow(row.toList()) } - .toList() - } - } - } - - override fun deleteById(id: String): Boolean { - TODO("Not yet implemented") - } - - override fun update(id: String, row: T): Boolean { - TODO("Not yet implemented") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/data/csv_data_source/CsvDataSource.kt b/src/main/kotlin/data/csv_data_source/CsvDataSource.kt new file mode 100644 index 0000000..7993d28 --- /dev/null +++ b/src/main/kotlin/data/csv_data_source/CsvDataSource.kt @@ -0,0 +1,36 @@ +package com.berlin.data.csv_data_source + +import com.berlin.data.BaseDataSource +import com.berlin.data.BaseSchema + +class CsvDataSource( + private val rootDirectory: String, + private val schema: BaseSchema +) : BaseDataSource { + + override fun getAll(): List { + return emptyList() + } + + override fun getById(id: String): T? { + return null + } + + override fun update(id: String, entity: T): Boolean { + return false + } + + override fun delete(id: String): Boolean { + return false + } + + override fun write(entity: T): Boolean { + return false + } + + override fun writeAll(entities: List): Boolean { + return false + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/data/schema/AuditSchema.kt b/src/main/kotlin/data/schema/AuditSchema.kt new file mode 100644 index 0000000..1da801f --- /dev/null +++ b/src/main/kotlin/data/schema/AuditSchema.kt @@ -0,0 +1,22 @@ +package com.berlin.data.schema + +import com.berlin.data.BaseSchema +import com.berlin.model.AuditLog + +class AuditSchema( + override val fileName: String, + override val header: List +) :BaseSchema { + override fun toRow(entity: AuditLog): List { + TODO("Not yet implemented") + } + + override fun fromRow(row:List): AuditLog { + TODO("Not yet implemented") + } + + override fun getId(entity: AuditLog): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/data/schema/ProjectSchema.kt b/src/main/kotlin/data/schema/ProjectSchema.kt new file mode 100644 index 0000000..f95b3bf --- /dev/null +++ b/src/main/kotlin/data/schema/ProjectSchema.kt @@ -0,0 +1,22 @@ +package com.berlin.data.schema + +import com.berlin.data.BaseSchema +import com.berlin.model.Project + +class ProjectSchema ( + override val fileName: String, + override val header: List +) : BaseSchema { + override fun toRow(entity: Project): List { + TODO("Not yet implemented") + } + + override fun fromRow(row:List): Project { + TODO("Not yet implemented") + } + + override fun getId(entity: Project): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/data/schema/StateSchema.kt b/src/main/kotlin/data/schema/StateSchema.kt new file mode 100644 index 0000000..06bc95a --- /dev/null +++ b/src/main/kotlin/data/schema/StateSchema.kt @@ -0,0 +1,22 @@ +package com.berlin.data.schema + +import com.berlin.data.BaseSchema +import com.berlin.model.State + +class StateSchema( + override val fileName: String, + override val header: List +) : BaseSchema { + override fun toRow(entity: State): List { + TODO("Not yet implemented") + } + + override fun fromRow(row:List): State { + TODO("Not yet implemented") + } + + override fun getId(entity: State): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/data/schema/TaskSchema.kt b/src/main/kotlin/data/schema/TaskSchema.kt new file mode 100644 index 0000000..e06d9a6 --- /dev/null +++ b/src/main/kotlin/data/schema/TaskSchema.kt @@ -0,0 +1,22 @@ +package com.berlin.data.schema + +import com.berlin.data.BaseSchema +import com.berlin.model.Task + +class TaskSchema( + override val fileName: String, + override val header: List +) : BaseSchema { + override fun toRow(entity: Task): List { + TODO("Not yet implemented") + } + + override fun fromRow(row:List): Task { + TODO("Not yet implemented") + } + + override fun getId(entity: Task): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/data/schema/UserSchema.kt b/src/main/kotlin/data/schema/UserSchema.kt new file mode 100644 index 0000000..b833aac --- /dev/null +++ b/src/main/kotlin/data/schema/UserSchema.kt @@ -0,0 +1,22 @@ +package com.berlin.data.schema + +import com.berlin.data.BaseSchema +import com.berlin.model.User + +class UserSchema( + override val fileName: String, + override val header: List +) : BaseSchema { + override fun toRow(entity: User): List { + TODO("Not yet implemented") + } + + override fun fromRow(row:List): User { + TODO("Not yet implemented") + } + + override fun getId(entity: User): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/src/test/kotlin/data/CsvDataSourceTest.kt b/src/test/kotlin/data/CsvDataSourceTest.kt deleted file mode 100644 index 94e260f..0000000 --- a/src/test/kotlin/data/CsvDataSourceTest.kt +++ /dev/null @@ -1,393 +0,0 @@ -package com.berlin.data - -import com.berlin.model.Project -import com.google.common.truth.Truth.assertThat -import com.opencsv.CSVWriter -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.assertThrows -import java.io.File -import java.io.FileNotFoundException -import java.io.FileWriter -import kotlin.test.Test - - -class CsvDataSourceTest { - - private lateinit var dataSource: CsvDataSource - private lateinit var testFile: File - - - @BeforeEach - fun setUp() { - testFile = File("test_projects.csv") - dataSource = createDataSource(testFile.path) - } - - @AfterEach - fun clear() { - if (testFile.exists()) testFile.delete() - } - - //region readAll test - @Test - fun `readAll should return throw FileNotFoundException when file is not exist`() { - // Given - dataSource = createDataSource("path") - // When // then - assertThrows { dataSource.readAll() } - } - - @Test - fun `readAll should return list of projects when valid file`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - ) - ) - // When - val result = dataSource.readAll() - // Then - assertThat(result.size).isEqualTo(1) - } - - @Test - fun `readAll should return empty list when file is empty`() { - // Given - writeInCsv(testFile, emptyList()) - // When - val result = dataSource.readAll() - // Then - assertThat(result).isEmpty() - } - - @Test - fun `readAll should return empty list when file is only have invalid row`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Description1"), - ) - ) - // When - val result = dataSource.readAll() - // Then - assertThat(result).isEmpty() - } - - @Test - fun `readAll should return empty list when file is only have the header row`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId") - ) - ) - // When - val result = dataSource.readAll() - // Then - assertThat(result).isEmpty() - } - - @Test - fun `readAll should return valid rows when file is have valid rows`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]") - ) - ) - // When - val result = dataSource.readAll() - // Then - assertThat(result.size).isEqualTo(2) - } - - @Test - fun `readAll should return empty list when file have only spaces`() { - // Given - writeInCsv( - testFile, listOf( - listOf("", "", "", "", ""), - listOf("", "", "", "", ""), - listOf("", "", "", "", "") - ) - ) - // When - val result = dataSource.readAll() - // Then - assertThat(result).isEmpty() - } - //endregion - // region update test - @Test - fun `update should throw FileNotFoundException when file does not exist`() { - // Given - dataSource = createDataSource("path") - val updatedProject = Project( - id = "1", - name = "UpdatedProject", - description = "UpdatedDescription", - statesId = listOf("state4", "state5"), - tasksId = listOf("task4") - ) - // When // Then - assertThrows { dataSource.update("1", updatedProject) } - } - - @Test - fun `update should replace project with matching id when valid file`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll().find { it.id == "1" }).isEqualTo(UPDATED_PROJECT) - } - - @Test - fun `update should keep other row unchanged when replacing row with matching id`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll().find { it.id == "2" }?.name).isEqualTo("Project2") - } - - @Test - fun `update should do nothing when id does not exist`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - dataSource.update("3", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll().find { it.id == "3" }).isNull() - } - - @Test - fun `update should return false id does not exist`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - val result=dataSource.update("3", UPDATED_PROJECT) - // Then - assertThat(result).isFalse() - } - - @Test - fun `update should do nothing when file is empty`() { - // Given - writeInCsv(testFile, emptyList()) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll()).isEmpty() - } - - @Test - fun `update should return false when file is empty`() { - // Given - writeInCsv(testFile, emptyList()) - // When - val result=dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(result).isFalse() - } - - @Test - fun `update should do nothing when file has only header row`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId") - ) - ) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll()).isEmpty() - } - - @Test - fun `update should return false when file has only header row`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId") - ) - ) - // When - val result=dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(result).isFalse() - } - - @Test - fun `update should replace project with matching id when multiple valid rows`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project123", "Description16", "[state1,state23]", "[task133,task352]"), - listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll().find { it.id == "1" }).isEqualTo(UPDATED_PROJECT) - } - - @Test - fun `update should return true when when multiple valid rows`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project123", "Description16", "[state1,state23]", "[task133,task352]"), - listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - val result=dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(result).isTrue() - } - - @Test - fun `update should keep other rows unchanged when multiple valid rows`() { - // Given - writeInCsv( - testFile, listOf( - listOf("id", "name", "description", "statesId", "tasksId"), - listOf("1", "Project1", "Description1", "[state1,state2]", "[task1,task2]"), - listOf("165", "Project31", "Description6471", "[state17,state44,state28]", "[task91,task20,task70]"), - listOf("2", "Project2", "Description2", "[state3]", "[task3]") - ) - ) - // When - dataSource.update("165", UPDATED_PROJECT) - - // Then - assertThat(dataSource.readAll().find { it.id == "1" }?.name).isEqualTo("Project1") - } - - @Test - fun `update should do nothing when file has only blank cells`() { - // Given - writeInCsv( - testFile, listOf( - listOf("", "", "", "", ""), - listOf("", "", "", "", ""), - listOf("", "", "", "", "") - ) - ) - // When - dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(dataSource.readAll()).isEmpty() - } - - @Test - fun `update should return false when file has only blank cells`() { - // Given - writeInCsv( - testFile, listOf( - listOf("", "", "", "", ""), - listOf("", "", "", "", ""), - listOf("", "", "", "", "") - ) - ) - // When - val result=dataSource.update("1", UPDATED_PROJECT) - // Then - assertThat(result).isFalse() - } - //endregion - - - private fun writeInCsv(file: File, rows: List>) { - FileWriter(file).use { writer -> - CSVWriter(writer).use { csvWriter -> - rows.forEach { row -> csvWriter.writeNext(row.toTypedArray()) } - } - } - } - - private fun testFromRowMethodForProject(row:List):Project?{ - return row.takeIf { row.size == 5 && row.all { cell -> cell.isNotBlank() } - }?.let { - Project( - id = row[0], - name = row[1], - description = row[2], - statesId = row[3] - .removeSurrounding("[", "]") - .split(","), - tasksId = row[4] - .removeSurrounding("[", "]") - .split(",") - ) - } - } - - private fun testToRowMethodForProject(project: Project):List { - return listOf( - project.id, - project.name, - project.description.toString(), - "[${project.statesId.joinToString(",")}]", - "[${project.tasksId.joinToString(",")}]") - } - - private fun createDataSource(filePath: String): CsvDataSource { - return object : CsvDataSource( - filePath = filePath, - headers = listOf("id", "name", "description", "statesId", "tasksId"), - toRow = ::testToRowMethodForProject , - fromRow = ::testFromRowMethodForProject - ) {} - } - - companion object{ - val UPDATED_PROJECT=Project( - id = "1", - name = "UpdatedProject", - description = "UpdatedDescription", - statesId = listOf("state4", "state5"), - tasksId = listOf("task4") - ) - } -} \ No newline at end of file From f8362f88f580364f3bb5278d28b7501dc72a9aba Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 02:04:25 +0300 Subject: [PATCH 04/15] add indexes file --- src/main/kotlin/data/indexes.kt | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/kotlin/data/indexes.kt diff --git a/src/main/kotlin/data/indexes.kt b/src/main/kotlin/data/indexes.kt new file mode 100644 index 0000000..a29aa91 --- /dev/null +++ b/src/main/kotlin/data/indexes.kt @@ -0,0 +1,43 @@ +package com.berlin.data + +object UserIndex { + const val ID = 0 + const val USER_NAME = 1 + const val PASSWORD = 2 + const val ROLE = 3 +} + +object TaskIndex { + const val ID = 0 + const val PROJECT_ID = 1 + const val TITLE = 2 + const val DESCRIPTION = 3 + const val STATE_ID = 4 + const val ASSIGNED_TO = 5 + const val CREATE_BY = 6 + const val AUDIT_LOGS = 7 +} + +object StateIndex { + const val ID = 0 + const val NAME = 1 + const val PROJECT_ID = 2 +} + +object ProjectIndex { + const val ID = 0 + const val NAME = 1 + const val DESCRIPTION = 2 + const val STATES_ID = 3 + const val TASKS_ID = 4 +} + +object AuditLogIndex { + const val ID = 0 + const val TIMES_TAMP = 1 + const val CREATE_BY = 2 + const val AUDIT_ACTION = 3 + const val CHANGES_DESCRIPTION = 4 + const val ENTITY_TYPE = 5 + const val ENTITY_ID = 6 +} \ No newline at end of file From 24107e5afe9ae29325d0cf7a1d5a770926a13d9e Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 02:23:32 +0300 Subject: [PATCH 05/15] make entity nullable in method from row and replace todo to default value --- src/main/kotlin/data/BaseSchema.kt | 2 +- src/main/kotlin/data/schema/AuditSchema.kt | 8 ++++---- src/main/kotlin/data/schema/ProjectSchema.kt | 8 ++++---- src/main/kotlin/data/schema/StateSchema.kt | 8 ++++---- src/main/kotlin/data/schema/TaskSchema.kt | 8 ++++---- src/main/kotlin/data/schema/UserSchema.kt | 8 ++++---- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/data/BaseSchema.kt b/src/main/kotlin/data/BaseSchema.kt index b3f867c..9d620d4 100644 --- a/src/main/kotlin/data/BaseSchema.kt +++ b/src/main/kotlin/data/BaseSchema.kt @@ -5,6 +5,6 @@ interface BaseSchema { val header: List fun toRow(entity: T): List - fun fromRow(row: List): T + fun fromRow(row: List): T? fun getId(entity: T): String } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/AuditSchema.kt b/src/main/kotlin/data/schema/AuditSchema.kt index 1da801f..4043180 100644 --- a/src/main/kotlin/data/schema/AuditSchema.kt +++ b/src/main/kotlin/data/schema/AuditSchema.kt @@ -8,15 +8,15 @@ class AuditSchema( override val header: List ) :BaseSchema { override fun toRow(entity: AuditLog): List { - TODO("Not yet implemented") + return emptyList() } - override fun fromRow(row:List): AuditLog { - TODO("Not yet implemented") + override fun fromRow(row:List): AuditLog? { + return null } override fun getId(entity: AuditLog): String { - TODO("Not yet implemented") + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/ProjectSchema.kt b/src/main/kotlin/data/schema/ProjectSchema.kt index f95b3bf..71cfb44 100644 --- a/src/main/kotlin/data/schema/ProjectSchema.kt +++ b/src/main/kotlin/data/schema/ProjectSchema.kt @@ -8,15 +8,15 @@ class ProjectSchema ( override val header: List ) : BaseSchema { override fun toRow(entity: Project): List { - TODO("Not yet implemented") + return emptyList() } - override fun fromRow(row:List): Project { - TODO("Not yet implemented") + override fun fromRow(row:List): Project? { + return null } override fun getId(entity: Project): String { - TODO("Not yet implemented") + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/StateSchema.kt b/src/main/kotlin/data/schema/StateSchema.kt index 06bc95a..79b0729 100644 --- a/src/main/kotlin/data/schema/StateSchema.kt +++ b/src/main/kotlin/data/schema/StateSchema.kt @@ -8,15 +8,15 @@ class StateSchema( override val header: List ) : BaseSchema { override fun toRow(entity: State): List { - TODO("Not yet implemented") + return emptyList() } - override fun fromRow(row:List): State { - TODO("Not yet implemented") + override fun fromRow(row:List): State? { + return null } override fun getId(entity: State): String { - TODO("Not yet implemented") + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/TaskSchema.kt b/src/main/kotlin/data/schema/TaskSchema.kt index e06d9a6..c37b76b 100644 --- a/src/main/kotlin/data/schema/TaskSchema.kt +++ b/src/main/kotlin/data/schema/TaskSchema.kt @@ -8,15 +8,15 @@ class TaskSchema( override val header: List ) : BaseSchema { override fun toRow(entity: Task): List { - TODO("Not yet implemented") + return emptyList() } - override fun fromRow(row:List): Task { - TODO("Not yet implemented") + override fun fromRow(row:List): Task? { + return null } override fun getId(entity: Task): String { - TODO("Not yet implemented") + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/UserSchema.kt b/src/main/kotlin/data/schema/UserSchema.kt index b833aac..ab7699e 100644 --- a/src/main/kotlin/data/schema/UserSchema.kt +++ b/src/main/kotlin/data/schema/UserSchema.kt @@ -8,15 +8,15 @@ class UserSchema( override val header: List ) : BaseSchema { override fun toRow(entity: User): List { - TODO("Not yet implemented") + return emptyList() } - override fun fromRow(row:List): User { - TODO("Not yet implemented") + override fun fromRow(row:List): User? { + return null } override fun getId(entity: User): String { - TODO("Not yet implemented") + return "" } } \ No newline at end of file From 8fa9e4729d3a7765acd4b48cdf26af9d6d3fc1fb Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 03:53:29 +0300 Subject: [PATCH 06/15] add user schema class unit test --- src/test/kotlin/data/schema/UserSchemaTest.kt | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/test/kotlin/data/schema/UserSchemaTest.kt diff --git a/src/test/kotlin/data/schema/UserSchemaTest.kt b/src/test/kotlin/data/schema/UserSchemaTest.kt new file mode 100644 index 0000000..5db7089 --- /dev/null +++ b/src/test/kotlin/data/schema/UserSchemaTest.kt @@ -0,0 +1,181 @@ +package data.schema + +import com.berlin.data.schema.UserSchema +import com.berlin.model.User +import com.berlin.model.UserRole +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + + +class UserSchemaTest { + + private lateinit var userSchema: UserSchema + + @BeforeEach + fun setup() { + userSchema = fakeUserSchema() + } + + //region create object + + @Test + fun `should throw IllegalArgumentException when try to create object with blank file name`() { + //when //then + assertThrows { + userSchema = UserSchema("", listOf("a", "b", "c", "d")) + } + } + + @Test + fun `should throw IllegalArgumentException when try to create object with invalid size header`() { + //when //then + assertThrows { + userSchema = UserSchema("test.csv", listOf("a", "b")) + } + } + + //endregion + + //region toRow + + @Test + fun `toRow should return list of valid user attributes when valid user passed`() { + //when + val result=userSchema.toRow(validUser) + //then + assertThat(result).isEqualTo(validRow) + } + + @Test + fun `toRow should return empty list when invalid user passed miss id attribute`() { + //when + val result=userSchema.toRow(invalidUserEmptyId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid user passed miss userName attribute`() { + //when + val result=userSchema.toRow(invalidUserEmptyUserName) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid user passed miss password attribute`() { + //when + val result=userSchema.toRow(invalidUserEmptyPass) + //then + assertThat(result).isEmpty() + } + + //endregion + + //region fromRow + + @Test + fun `fromRow should return user when valid row passed`() { + //when + val result=userSchema.fromRow(validRow) + //then + assertThat(result).isEqualTo(validUser) + } + + @Test + fun `fromRow should return null when invalid row passed miss id column`() { + //when + val result=userSchema.fromRow(validRow) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid user passed miss userName column`() { + //when + val result=userSchema.fromRow(validRow) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid user passed miss password column`() { + //when + val result=userSchema.fromRow(validRow) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid user passed miss role column`() { + //when + val result=userSchema.fromRow(validRow) + //then + assertThat(result).isNull() + } + + //endregion + + //region getId + + @Test + fun `getId should return id of user passed`() { + //when + val result=userSchema.getId(validUser) + //then + assertThat(result).isEqualTo(validUser.id) + } + + @Test + fun `getId should return id empty when user passed have empty id`() { + //when + val result=userSchema.getId(invalidUserEmptyId) + //then + assertThat(result).isEqualTo("") + } + + //endregion + + private fun fakeUserSchema() = UserSchema("test.csv", listOf("a", "b", "c", "d")) + + private companion object { + //region Some Users + + val validUser = User( + id = "abcs123", userName = "marwanMahmoud", password = "ui76654898", role = UserRole.ADMIN + ) + val invalidUserEmptyId = User( + id = "", userName = "marwanMahmoud", password = "ui76654898", role = UserRole.ADMIN + ) + val invalidUserEmptyUserName = User( + id = "abcs123", userName = "", password = "ui76654898", role = UserRole.ADMIN + ) + val invalidUserEmptyPass = User( + id = "abcs123", userName = "marwanMahmoud", password = "", role = UserRole.ADMIN + ) + + //endregion + + //region Some Rows + + val validRow = listOf( + "abcs123", "marwanMahmoud", "ui76654898", UserRole.ADMIN.toString() + ) + val invalidRowEmptyId = listOf( + "", "marwanMahmoud", "ui76654898", UserRole.ADMIN.toString() + ) + val invalidRowEmptyUserName = listOf( + "abcs123", "", "ui76654898", UserRole.ADMIN.toString() + ) + val invalidRowEmptyPass = listOf( + "abcs123", "marwanMahmoud", "", UserRole.ADMIN.toString() + ) + val invalidRowEmptyRole = listOf( + "abcs123", "marwanMahmoud", "ui76654898", "" + ) + + //endregion + } +} \ No newline at end of file From 268d1bc53d681db30df4fc8b327cd16813b7e204 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 04:45:42 +0300 Subject: [PATCH 07/15] add implementation of user schema class --- src/main/kotlin/data/schema/UserSchema.kt | 45 +++++++++++++++++-- src/test/kotlin/data/schema/UserSchemaTest.kt | 30 +++++++++---- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/data/schema/UserSchema.kt b/src/main/kotlin/data/schema/UserSchema.kt index ab7699e..ff92e6f 100644 --- a/src/main/kotlin/data/schema/UserSchema.kt +++ b/src/main/kotlin/data/schema/UserSchema.kt @@ -1,22 +1,61 @@ package com.berlin.data.schema import com.berlin.data.BaseSchema +import com.berlin.data.UserIndex import com.berlin.model.User +import com.berlin.model.UserRole class UserSchema( override val fileName: String, override val header: List ) : BaseSchema { + init { + require(fileName.isNotEmpty()&&header.size==NUMBER_OF_ATTRIBUTES) + } override fun toRow(entity: User): List { - return emptyList() + return if ( + entity.id.isEmpty()|| + entity.userName.isEmpty()|| + entity.password.isEmpty() + ) emptyList() + + else listOf( + entity.id, + entity.userName, + entity.password, + entity.role.toString(), + ) + } override fun fromRow(row:List): User? { - return null + return if ( + row[UserIndex.ID].isEmpty()|| + row[UserIndex.USER_NAME].isEmpty()|| + row[UserIndex.PASSWORD].isEmpty()|| + row[UserIndex.ROLE] !in enumValues().map { it.name } + + ) null + + else User( + id = row[UserIndex.ID], + userName = row[UserIndex.USER_NAME], + password = row[UserIndex.PASSWORD], + role = when(row[UserIndex.ROLE]){ + UserRole.ADMIN.toString()->UserRole.ADMIN + UserRole.MATE.toString()->UserRole.MATE + else ->UserRole.MATE + } + ) } + override fun getId(entity: User): String { - return "" + + return entity.id.ifEmpty { "" } + } + private companion object{ + const val NUMBER_OF_ATTRIBUTES=4 } } \ No newline at end of file diff --git a/src/test/kotlin/data/schema/UserSchemaTest.kt b/src/test/kotlin/data/schema/UserSchemaTest.kt index 5db7089..fcee1bc 100644 --- a/src/test/kotlin/data/schema/UserSchemaTest.kt +++ b/src/test/kotlin/data/schema/UserSchemaTest.kt @@ -45,7 +45,7 @@ class UserSchemaTest { //when val result=userSchema.toRow(validUser) //then - assertThat(result).isEqualTo(validRow) + assertThat(result).isEqualTo(validRowAdmin) } @Test @@ -77,17 +77,25 @@ class UserSchemaTest { //region fromRow @Test - fun `fromRow should return user when valid row passed`() { + fun `fromRow should return user when valid row admin passed`() { //when - val result=userSchema.fromRow(validRow) + val result=userSchema.fromRow(validRowAdmin) //then assertThat(result).isEqualTo(validUser) } + @Test + fun `fromRow should return user when valid row mate passed`() { + //when + val result=userSchema.fromRow(validRowMate) + //then + assertThat(result).isEqualTo(validUserMate) + } + @Test fun `fromRow should return null when invalid row passed miss id column`() { //when - val result=userSchema.fromRow(validRow) + val result=userSchema.fromRow(invalidRowEmptyId) //then assertThat(result).isNull() } @@ -95,7 +103,7 @@ class UserSchemaTest { @Test fun `fromRow should return null when invalid user passed miss userName column`() { //when - val result=userSchema.fromRow(validRow) + val result=userSchema.fromRow(invalidRowEmptyUserName) //then assertThat(result).isNull() } @@ -103,7 +111,7 @@ class UserSchemaTest { @Test fun `fromRow should return null when invalid user passed miss password column`() { //when - val result=userSchema.fromRow(validRow) + val result=userSchema.fromRow(invalidRowEmptyPass) //then assertThat(result).isNull() } @@ -111,7 +119,7 @@ class UserSchemaTest { @Test fun `fromRow should return null when invalid user passed miss role column`() { //when - val result=userSchema.fromRow(validRow) + val result=userSchema.fromRow(invalidRowEmptyRole) //then assertThat(result).isNull() } @@ -143,6 +151,9 @@ class UserSchemaTest { private companion object { //region Some Users + val validUserMate = User( + id = "abcs123", userName = "marwanMahmoud", password = "ui76654898", role = UserRole.MATE + ) val validUser = User( id = "abcs123", userName = "marwanMahmoud", password = "ui76654898", role = UserRole.ADMIN ) @@ -160,9 +171,12 @@ class UserSchemaTest { //region Some Rows - val validRow = listOf( + val validRowAdmin = listOf( "abcs123", "marwanMahmoud", "ui76654898", UserRole.ADMIN.toString() ) + val validRowMate = listOf( + "abcs123", "marwanMahmoud", "ui76654898", UserRole.MATE.toString() + ) val invalidRowEmptyId = listOf( "", "marwanMahmoud", "ui76654898", UserRole.ADMIN.toString() ) From 3657550f1c23a0862f01dbeef2a41f9c49c21841 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 06:20:24 +0300 Subject: [PATCH 08/15] add unit test for audit,project,state,and task schemas classes --- .../kotlin/data/schema/AuditSchemaTest.kt | 328 ++++++++++++++++++ .../kotlin/data/schema/ProjectSchemaTest.kt | 252 ++++++++++++++ .../kotlin/data/schema/StateSchemaTest.kt | 182 ++++++++++ src/test/kotlin/data/schema/TaskSchemaTest.kt | 312 +++++++++++++++++ 4 files changed, 1074 insertions(+) create mode 100644 src/test/kotlin/data/schema/AuditSchemaTest.kt create mode 100644 src/test/kotlin/data/schema/ProjectSchemaTest.kt create mode 100644 src/test/kotlin/data/schema/StateSchemaTest.kt create mode 100644 src/test/kotlin/data/schema/TaskSchemaTest.kt diff --git a/src/test/kotlin/data/schema/AuditSchemaTest.kt b/src/test/kotlin/data/schema/AuditSchemaTest.kt new file mode 100644 index 0000000..8e56ef3 --- /dev/null +++ b/src/test/kotlin/data/schema/AuditSchemaTest.kt @@ -0,0 +1,328 @@ +package data.schema + +import com.berlin.data.schema.AuditSchema +import com.berlin.model.AuditLog +import com.berlin.model.User +import com.berlin.model.UserRole +import com.berlin.model.AuditAction +import com.berlin.model.EntityType +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class AuditSchemaTest { + + private lateinit var auditSchema: AuditSchema + + @BeforeEach + fun setup() { + auditSchema = fakeAuditSchema() + } + + //region create object + + @Test + fun `should throw IllegalArgumentException when try to create object with blank file name`() { + //when //then + assertThrows { + auditSchema = AuditSchema("", listOf("a", "b", "c", "d", "e", "f", "g")) + } + } + + @Test + fun `should throw IllegalArgumentException when try to create object with invalid size header`() { + //when //then + assertThrows { + auditSchema = AuditSchema("test.csv", listOf("a", "b")) + } + } + + //endregion + + //region toRow + + @Test + fun `toRow should return list of valid audit log attributes when valid audit log passed`() { + //when + val result = auditSchema.toRow(validAuditLog) + //then + assertThat(result).isEqualTo(validRow) + } + + @Test + fun `toRow should return list of valid audit log attributes when audit log with empty changesDescription passed`() { + //when + val result = auditSchema.toRow(validAuditLogEmptyChangesDescription) + //then + assertThat(result).isEqualTo(validRowEmptyChangesDescription) + } + + @Test + fun `toRow should return empty list when invalid audit log passed miss id attribute`() { + //when + val result = auditSchema.toRow(invalidAuditLogEmptyId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid audit log passed miss timestamp attribute`() { + //when + val result = auditSchema.toRow(invalidAuditLogZeroTimestamp) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid audit log passed miss createdBy attribute`() { + //when + val result = auditSchema.toRow(invalidAuditLogEmptyCreatedBy) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid audit log passed miss entityId attribute`() { + //when + val result = auditSchema.toRow(invalidAuditLogEmptyEntityId) + //then + assertThat(result).isEmpty() + } + + //endregion + + //region fromRow + + @Test + fun `fromRow should return audit log when valid row full passed`() { + //when + val result = auditSchema.fromRow(validRow) + //then + assertThat(result).isEqualTo(validAuditLog) + } + + @Test + fun `fromRow should return audit log when valid row empty changesDescription passed`() { + //when + val result = auditSchema.fromRow(validRowEmptyChangesDescription) + //then + assertThat(result).isEqualTo(validAuditLogEmptyChangesDescription) + } + + @Test + fun `fromRow should return null when invalid row passed miss id column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyId) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss timestamp column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyTimestamp) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss createdBy column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyCreatedBy) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss auditAction column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyAuditAction) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss entityType column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyEntityType) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss entityId column`() { + //when + val result = auditSchema.fromRow(invalidRowEmptyEntityId) + //then + assertThat(result).isNull() + } + + //endregion + + //region getId + + @Test + fun `getId should return id of audit log passed`() { + //when + val result = auditSchema.getId(validAuditLog) + //then + assertThat(result).isEqualTo(validAuditLog.id) + } + + @Test + fun `getId should return id empty when audit log passed have empty id`() { + //when + val result = auditSchema.getId(invalidAuditLogEmptyId) + //then + assertThat(result).isEqualTo("") + } + + //endregion + + private fun fakeAuditSchema() = AuditSchema("test.csv", listOf("a", "b", "c", "d", "e", "f", "g")) + + private companion object { + + val testUser = User( + id = "u1", + userName = "user1", + password = "pass1", + role = UserRole.MATE + ) + + //region Some AuditLogs + val validAuditLog = AuditLog( + id = "a1", + timestamp = 1000L, + createdBy = testUser, + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "e1" + ) + val validAuditLogEmptyChangesDescription = AuditLog( + id = "a1", + timestamp = 1000L, + createdBy = testUser, + auditAction = AuditAction.CREATE, + changesDescription = null, + entityType = EntityType.TASK, + entityId = "e1" + ) + val invalidAuditLogEmptyId = AuditLog( + id = "", + timestamp = 1000L, + createdBy = testUser, + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "e1" + ) + val invalidAuditLogZeroTimestamp = AuditLog( + id = "a1", + timestamp = 0L, + createdBy = testUser, + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "e1" + ) + val invalidAuditLogEmptyCreatedBy = AuditLog( + id = "a1", + timestamp = 1000L, + createdBy = User("", "user", "pass", UserRole.MATE), + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "e1" + ) + val invalidAuditLogEmptyEntityId = AuditLog( + id = "a1", + timestamp = 1000L, + createdBy = testUser, + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "" + ) + + //endregion + + //region Some Rows + val validRow = listOf( + "a1", + "1000", + "u1", + "CREATE", + "create", + "TASK", + "e1" + ) + val validRowEmptyChangesDescription = listOf( + "a1", + "1000", + "u1", + "CREATE", + "", + "TASK", + "e1" + ) + val invalidRowEmptyId = listOf( + "", + "1000", + "u1", + "CREATE", + "create", + "TASK", + "e1" + ) + val invalidRowEmptyTimestamp = listOf( + "a1", + "", + "u1", + "CREATE", + "create", + "TASK", + "e1" + ) + val invalidRowEmptyCreatedBy = listOf( + "a1", + "1000", + "", + "CREATE", + "create", + "TASK", + "e1" + ) + val invalidRowEmptyAuditAction = listOf( + "a1", + "1000", + "u1", + "", + "create", + "TASK", + "e1" + ) + val invalidRowEmptyEntityType = listOf( + "a1", + "1000", + "u1", + "CREATE", + "create", + "", + "e1" + ) + val invalidRowEmptyEntityId = listOf( + "a1", + "1000", + "u1", + "CREATE", + "create", + "TASK", + "" + ) + + //endregion + } +} \ No newline at end of file diff --git a/src/test/kotlin/data/schema/ProjectSchemaTest.kt b/src/test/kotlin/data/schema/ProjectSchemaTest.kt new file mode 100644 index 0000000..83c2175 --- /dev/null +++ b/src/test/kotlin/data/schema/ProjectSchemaTest.kt @@ -0,0 +1,252 @@ +package data.schema + +import com.berlin.data.schema.ProjectSchema +import com.berlin.model.Project +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class ProjectSchemaTest { + + private lateinit var projectSchema: ProjectSchema + + @BeforeEach + fun setup() { + projectSchema = fakeProjectSchema() + } + + //region create object + + @Test + fun `should throw IllegalArgumentException when try to create object with blank file name`() { + //when //then + assertThrows { + projectSchema = ProjectSchema("", listOf("a", "b", "c", "d", "e")) + } + } + + @Test + fun `should throw IllegalArgumentException when try to create object with invalid size header`() { + //when //then + assertThrows { + projectSchema = ProjectSchema("test.csv", listOf("a", "b")) + } + } + + //endregion + + //region toRow + + @Test + fun `toRow should return list of valid project attributes when valid project passed`() { + //when + val result = projectSchema.toRow(validProject) + //then + assertThat(result).isEqualTo(validRow) + } + + @Test + fun `toRow should return list of valid project attributes when project with empty statesId passed`() { + //when + val result = projectSchema.toRow(validProjectEmptyStatesId) + //then + assertThat(result).isEqualTo(validRowEmptyStatesId) + } + + @Test + fun `toRow should return list of valid project attributes when project with empty tasksId passed`() { + //when + val result = projectSchema.toRow(validProjectEmptyTasksId) + //then + assertThat(result).isEqualTo(validRowEmptyTasksId) + } + + @Test + fun `toRow should return empty list when invalid project passed miss id attribute`() { + //when + val result = projectSchema.toRow(invalidProjectEmptyId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid project passed miss name attribute`() { + //when + val result = projectSchema.toRow(invalidProjectEmptyName) + //then + assertThat(result).isEmpty() + } + + //endregion + + //region fromRow + + @Test + fun `fromRow should return project when valid row full passed`() { + //when + val result = projectSchema.fromRow(validRow) + //then + assertThat(result).isEqualTo(validProject) + } + + @Test + fun `fromRow should return project when valid row empty description passed`() { + //when + val result = projectSchema.fromRow(validRowEmptyDescription) + //then + assertThat(result).isEqualTo(validProjectEmptyDescription) + } + + @Test + fun `fromRow should return project when valid row empty statesId passed`() { + //when + val result = projectSchema.fromRow(validRowEmptyStatesId) + //then + assertThat(result).isEqualTo(validProjectEmptyStatesId) + } + + @Test + fun `fromRow should return project when valid row empty tasksId passed`() { + //when + val result = projectSchema.fromRow(validRowEmptyTasksId) + //then + assertThat(result).isEqualTo(validProjectEmptyTasksId) + } + + @Test + fun `fromRow should return null when invalid row passed miss id column`() { + //when + val result = projectSchema.fromRow(invalidRowEmptyId) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid project passed miss name column`() { + //when + val result = projectSchema.fromRow(invalidRowEmptyName) + //then + assertThat(result).isNull() + } + + //endregion + + //region getId + + @Test + fun `getId should return id of project passed`() { + //when + val result = projectSchema.getId(validProject) + //then + assertThat(result).isEqualTo(validProject.id) + } + + @Test + fun `getId should return id empty when project passed have empty id`() { + //when + val result = projectSchema.getId(invalidProjectEmptyId) + //then + assertThat(result).isEqualTo("") + } + + //endregion + + private fun fakeProjectSchema() = ProjectSchema("test.csv", listOf("a", "b", "c", "d", "e")) + + private companion object { + //region Some Projects + + val validProject = Project( + id = "proj123", + name = "SampleProject", + description = "A sample project", + statesId = listOf("state1", "state2"), + tasksId = listOf("task1", "task2") + ) + val validProjectEmptyDescription = Project( + id = "proj123", + name = "SampleProject", + description = null, + statesId = listOf("state1", "state2"), + tasksId = listOf("task1", "task2") + ) + val validProjectEmptyStatesId = Project( + id = "proj123", + name = "SampleProject", + description = "A sample project", + statesId = emptyList(), + tasksId = listOf("task1", "task2") + ) + val validProjectEmptyTasksId = Project( + id = "proj123", + name = "SampleProject", + description = "A sample project", + statesId = listOf("state1", "state2"), + tasksId = emptyList() + ) + val invalidProjectEmptyId = Project( + id = "", + name = "SampleProject", + description = "A sample project", + statesId = listOf("state1", "state2"), + tasksId = listOf("task1", "task2") + ) + val invalidProjectEmptyName = Project( + id = "proj123", + name = "", + description = "A sample project", + statesId = listOf("state1", "state2"), + tasksId = listOf("task1", "task2") + ) + + //endregion + + //region Some Rows + + val validRow = listOf( + "proj123", + "SampleProject", + "A sample project", + "state1,state2", + "task1,task2" + ) + val validRowEmptyDescription = listOf( + "proj123", + "SampleProject", + "", + "state1,state2", + "task1,task2" + ) + val validRowEmptyStatesId = listOf( + "proj123", + "SampleProject", + "A sample project", + "", + "task1,task2" + ) + val validRowEmptyTasksId = listOf( + "proj123", + "SampleProject", + "A sample project", + "state1,state2", + "" + ) + val invalidRowEmptyId = listOf( + "", + "SampleProject", + "A sample project", + "state1,state2", + "task1,task2" + ) + val invalidRowEmptyName = listOf( + "proj123", + "", + "A sample project", + "state1,state2", + "task1,task2" + ) + + //endregion + } +} \ No newline at end of file diff --git a/src/test/kotlin/data/schema/StateSchemaTest.kt b/src/test/kotlin/data/schema/StateSchemaTest.kt new file mode 100644 index 0000000..e072ada --- /dev/null +++ b/src/test/kotlin/data/schema/StateSchemaTest.kt @@ -0,0 +1,182 @@ +package data.schema + +import com.berlin.data.schema.StateSchema +import com.berlin.model.State +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class StateSchemaTest { + + private lateinit var stateSchema: StateSchema + + @BeforeEach + fun setup() { + stateSchema = fakeStateSchema() + } + + //region create object + + @Test + fun `should throw IllegalArgumentException when try to create object with blank file name`() { + //when //then + assertThrows { + stateSchema = StateSchema("", listOf("a", "b", "c")) + } + } + + @Test + fun `should throw IllegalArgumentException when try to create object with invalid size header`() { + //when //then + assertThrows { + stateSchema = StateSchema("test.csv", listOf("a", "b")) + } + } + + //endregion + + //region toRow + + @Test + fun `toRow should return list of valid state attributes when valid state passed`() { + //when + val result = stateSchema.toRow(validState) + //then + assertThat(result).isEqualTo(validRow) + } + + @Test + fun `toRow should return empty list when invalid state passed miss id attribute`() { + //when + val result = stateSchema.toRow(invalidStateEmptyId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid state passed miss name attribute`() { + //when + val result = stateSchema.toRow(invalidStateEmptyName) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid state passed miss projectId attribute`() { + //when + val result = stateSchema.toRow(invalidStateEmptyProjectId) + //then + assertThat(result).isEmpty() + } + + //endregion + + //region fromRow + + @Test + fun `fromRow should return state when valid row passed`() { + //when + val result = stateSchema.fromRow(validRow) + //then + assertThat(result).isEqualTo(validState) + } + + @Test + fun `fromRow should return null when invalid row passed miss id column`() { + //when + val result = stateSchema.fromRow(invalidRowEmptyId) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss name column`() { + //when + val result = stateSchema.fromRow(invalidRowEmptyName) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss projectId column`() { + //when + val result = stateSchema.fromRow(invalidRowEmptyProjectId) + //then + assertThat(result).isNull() + } + + //endregion + + //region getId + + @Test + fun `getId should return id of state passed`() { + //when + val result = stateSchema.getId(validState) + //then + assertThat(result).isEqualTo(validState.id) + } + + @Test + fun `getId should return id empty when state passed have empty id`() { + //when + val result = stateSchema.getId(invalidStateEmptyId) + //then + assertThat(result).isEqualTo("") + } + + //endregion + + private fun fakeStateSchema() = StateSchema("test.csv", listOf("a", "b", "c")) + + private companion object { + //region Some States + val validState = State( + id = "s1", + name = "n1", + projectId = "p1" + ) + val invalidStateEmptyId = State( + id = "", + name = "n1", + projectId = "p1" + ) + val invalidStateEmptyName = State( + id = "s1", + name = "", + projectId = "p1" + ) + val invalidStateEmptyProjectId = State( + id = "s1", + name = "n1", + projectId = "" + ) + + //endregion + + //region Some Rows + val validRow = listOf( + "s1", + "n1", + "p1" + ) + val invalidRowEmptyId = listOf( + "", + "n1", + "p1" + ) + val invalidRowEmptyName = listOf( + "s1", + "", + "p1" + ) + val invalidRowEmptyProjectId = listOf( + "s1", + "n1", + "" + ) + + //endregion + } +} \ No newline at end of file diff --git a/src/test/kotlin/data/schema/TaskSchemaTest.kt b/src/test/kotlin/data/schema/TaskSchemaTest.kt new file mode 100644 index 0000000..642aae1 --- /dev/null +++ b/src/test/kotlin/data/schema/TaskSchemaTest.kt @@ -0,0 +1,312 @@ +package data.schema + +import com.berlin.data.schema.TaskSchema +import com.berlin.model.* +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class TaskSchemaTest { + + private lateinit var taskSchema: TaskSchema + + @BeforeEach + fun setup() { + taskSchema = fakeTaskSchema() + } + + //region create object + + @Test + fun `should throw IllegalArgumentException when try to create object with blank file name`() { + //when //then + assertThrows { + taskSchema = TaskSchema("", listOf("a", "b", "c", "d", "e", "f", "g", "h")) + } + } + + @Test + fun `should throw IllegalArgumentException when try to create object with invalid size header`() { + //when //then + assertThrows { + taskSchema = TaskSchema("test.csv", listOf("a", "b")) + } + } + + //endregion + + //region toRow + + @Test + fun `toRow should return list of valid task attributes when valid task passed`() { + //when + val result = taskSchema.toRow(validTask) + //then + assertThat(result).isEqualTo(validRow) + } + + @Test + fun `toRow should return list of valid task attributes when task with empty auditLogs passed`() { + //when + val result = taskSchema.toRow(validTaskEmptyAuditLogs) + //then + assertThat(result).isEqualTo(validRowEmptyAuditLogs) + } + + @Test + fun `toRow should return list of valid task attributes when task with empty description passed`() { + //when + val result = taskSchema.toRow(validTaskEmptyDescription) + //then + assertThat(result).isEqualTo(validRowEmptyDescription) + } + + @Test + fun `toRow should return empty list when invalid task passed miss id attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid task passed miss projectId attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyProjectId) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid task passed miss title attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyTitle) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid task passed miss stateId attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyStateId) + //then + assertThat(result).isEmpty() + } + + //endregion + + //region fromRow + + @Test + fun `fromRow should return task when valid row full passed`() { + //when + val result = taskSchema.fromRow(validRow) + //then + assertThat(result).isEqualTo(validTask) + } + + @Test + fun `fromRow should return task when valid row empty description passed`() { + //when + val result = taskSchema.fromRow(validRowEmptyDescription) + //then + assertThat(result).isEqualTo(validTaskEmptyDescription) + } + + @Test + fun `fromRow should return task when valid row empty auditLogs passed`() { + //when + val result = taskSchema.fromRow(validRowEmptyAuditLogs) + //then + assertThat(result).isEqualTo(validTaskEmptyAuditLogs) + } + + @Test + fun `fromRow should return null when invalid row passed miss id column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyId) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss projectId column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyProjectId) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss title column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyTitle) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss stateId column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyStateId) + //then + assertThat(result).isNull() + } + + //endregion + + //region getId + + @Test + fun `getId should return id of task passed`() { + //when + val result = taskSchema.getId(validTask) + //then + assertThat(result).isEqualTo(validTask.id) + } + + @Test + fun `getId should return id empty when task passed have empty id`() { + //when + val result = taskSchema.getId(invalidTaskEmptyId) + //then + assertThat(result).isEqualTo("") + } + + //endregion + + private fun fakeTaskSchema() = TaskSchema("test.csv", listOf("a", "b", "c", "d", "e", "f", "g", "h")) + + private companion object { + //region Some Users + val sampleUser = User( + id = "u1", userName = "user1", password = "pass1", role = UserRole.MATE + ) + val sampleUser2 = User( + id = "u2", userName = "user2", password = "pass2", role = UserRole.ADMIN + ) + //endregion + + //region Some AuditLogs + val testAuditLog1 = AuditLog( + id = "a1", + timestamp = 1000L, + createdBy = sampleUser, + auditAction = AuditAction.CREATE, + changesDescription = "create", + entityType = EntityType.TASK, + entityId = "t1" + ) + val testAuditLog2 = AuditLog( + id = "a2", + timestamp = 2000L, + createdBy = sampleUser2, + auditAction = AuditAction.UPDATE, + changesDescription = null, + entityType = EntityType.TASK, + entityId = "t1" + ) + //endregion + + //region Some Tasks + val validTask = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + val validTaskEmptyDescription = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = null, + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + val validTaskEmptyAuditLogs = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = emptyList() + ) + val invalidTaskEmptyId = Task( + id = "", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + val invalidTaskEmptyProjectId = Task( + id = "t1", + projectId = "", + title = "task1", + description = "desc", + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + val invalidTaskEmptyTitle = Task( + id = "t1", + projectId = "p1", + title = "", + description = "desc", + stateId = "s1", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + val invalidTaskEmptyStateId = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "", + assignedTo = sampleUser, + createBy = sampleUser2, + auditLogs = listOf(testAuditLog1, testAuditLog2) + ) + + //endregion + + //region Some Rows + + val validRow = listOf( + "t1", "p1", "task1", "desc", "s1", "u1", "u2", "a1,a2" + ) + val validRowEmptyDescription = listOf( + "t1", "p1", "task1", "", "s1", "u1", "u2", "a1,a2" + ) + val validRowEmptyAuditLogs = listOf( + "t1", "p1", "task1", "desc", "s1", "u1", "u2", "" + ) + val invalidRowEmptyId = listOf( + "", "p1", "task1", "desc", "s1", "u1", "u2", "a1,a2" + ) + val invalidRowEmptyProjectId = listOf( + "t1", "", "task1", "desc", "s1", "u1", "u2", "a1,a2" + ) + val invalidRowEmptyTitle = listOf( + "t1", "p1", "", "desc", "s1", "u1", "u2", "a1,a2" + ) + val invalidRowEmptyStateId = listOf( + "t1", "p1", "task1", "desc", "", "u1", "u2", "a1,a2" + ) + + //endregion + } +} \ No newline at end of file From 21e86c049b7c0f8237d409ddb2589270688635b0 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 06:32:38 +0300 Subject: [PATCH 09/15] add implementation of state schema class --- src/main/kotlin/data/schema/StateSchema.kt | 34 +++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/data/schema/StateSchema.kt b/src/main/kotlin/data/schema/StateSchema.kt index 79b0729..c02cfa4 100644 --- a/src/main/kotlin/data/schema/StateSchema.kt +++ b/src/main/kotlin/data/schema/StateSchema.kt @@ -1,22 +1,48 @@ package com.berlin.data.schema import com.berlin.data.BaseSchema +import com.berlin.data.StateIndex import com.berlin.model.State class StateSchema( override val fileName: String, override val header: List ) : BaseSchema { + init { + require(fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES) + } + override fun toRow(entity: State): List { - return emptyList() + return if ( + entity.id.isEmpty() || + entity.name.isEmpty() || + entity.projectId.isEmpty() + ) emptyList() + else listOf( + entity.id, + entity.name, + entity.projectId + ) } - override fun fromRow(row:List): State? { - return null + override fun fromRow(row: List): State? { + return if ( + row[StateIndex.ID].isEmpty() || + row[StateIndex.NAME].isEmpty() || + row[StateIndex.PROJECT_ID].isEmpty() + ) null + else State( + id = row[StateIndex.ID], + name = row[StateIndex.NAME], + projectId = row[StateIndex.PROJECT_ID] + ) } override fun getId(entity: State): String { - return "" + return entity.id.ifEmpty { "" } } + private companion object { + const val NUMBER_OF_ATTRIBUTES = 3 + } } \ No newline at end of file From c86c5889b59fa985ee4906792a7412bcf38837ce Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 07:36:59 +0300 Subject: [PATCH 10/15] add implementation of project schema class,edit test to make the list contain [] --- src/main/kotlin/data/schema/ProjectSchema.kt | 35 +++++++++++++++---- .../kotlin/data/schema/ProjectSchemaTest.kt | 32 ++++++++++------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/data/schema/ProjectSchema.kt b/src/main/kotlin/data/schema/ProjectSchema.kt index 71cfb44..6164ec3 100644 --- a/src/main/kotlin/data/schema/ProjectSchema.kt +++ b/src/main/kotlin/data/schema/ProjectSchema.kt @@ -1,22 +1,43 @@ package com.berlin.data.schema import com.berlin.data.BaseSchema +import com.berlin.data.ProjectIndex import com.berlin.model.Project -class ProjectSchema ( - override val fileName: String, - override val header: List +class ProjectSchema( + override val fileName: String, override val header: List ) : BaseSchema { + init { + require(fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES) + } + override fun toRow(entity: Project): List { - return emptyList() + return if (entity.id.isEmpty() || entity.name.isEmpty()) emptyList() + else listOf( + entity.id, + entity.name, + entity.description ?: "", + "[${entity.statesId.joinToString(",")}]", + "[${entity.tasksId.joinToString(",")}]" + ) } - override fun fromRow(row:List): Project? { - return null + override fun fromRow(row: List): Project? { + return if (row[ProjectIndex.ID].isEmpty() || row[ProjectIndex.NAME].isEmpty()) null + else Project( + id = row[ProjectIndex.ID], + name = row[ProjectIndex.NAME], + description = row[ProjectIndex.DESCRIPTION].ifEmpty { null }, + statesId = row[ProjectIndex.STATES_ID].removeSurrounding("[", "]").takeIf { it.isNotEmpty() }?.split(",")?: emptyList(), + tasksId = row[ProjectIndex.TASKS_ID].removeSurrounding("[", "]").takeIf { it.isNotEmpty() }?.split(",")?: emptyList() + ) } override fun getId(entity: Project): String { - return "" + return entity.id.ifEmpty { "" } } + private companion object { + const val NUMBER_OF_ATTRIBUTES = 5 + } } \ No newline at end of file diff --git a/src/test/kotlin/data/schema/ProjectSchemaTest.kt b/src/test/kotlin/data/schema/ProjectSchemaTest.kt index 83c2175..054305d 100644 --- a/src/test/kotlin/data/schema/ProjectSchemaTest.kt +++ b/src/test/kotlin/data/schema/ProjectSchemaTest.kt @@ -54,6 +54,14 @@ class ProjectSchemaTest { assertThat(result).isEqualTo(validRowEmptyStatesId) } + @Test + fun `toRow should return list of valid project attributes when project with empty description passed`() { + //when + val result = projectSchema.toRow(validProjectEmptyDescription) + //then + assertThat(result).isEqualTo(validRowEmptyDescription) + } + @Test fun `toRow should return list of valid project attributes when project with empty tasksId passed`() { //when @@ -208,43 +216,43 @@ class ProjectSchemaTest { "proj123", "SampleProject", "A sample project", - "state1,state2", - "task1,task2" + "[state1,state2]", + "[task1,task2]" ) val validRowEmptyDescription = listOf( "proj123", "SampleProject", "", - "state1,state2", - "task1,task2" + "[state1,state2]", + "[task1,task2]" ) val validRowEmptyStatesId = listOf( "proj123", "SampleProject", "A sample project", - "", - "task1,task2" + "[]", + "[task1,task2]" ) val validRowEmptyTasksId = listOf( "proj123", "SampleProject", "A sample project", - "state1,state2", - "" + "[state1,state2]", + "[]" ) val invalidRowEmptyId = listOf( "", "SampleProject", "A sample project", - "state1,state2", - "task1,task2" + "[state1,state2]", + "[task1,task2]" ) val invalidRowEmptyName = listOf( "proj123", "", "A sample project", - "state1,state2", - "task1,task2" + "[state1,state2]", + "[task1,task2]" ) //endregion From 0637e971f8245615d605884806a835238080457a Mon Sep 17 00:00:00 2001 From: Diyar Date: Wed, 30 Apr 2025 20:50:37 +0300 Subject: [PATCH 11/15] add test cases for CsvDataSource --- .../data/csv_data_source/CsvDataSourceTest.kt | 343 ++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt diff --git a/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt b/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt new file mode 100644 index 0000000..31b0c99 --- /dev/null +++ b/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt @@ -0,0 +1,343 @@ +package com.berlin.data.csv_data_source + +import com.berlin.data.BaseSchema +import com.berlin.model.User +import com.berlin.model.UserRole +import com.google.common.truth.Truth.assertThat +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.io.File +import java.nio.file.Path + +class CsvDataSourceTest { + + private lateinit var csvDataSource: CsvDataSource + private lateinit var mockSchema: BaseSchema + + private val testUser = User( + id = "u1", + userName = "testUser", + password = "password123", + role = UserRole.MATE + ) + + private val testUsers = listOf( + testUser, + User("u2", "user2", "pass2", UserRole.ADMIN), + User("u3", "user3", "pass3", UserRole.MATE) + ) + + @TempDir + lateinit var tempDir: Path + + @BeforeEach + fun setup() { + mockSchema = mockk(relaxed = true) + + // Set up mock schema behavior + every { mockSchema.fileName } returns "users.csv" + every { mockSchema.header } returns listOf("id", "userName", "password", "role") + every { mockSchema.getId(any()) } answers { + val user = firstArg() + user.id + } + + csvDataSource = CsvDataSource(tempDir.toString(), mockSchema) + } + + //region initialization tests + + @Test + fun `constructor should properly initialize with valid parameters`() { + // Given: valid parameters passed to constructor + // When: constructor is called (in setup) + // Then: object should be created successfully + assertThat(csvDataSource).isNotNull() + } + + //endregion + + //region getAll tests + + @Test + fun `getAll should return empty list when file does not exist`() { + // Given: CSV file does not exist + + // When: getAll is called + val result = csvDataSource.getAll() + + // Then: an empty list should be returned + assertThat(result).isEmpty() + } + + @Test + fun `getAll should return list of entities when file exists with valid data`() { + // Given: CSV file exists with test data + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + + // When: getAll is called + val result = csvDataSource.getAll() + + // Then: list of all entities should be returned + assertThat(result).hasSize(testUsers.size) + verify(atLeast = 1) { mockSchema.fromRow(any()) } + } + + @Test + fun `getAll should filter out null entities from schema conversion`() { + // Given: CSV file exists but some rows will convert to null entities + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returns testUser andThen null andThen testUsers[2] + + // When: getAll is called + val result = csvDataSource.getAll() + + // Then: only valid entities should be returned + assertThat(result).hasSize(2) + assertThat(result).contains(testUser) + assertThat(result).contains(testUsers[2]) + } + + //endregion + + //region getById tests + + @Test + fun `getById should return null when file does not exist`() { + // Given: CSV file does not exist + + // When: getById is called with an id + val result = csvDataSource.getById("u1") + + // Then: null should be returned + assertThat(result).isNull() + } + + @Test + fun `getById should return entity when file exists and id matches`() { + // Given: CSV file exists with test data + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + + // When: getById is called with an existing id + val result = csvDataSource.getById("u1") + + // Then: matching entity should be returned + assertThat(result).isEqualTo(testUser) + } + + @Test + fun `getById should return null when file exists but id does not match`() { + // Given: CSV file exists with test data + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + + // When: getById is called with a non-existent id + val result = csvDataSource.getById("nonexistent") + + // Then: null should be returned + assertThat(result).isNull() + } + + //endregion + + //region write tests + + @Test + fun `write should create file and return true when writing to non-existent file`() { + // Given: file does not exist and schema returns valid row for entity + val csvFile = File(tempDir.toFile(), mockSchema.fileName) + every { mockSchema.toRow(any()) } returns listOf("u1", "testUser", "password123", "MATE") + + // When: write is called with a valid entity + val result = csvDataSource.write(testUser) + + // Then: file should be created and operation should succeed + assertThat(result).isTrue() + assertThat(csvFile.exists()).isTrue() + verify { mockSchema.toRow(testUser) } + } + + @Test + fun `write should append to file and return true when writing to existing file`() { + // Given: file exists with test data + createCsvWithTestData() + val newUser = User("u4", "user4", "pass4", UserRole.ADMIN) + every { mockSchema.toRow(newUser) } returns listOf("u4", "user4", "pass4", "ADMIN") + + // When: write is called with a new entity + val result = csvDataSource.write(newUser) + + // Then: entity should be appended and operation should succeed + assertThat(result).isTrue() + verify { mockSchema.toRow(newUser) } + } + + @Test + fun `write should return false when schema returns empty row`() { + // Given: schema returns empty row for the entity + every { mockSchema.toRow(any()) } returns emptyList() + + // When: write is called with an invalid entity + val result = csvDataSource.write(testUser) + + // Then: operation should fail + assertThat(result).isFalse() + } + + //endregion + + //region writeAll tests + + @Test + fun `writeAll should create file and return true when writing to non-existent file`() { + // Given: file does not exist and schema converts entities to rows properly + val csvFile = File(tempDir.toFile(), mockSchema.fileName) + every { mockSchema.toRow(any()) } answers { + val user = firstArg() + listOf(user.id, user.userName, user.password, user.role.toString()) + } + + // When: writeAll is called with multiple entities + val result = csvDataSource.writeAll(testUsers) + + // Then: file should be created and operation should succeed + assertThat(result).isTrue() + assertThat(csvFile.exists()).isTrue() + verify(exactly = testUsers.size) { mockSchema.toRow(any()) } + } + + @Test + fun `writeAll should return false when list is empty`() { + // Given: empty list of entities + + // When: writeAll is called with empty list + val result = csvDataSource.writeAll(emptyList()) + + // Then: operation should fail + assertThat(result).isFalse() + } + + @Test + fun `writeAll should filter out entities that produce empty rows`() { + // Given: some entities produce empty rows + every { mockSchema.toRow(testUsers[0]) } returns listOf("u1", "testUser", "password123", "MATE") + every { mockSchema.toRow(testUsers[1]) } returns emptyList() + every { mockSchema.toRow(testUsers[2]) } returns listOf("u3", "user3", "pass3", "MATE") + + // When: writeAll is called with mix of valid/invalid entities + val result = csvDataSource.writeAll(testUsers) + + // Then: operation should succeed with valid entities only + assertThat(result).isTrue() + verify(exactly = testUsers.size) { mockSchema.toRow(any()) } + } + + //endregion + + //region update tests + + @Test + fun `update should return false when file does not exist`() { + // Given: CSV file does not exist + + // When: update is called with an id and entity + val result = csvDataSource.update("u1", testUser) + + // Then: operation should fail + assertThat(result).isFalse() + } + + @Test + fun `update should return false when entity id does not exist`() { + // Given: CSV file exists but doesn't contain the entity with specified id + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + + // When: update is called with non-existent id + val result = csvDataSource.update("nonexistent", testUser) + + // Then: operation should fail + assertThat(result).isFalse() + } + + @Test + fun `update should return true when update is successful`() { + // Given: CSV file exists with entity that matches the id + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + val updatedUser = testUser.copy(userName = "updatedName") + every { mockSchema.toRow(updatedUser) } returns listOf("u1", "updatedName", "password123", "MATE") + + // When: update is called with existing id and modified entity + val result = csvDataSource.update("u1", updatedUser) + + // Then: entity should be updated and operation should succeed + assertThat(result).isTrue() + verify { mockSchema.toRow(updatedUser) } + } + + //endregion + + //region delete tests + + @Test + fun `delete should return false when file does not exist`() { + // Given: CSV file does not exist + + // When: delete is called with an id + val result = csvDataSource.delete("u1") + + // Then: operation should fail + assertThat(result).isFalse() + } + + @Test + fun `delete should return false when entity id does not exist`() { + // Given: CSV file exists but doesn't contain entity with specified id + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + + // When: delete is called with non-existent id + val result = csvDataSource.delete("nonexistent") + + // Then: operation should fail + assertThat(result).isFalse() + } + + @Test + fun `delete should return true when delete is successful`() { + // Given: CSV file exists with entity that matches the id + createCsvWithTestData() + every { mockSchema.fromRow(any()) } returnsMany testUsers + every { mockSchema.toRow(any()) } answers { + val user = firstArg() + listOf(user.id, user.userName, user.password, user.role.toString()) + } + + // When: delete is called with existing id + val result = csvDataSource.delete("u1") + + // Then: entity should be deleted and operation should succeed + assertThat(result).isTrue() + } + + //endregion + + // Helper method to create test CSV file + private fun createCsvWithTestData() { + val csvFile = File(tempDir.toFile(), mockSchema.fileName) + csvFile.createNewFile() + csvFile.writeText( + mockSchema.header.joinToString(",") + "\n" + + "u1,testUser,password123,MATE\n" + + "u2,user2,pass2,ADMIN\n" + + "u3,user3,pass3,MATE\n" + ) + } +} \ No newline at end of file From 3fab69cf75abac34a67f6921af8e0439e4cc10ac Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Wed, 30 Apr 2025 23:18:43 +0300 Subject: [PATCH 12/15] edit each class have object to id of object --- src/main/kotlin/data/schema/ProjectSchema.kt | 8 +- src/main/kotlin/model/AuditLog.kt | 2 +- src/main/kotlin/model/Task.kt | 5 +- .../kotlin/data/schema/AuditSchemaTest.kt | 20 ++--- src/test/kotlin/data/schema/TaskSchemaTest.kt | 78 ++++++------------- 5 files changed, 41 insertions(+), 72 deletions(-) diff --git a/src/main/kotlin/data/schema/ProjectSchema.kt b/src/main/kotlin/data/schema/ProjectSchema.kt index 6164ec3..d58dd01 100644 --- a/src/main/kotlin/data/schema/ProjectSchema.kt +++ b/src/main/kotlin/data/schema/ProjectSchema.kt @@ -28,8 +28,12 @@ class ProjectSchema( id = row[ProjectIndex.ID], name = row[ProjectIndex.NAME], description = row[ProjectIndex.DESCRIPTION].ifEmpty { null }, - statesId = row[ProjectIndex.STATES_ID].removeSurrounding("[", "]").takeIf { it.isNotEmpty() }?.split(",")?: emptyList(), - tasksId = row[ProjectIndex.TASKS_ID].removeSurrounding("[", "]").takeIf { it.isNotEmpty() }?.split(",")?: emptyList() + statesId = row[ProjectIndex.STATES_ID] + .removeSurrounding("[", "]") + .takeIf { it.isNotEmpty() }?.split(",")?: emptyList(), + tasksId = row[ProjectIndex.TASKS_ID] + .removeSurrounding("[", "]") + .takeIf { it.isNotEmpty() }?.split(",")?: emptyList() ) } diff --git a/src/main/kotlin/model/AuditLog.kt b/src/main/kotlin/model/AuditLog.kt index 1cc246d..02c4944 100644 --- a/src/main/kotlin/model/AuditLog.kt +++ b/src/main/kotlin/model/AuditLog.kt @@ -4,7 +4,7 @@ package com.berlin.model data class AuditLog( val id:String, val timestamp: Long, - val createdBy:User, + val createdByUserId:String, val auditAction:AuditAction, val changesDescription:String?, val entityType:EntityType, diff --git a/src/main/kotlin/model/Task.kt b/src/main/kotlin/model/Task.kt index 1c576a3..fccd415 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 + val assignedToUserId:String, + val createByUserId:String, ) diff --git a/src/test/kotlin/data/schema/AuditSchemaTest.kt b/src/test/kotlin/data/schema/AuditSchemaTest.kt index 8e56ef3..56b886d 100644 --- a/src/test/kotlin/data/schema/AuditSchemaTest.kt +++ b/src/test/kotlin/data/schema/AuditSchemaTest.kt @@ -184,18 +184,14 @@ class AuditSchemaTest { private companion object { - val testUser = User( - id = "u1", - userName = "user1", - password = "pass1", - role = UserRole.MATE - ) + val testUserId = "u1" + //region Some AuditLogs val validAuditLog = AuditLog( id = "a1", timestamp = 1000L, - createdBy = testUser, + createdByUserId = testUserId, auditAction = AuditAction.CREATE, changesDescription = "create", entityType = EntityType.TASK, @@ -204,7 +200,7 @@ class AuditSchemaTest { val validAuditLogEmptyChangesDescription = AuditLog( id = "a1", timestamp = 1000L, - createdBy = testUser, + createdByUserId = testUserId, auditAction = AuditAction.CREATE, changesDescription = null, entityType = EntityType.TASK, @@ -213,7 +209,7 @@ class AuditSchemaTest { val invalidAuditLogEmptyId = AuditLog( id = "", timestamp = 1000L, - createdBy = testUser, + createdByUserId = testUserId, auditAction = AuditAction.CREATE, changesDescription = "create", entityType = EntityType.TASK, @@ -222,7 +218,7 @@ class AuditSchemaTest { val invalidAuditLogZeroTimestamp = AuditLog( id = "a1", timestamp = 0L, - createdBy = testUser, + createdByUserId = testUserId, auditAction = AuditAction.CREATE, changesDescription = "create", entityType = EntityType.TASK, @@ -231,7 +227,7 @@ class AuditSchemaTest { val invalidAuditLogEmptyCreatedBy = AuditLog( id = "a1", timestamp = 1000L, - createdBy = User("", "user", "pass", UserRole.MATE), + createdByUserId = "", auditAction = AuditAction.CREATE, changesDescription = "create", entityType = EntityType.TASK, @@ -240,7 +236,7 @@ class AuditSchemaTest { val invalidAuditLogEmptyEntityId = AuditLog( id = "a1", timestamp = 1000L, - createdBy = testUser, + createdByUserId = testUserId, auditAction = AuditAction.CREATE, changesDescription = "create", entityType = EntityType.TASK, diff --git a/src/test/kotlin/data/schema/TaskSchemaTest.kt b/src/test/kotlin/data/schema/TaskSchemaTest.kt index 642aae1..e108021 100644 --- a/src/test/kotlin/data/schema/TaskSchemaTest.kt +++ b/src/test/kotlin/data/schema/TaskSchemaTest.kt @@ -180,33 +180,10 @@ class TaskSchemaTest { private companion object { //region Some Users - val sampleUser = User( - id = "u1", userName = "user1", password = "pass1", role = UserRole.MATE - ) - val sampleUser2 = User( - id = "u2", userName = "user2", password = "pass2", role = UserRole.ADMIN - ) - //endregion + val testUserId ="u1" + + val testUserId2 ="u2" - //region Some AuditLogs - val testAuditLog1 = AuditLog( - id = "a1", - timestamp = 1000L, - createdBy = sampleUser, - auditAction = AuditAction.CREATE, - changesDescription = "create", - entityType = EntityType.TASK, - entityId = "t1" - ) - val testAuditLog2 = AuditLog( - id = "a2", - timestamp = 2000L, - createdBy = sampleUser2, - auditAction = AuditAction.UPDATE, - changesDescription = null, - entityType = EntityType.TASK, - entityId = "t1" - ) //endregion //region Some Tasks @@ -216,9 +193,8 @@ class TaskSchemaTest { title = "task1", description = "desc", stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2 ) val validTaskEmptyDescription = Task( id = "t1", @@ -226,9 +202,8 @@ class TaskSchemaTest { title = "task1", description = null, stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2, ) val validTaskEmptyAuditLogs = Task( id = "t1", @@ -236,9 +211,8 @@ class TaskSchemaTest { title = "task1", description = "desc", stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = emptyList() + assignedToUserId = testUserId, + createByUserId = testUserId2, ) val invalidTaskEmptyId = Task( id = "", @@ -246,9 +220,8 @@ class TaskSchemaTest { title = "task1", description = "desc", stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2, ) val invalidTaskEmptyProjectId = Task( id = "t1", @@ -256,9 +229,8 @@ class TaskSchemaTest { title = "task1", description = "desc", stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2, ) val invalidTaskEmptyTitle = Task( id = "t1", @@ -266,9 +238,8 @@ class TaskSchemaTest { title = "", description = "desc", stateId = "s1", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2, ) val invalidTaskEmptyStateId = Task( id = "t1", @@ -276,9 +247,8 @@ class TaskSchemaTest { title = "task1", description = "desc", stateId = "", - assignedTo = sampleUser, - createBy = sampleUser2, - auditLogs = listOf(testAuditLog1, testAuditLog2) + assignedToUserId = testUserId, + createByUserId = testUserId2, ) //endregion @@ -286,25 +256,25 @@ class TaskSchemaTest { //region Some Rows val validRow = listOf( - "t1", "p1", "task1", "desc", "s1", "u1", "u2", "a1,a2" + "t1", "p1", "task1", "desc", "s1", "u1", "u2" ) val validRowEmptyDescription = listOf( - "t1", "p1", "task1", "", "s1", "u1", "u2", "a1,a2" + "t1", "p1", "task1", "", "s1", "u1", "u2" ) val validRowEmptyAuditLogs = listOf( - "t1", "p1", "task1", "desc", "s1", "u1", "u2", "" + "t1", "p1", "task1", "desc", "s1", "u1", "u2" ) val invalidRowEmptyId = listOf( - "", "p1", "task1", "desc", "s1", "u1", "u2", "a1,a2" + "", "p1", "task1", "desc", "s1", "u1", "u2" ) val invalidRowEmptyProjectId = listOf( - "t1", "", "task1", "desc", "s1", "u1", "u2", "a1,a2" + "t1", "", "task1", "desc", "s1", "u1", "u2" ) val invalidRowEmptyTitle = listOf( - "t1", "p1", "", "desc", "s1", "u1", "u2", "a1,a2" + "t1", "p1", "", "desc", "s1", "u1", "u2" ) val invalidRowEmptyStateId = listOf( - "t1", "p1", "task1", "desc", "", "u1", "u2", "a1,a2" + "t1", "p1", "task1", "desc", "", "u1", "u2" ) //endregion From fec588cb49eadd7500aeda176afc9f141921dbdb Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Thu, 1 May 2025 00:05:35 +0300 Subject: [PATCH 13/15] add implementation of method getAll --- .../data/csv_data_source/CsvDataSource.kt | 27 ++++++++++++++++--- .../data/csv_data_source/CsvDataSourceTest.kt | 11 ++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/data/csv_data_source/CsvDataSource.kt b/src/main/kotlin/data/csv_data_source/CsvDataSource.kt index 7993d28..aa24a46 100644 --- a/src/main/kotlin/data/csv_data_source/CsvDataSource.kt +++ b/src/main/kotlin/data/csv_data_source/CsvDataSource.kt @@ -2,14 +2,35 @@ package com.berlin.data.csv_data_source import com.berlin.data.BaseDataSource import com.berlin.data.BaseSchema +import com.opencsv.CSVReaderBuilder +import java.io.File +import java.io.FileNotFoundException +import java.io.FileReader class CsvDataSource( - private val rootDirectory: String, - private val schema: BaseSchema + private val rootDirectory: String, private val schema: BaseSchema ) : BaseDataSource { + private val csvFile: File + get() = File(rootDirectory, schema.fileName) + override fun getAll(): List { - return emptyList() + if (!csvFile.exists()) + throw FileNotFoundException("File not found: ${csvFile.name}") + + return FileReader(csvFile).use { reader -> + CSVReaderBuilder(reader) + .withSkipLines(1) + .build() + .use { csvReader -> + csvReader + .asSequence() + .filter { row -> row.isNotEmpty() } + .mapNotNull { row -> schema.fromRow(row.toList()) } + .toList() + } + } + } override fun getById(id: String): T? { diff --git a/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt b/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt index 31b0c99..7109215 100644 --- a/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt +++ b/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt @@ -9,8 +9,10 @@ import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.io.TempDir import java.io.File +import java.io.FileNotFoundException import java.nio.file.Path class CsvDataSourceTest { @@ -64,14 +66,11 @@ class CsvDataSourceTest { //region getAll tests @Test - fun `getAll should return empty list when file does not exist`() { + fun `getAll should throw FileNotFoundException when file does not exist`() { // Given: CSV file does not exist - // When: getAll is called - val result = csvDataSource.getAll() - - // Then: an empty list should be returned - assertThat(result).isEmpty() + // When // Then: getAll is called + assertThrows { csvDataSource.getAll() } } @Test From 5b5ff8f9162a33e1c54d64e5e03ae7c7572a4589 Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Thu, 1 May 2025 02:41:17 +0300 Subject: [PATCH 14/15] add implementation of task schema class ,enhance all schema classes code readability,refactor getId method,and add extra test cases to cover branches not covered --- src/main/kotlin/data/BaseSchema.kt | 2 +- src/main/kotlin/data/schema/AuditSchema.kt | 90 ++++++++++++++++-- src/main/kotlin/data/schema/ProjectSchema.kt | 60 ++++++++---- src/main/kotlin/data/schema/StateSchema.kt | 49 ++++++---- src/main/kotlin/data/schema/TaskSchema.kt | 63 ++++++++++++- src/main/kotlin/data/schema/UserSchema.kt | 72 ++++++++------ .../kotlin/data/schema/AuditSchemaTest.kt | 54 +++++++++-- .../kotlin/data/schema/ProjectSchemaTest.kt | 4 +- .../kotlin/data/schema/StateSchemaTest.kt | 4 +- src/test/kotlin/data/schema/TaskSchemaTest.kt | 93 ++++++++++++------- src/test/kotlin/data/schema/UserSchemaTest.kt | 13 ++- 11 files changed, 379 insertions(+), 125 deletions(-) diff --git a/src/main/kotlin/data/BaseSchema.kt b/src/main/kotlin/data/BaseSchema.kt index 9d620d4..eeddaf3 100644 --- a/src/main/kotlin/data/BaseSchema.kt +++ b/src/main/kotlin/data/BaseSchema.kt @@ -6,5 +6,5 @@ interface BaseSchema { fun toRow(entity: T): List fun fromRow(row: List): T? - fun getId(entity: T): String + fun getId(entity: T): String? } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/AuditSchema.kt b/src/main/kotlin/data/schema/AuditSchema.kt index 4043180..b7b9d90 100644 --- a/src/main/kotlin/data/schema/AuditSchema.kt +++ b/src/main/kotlin/data/schema/AuditSchema.kt @@ -1,22 +1,96 @@ package com.berlin.data.schema +import com.berlin.data.AuditLogIndex import com.berlin.data.BaseSchema +import com.berlin.model.AuditAction import com.berlin.model.AuditLog +import com.berlin.model.EntityType class AuditSchema( - override val fileName: String, - override val header: List -) :BaseSchema { + override val fileName: String, override val header: List +) : BaseSchema { + + init { + require( + fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES + ) + } + override fun toRow(entity: AuditLog): List { - return emptyList() + return if (checkAuditLogIsNotValid(entity)) emptyList() + else auditLogToStringsList(entity) + } + + override fun fromRow(row: List): AuditLog? { + return if (checkRowIsNotValidAuditLog(row)) null + else stringsListToAuditLog(row) + } + + override fun getId(entity: AuditLog): String? { + return entity.id.ifEmpty { null } + } + + private fun auditLogToStringsList(auditLog: AuditLog): List { + return listOf( + auditLog.id, + auditLog.timestamp.toString(), + auditLog.createdByUserId, + auditLog.auditAction.toString(), + auditLog.changesDescription ?: "", + auditLog.entityType.toString(), + auditLog.entityId + ) + } + + private fun stringsListToAuditLog(row: List): AuditLog { + return AuditLog( + id = row[AuditLogIndex.ID], + timestamp = row[AuditLogIndex.TIMES_TAMP].toLong() , + createdByUserId = row[AuditLogIndex.CREATE_BY], + auditAction = stringToAuditAction(row[AuditLogIndex.AUDIT_ACTION]), + changesDescription = row[AuditLogIndex.CHANGES_DESCRIPTION].ifEmpty { null }, + entityType = stringToEntityType(row[AuditLogIndex.ENTITY_TYPE]), + entityId = row[AuditLogIndex.ENTITY_ID] + ) + } + + private fun checkRowIsNotValidAuditLog(row: List): Boolean { + return (row[AuditLogIndex.ID].isEmpty() || + row[AuditLogIndex.TIMES_TAMP].isEmpty() || + row[AuditLogIndex.CREATE_BY].isEmpty() || + row[AuditLogIndex.AUDIT_ACTION].isEmpty() || + row[AuditLogIndex.ENTITY_ID].isEmpty() || + row[AuditLogIndex.AUDIT_ACTION] !in enumValues().map { it.name } || + row[AuditLogIndex.ENTITY_TYPE] !in enumValues().map { it.name } + ) + } + + private fun stringToAuditAction(auditAction: String): AuditAction { + return when (auditAction) { + AuditAction.CREATE.toString() -> AuditAction.CREATE + AuditAction.UPDATE.toString() -> AuditAction.UPDATE + AuditAction.DELETE.toString() -> AuditAction.DELETE + else -> AuditAction.CREATE + } } - override fun fromRow(row:List): AuditLog? { - return null + private fun stringToEntityType(entityType: String): EntityType { + return when (entityType) { + EntityType.TASK.toString() -> EntityType.TASK + EntityType.PROJECT.toString() -> EntityType.PROJECT + else -> EntityType.TASK + } } - override fun getId(entity: AuditLog): String { - return "" + private fun checkAuditLogIsNotValid(auditLog: AuditLog): Boolean { + return (auditLog.id.isEmpty() || + auditLog.timestamp <= 0 || + auditLog.createdByUserId.isEmpty() || + auditLog.entityId.isEmpty() + ) } + private companion object { + const val NUMBER_OF_ATTRIBUTES = 7 + } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/ProjectSchema.kt b/src/main/kotlin/data/schema/ProjectSchema.kt index d58dd01..db14124 100644 --- a/src/main/kotlin/data/schema/ProjectSchema.kt +++ b/src/main/kotlin/data/schema/ProjectSchema.kt @@ -5,40 +5,62 @@ import com.berlin.data.ProjectIndex import com.berlin.model.Project class ProjectSchema( - override val fileName: String, override val header: List + override val fileName: String, + override val header: List ) : BaseSchema { + init { require(fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES) } override fun toRow(entity: Project): List { - return if (entity.id.isEmpty() || entity.name.isEmpty()) emptyList() - else listOf( - entity.id, - entity.name, - entity.description ?: "", - "[${entity.statesId.joinToString(",")}]", - "[${entity.tasksId.joinToString(",")}]" - ) + return if (checkProjectIsNotValid(entity)) emptyList() + else projectToStringsList(entity) } override fun fromRow(row: List): Project? { - return if (row[ProjectIndex.ID].isEmpty() || row[ProjectIndex.NAME].isEmpty()) null - else Project( + return if (checkRowIsNotValidProject(row)) null + else stringsListToProject(row) + } + + override fun getId(entity: Project): String? { + return entity.id.ifEmpty { null } + } + + private fun projectToStringsList(project: Project): List { + return listOf( + project.id, + project.name, + project.description ?: "", + "[${project.statesId.joinToString(",")}]", + "[${project.tasksId.joinToString(",")}]" + ) + } + + private fun stringsListToProject(row: List): Project { + return Project( id = row[ProjectIndex.ID], name = row[ProjectIndex.NAME], description = row[ProjectIndex.DESCRIPTION].ifEmpty { null }, - statesId = row[ProjectIndex.STATES_ID] - .removeSurrounding("[", "]") - .takeIf { it.isNotEmpty() }?.split(",")?: emptyList(), - tasksId = row[ProjectIndex.TASKS_ID] - .removeSurrounding("[", "]") - .takeIf { it.isNotEmpty() }?.split(",")?: emptyList() + statesId = stringListToList(row[ProjectIndex.STATES_ID]), + tasksId = stringListToList(row[ProjectIndex.TASKS_ID]) ) } - override fun getId(entity: Project): String { - return entity.id.ifEmpty { "" } + private fun checkRowIsNotValidProject(row: List): Boolean { + return (row[ProjectIndex.ID].isEmpty() || + row[ProjectIndex.NAME].isEmpty()) + } + + private fun checkProjectIsNotValid(project: Project): Boolean { + return project.id.isEmpty() || + project.name.isEmpty() + } + + private fun stringListToList(listString: String): List { + return listString + .removeSurrounding("[", "]") + .takeIf { it.isNotEmpty() }?.split(",") ?: emptyList() } private companion object { diff --git a/src/main/kotlin/data/schema/StateSchema.kt b/src/main/kotlin/data/schema/StateSchema.kt index c02cfa4..d2de3e3 100644 --- a/src/main/kotlin/data/schema/StateSchema.kt +++ b/src/main/kotlin/data/schema/StateSchema.kt @@ -8,38 +8,51 @@ class StateSchema( override val fileName: String, override val header: List ) : BaseSchema { + init { require(fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES) } override fun toRow(entity: State): List { - return if ( - entity.id.isEmpty() || - entity.name.isEmpty() || - entity.projectId.isEmpty() - ) emptyList() - else listOf( - entity.id, - entity.name, - entity.projectId - ) + return if (checkStateIsNotValid(entity)) emptyList() + else stateToStringsList(entity) } override fun fromRow(row: List): State? { - return if ( - row[StateIndex.ID].isEmpty() || - row[StateIndex.NAME].isEmpty() || - row[StateIndex.PROJECT_ID].isEmpty() - ) null - else State( + return if (checkRowIsNotValidState(row)) null + else stringsListToState(row) + } + + override fun getId(entity: State): String? { + return entity.id.ifEmpty { null } + } + + private fun stateToStringsList(state: State): List { + return listOf( + state.id, + state.name, + state.projectId + ) + } + + private fun stringsListToState(row: List): State { + return State( id = row[StateIndex.ID], name = row[StateIndex.NAME], projectId = row[StateIndex.PROJECT_ID] ) } - override fun getId(entity: State): String { - return entity.id.ifEmpty { "" } + private fun checkRowIsNotValidState(row: List): Boolean { + return (row[StateIndex.ID].isEmpty() || + row[StateIndex.NAME].isEmpty() || + row[StateIndex.PROJECT_ID].isEmpty()) + } + + private fun checkStateIsNotValid(state: State): Boolean { + return (state.id.isEmpty() || + state.name.isEmpty() || + state.projectId.isEmpty()) } private companion object { diff --git a/src/main/kotlin/data/schema/TaskSchema.kt b/src/main/kotlin/data/schema/TaskSchema.kt index c37b76b..c029c6d 100644 --- a/src/main/kotlin/data/schema/TaskSchema.kt +++ b/src/main/kotlin/data/schema/TaskSchema.kt @@ -1,22 +1,75 @@ package com.berlin.data.schema import com.berlin.data.BaseSchema +import com.berlin.data.TaskIndex import com.berlin.model.Task class TaskSchema( override val fileName: String, override val header: List ) : BaseSchema { + + init { + require(fileName.isNotEmpty() && header.size == NUMBER_OF_ATTRIBUTES) + } + override fun toRow(entity: Task): List { - return emptyList() + return if (checkTaskIsNotValid(entity)) emptyList() + else taskToStringsList(entity) + } + + override fun fromRow(row: List): Task? { + return if (checkRowIsNotValidTask(row)) null + else stringsListToTask(row) + } + + override fun getId(entity: Task): String? { + return entity.id.ifEmpty { null } + } + + private fun taskToStringsList(task: Task): List { + return listOf( + task.id, + task.projectId, + task.title, + task.description ?: "", + task.stateId, + task.assignedToUserId, + task.createByUserId + ) } - override fun fromRow(row:List): Task? { - return null + private fun stringsListToTask(row: List): Task { + return Task( + id = row[TaskIndex.ID], + projectId = row[TaskIndex.PROJECT_ID], + title = row[TaskIndex.TITLE], + description = row[TaskIndex.DESCRIPTION].ifEmpty { null }, + stateId = row[TaskIndex.STATE_ID], + assignedToUserId = row[TaskIndex.ASSIGNED_TO], + createByUserId = row[TaskIndex.CREATE_BY] + ) } - override fun getId(entity: Task): String { - return "" + private fun checkRowIsNotValidTask(row: List): Boolean { + return (row[TaskIndex.ID].isEmpty() || + row[TaskIndex.PROJECT_ID].isEmpty() || + row[TaskIndex.TITLE].isEmpty() || + row[TaskIndex.STATE_ID].isEmpty() || + row[TaskIndex.ASSIGNED_TO].isEmpty() || + row[TaskIndex.CREATE_BY].isEmpty()) } + private fun checkTaskIsNotValid(task: Task): Boolean { + return (task.id.isEmpty() || + task.projectId.isEmpty() || + task.title.isEmpty() || + task.stateId.isEmpty() || + task.assignedToUserId.isEmpty() || + task.createByUserId.isEmpty()) + } + + private companion object { + const val NUMBER_OF_ATTRIBUTES = 7 + } } \ No newline at end of file diff --git a/src/main/kotlin/data/schema/UserSchema.kt b/src/main/kotlin/data/schema/UserSchema.kt index ff92e6f..f134ee0 100644 --- a/src/main/kotlin/data/schema/UserSchema.kt +++ b/src/main/kotlin/data/schema/UserSchema.kt @@ -9,53 +9,65 @@ class UserSchema( override val fileName: String, override val header: List ) : BaseSchema { + init { require(fileName.isNotEmpty()&&header.size==NUMBER_OF_ATTRIBUTES) } + override fun toRow(entity: User): List { - return if ( - entity.id.isEmpty()|| - entity.userName.isEmpty()|| - entity.password.isEmpty() - ) emptyList() - - else listOf( - entity.id, - entity.userName, - entity.password, - entity.role.toString(), - ) + return if (checkUserIsNotValid(entity)) emptyList() + else userToStringsList(entity) + } + override fun fromRow(row: List): User? { + return if (checkRowIsNotValidUser(row)) null + else stringsListToUser(row) } - override fun fromRow(row:List): User? { - return if ( - row[UserIndex.ID].isEmpty()|| - row[UserIndex.USER_NAME].isEmpty()|| - row[UserIndex.PASSWORD].isEmpty()|| - row[UserIndex.ROLE] !in enumValues().map { it.name } + override fun getId(entity: User): String? { + return entity.id.ifEmpty { null } + } - ) null + private fun userToStringsList(user: User): List { + return listOf( + user.id, + user.userName, + user.password, + user.role.toString() + ) + } - else User( + private fun stringsListToUser(row: List): User { + return User( id = row[UserIndex.ID], userName = row[UserIndex.USER_NAME], password = row[UserIndex.PASSWORD], - role = when(row[UserIndex.ROLE]){ - UserRole.ADMIN.toString()->UserRole.ADMIN - UserRole.MATE.toString()->UserRole.MATE - else ->UserRole.MATE - } + role = stringToUserRole(row[UserIndex.ROLE]) ) } + private fun checkRowIsNotValidUser(row: List): Boolean { + return (row[UserIndex.ID].isEmpty() || + row[UserIndex.USER_NAME].isEmpty() || + row[UserIndex.PASSWORD].isEmpty() || + row[UserIndex.ROLE] !in enumValues().map { it.name }) + } - override fun getId(entity: User): String { - - return entity.id.ifEmpty { "" } + private fun checkUserIsNotValid(user: User): Boolean { + return (user.id.isEmpty() || + user.userName.isEmpty() || + user.password.isEmpty()) } - private companion object{ - const val NUMBER_OF_ATTRIBUTES=4 + + private fun stringToUserRole(roleString: String): UserRole { + return when (roleString) { + UserRole.ADMIN.toString() -> UserRole.ADMIN + UserRole.MATE.toString() -> UserRole.MATE + else -> UserRole.MATE + } } + private companion object { + const val NUMBER_OF_ATTRIBUTES = 4 + } } \ No newline at end of file diff --git a/src/test/kotlin/data/schema/AuditSchemaTest.kt b/src/test/kotlin/data/schema/AuditSchemaTest.kt index 56b886d..9abf887 100644 --- a/src/test/kotlin/data/schema/AuditSchemaTest.kt +++ b/src/test/kotlin/data/schema/AuditSchemaTest.kt @@ -2,8 +2,6 @@ package data.schema import com.berlin.data.schema.AuditSchema import com.berlin.model.AuditLog -import com.berlin.model.User -import com.berlin.model.UserRole import com.berlin.model.AuditAction import com.berlin.model.EntityType import com.google.common.truth.Truth.assertThat @@ -95,13 +93,21 @@ class AuditSchemaTest { //region fromRow @Test - fun `fromRow should return audit log when valid row full passed`() { + fun `fromRow should return audit log when valid row task full passed`() { //when val result = auditSchema.fromRow(validRow) //then assertThat(result).isEqualTo(validAuditLog) } + @Test + fun `fromRow should return audit log when valid row project full passed`() { + //when + val result = auditSchema.fromRow(validRowProject) + //then + assertThat(result).isEqualTo(validAuditLogProject) + } + @Test fun `fromRow should return audit log when valid row empty changesDescription passed`() { //when @@ -142,6 +148,14 @@ class AuditSchemaTest { assertThat(result).isNull() } + @Test + fun `fromRow should return null when invalid row passed wrong auditAction column`() { + //when + val result = auditSchema.fromRow(invalidRowWrongAuditAction) + //then + assertThat(result).isNull() + } + @Test fun `fromRow should return null when invalid row passed miss entityType column`() { //when @@ -171,11 +185,11 @@ class AuditSchemaTest { } @Test - fun `getId should return id empty when audit log passed have empty id`() { + fun `getId should return null when audit log passed have empty id`() { //when val result = auditSchema.getId(invalidAuditLogEmptyId) //then - assertThat(result).isEqualTo("") + assertThat(result).isNull() } //endregion @@ -186,8 +200,8 @@ class AuditSchemaTest { val testUserId = "u1" - //region Some AuditLogs + val validAuditLog = AuditLog( id = "a1", timestamp = 1000L, @@ -197,6 +211,15 @@ class AuditSchemaTest { entityType = EntityType.TASK, entityId = "e1" ) + val validAuditLogProject = AuditLog( + id = "a1", + timestamp = 1000L, + createdByUserId = testUserId, + auditAction = AuditAction.DELETE, + changesDescription = "create", + entityType = EntityType.PROJECT, + entityId = "e1" + ) val validAuditLogEmptyChangesDescription = AuditLog( id = "a1", timestamp = 1000L, @@ -246,6 +269,7 @@ class AuditSchemaTest { //endregion //region Some Rows + val validRow = listOf( "a1", "1000", @@ -255,6 +279,15 @@ class AuditSchemaTest { "TASK", "e1" ) + val validRowProject = listOf( + "a1", + "1000", + "u1", + "DELETE", + "create", + "PROJECT", + "e1" + ) val validRowEmptyChangesDescription = listOf( "a1", "1000", @@ -300,6 +333,15 @@ class AuditSchemaTest { "TASK", "e1" ) + val invalidRowWrongAuditAction = listOf( + "a1", + "1000", + "u1", + "jgj444h", + "create", + "TASK", + "e1" + ) val invalidRowEmptyEntityType = listOf( "a1", "1000", diff --git a/src/test/kotlin/data/schema/ProjectSchemaTest.kt b/src/test/kotlin/data/schema/ProjectSchemaTest.kt index 054305d..4491dce 100644 --- a/src/test/kotlin/data/schema/ProjectSchemaTest.kt +++ b/src/test/kotlin/data/schema/ProjectSchemaTest.kt @@ -151,11 +151,11 @@ class ProjectSchemaTest { } @Test - fun `getId should return id empty when project passed have empty id`() { + fun `getId should return null when project passed have empty id`() { //when val result = projectSchema.getId(invalidProjectEmptyId) //then - assertThat(result).isEqualTo("") + assertThat(result).isNull() } //endregion diff --git a/src/test/kotlin/data/schema/StateSchemaTest.kt b/src/test/kotlin/data/schema/StateSchemaTest.kt index e072ada..d846a81 100644 --- a/src/test/kotlin/data/schema/StateSchemaTest.kt +++ b/src/test/kotlin/data/schema/StateSchemaTest.kt @@ -119,11 +119,11 @@ class StateSchemaTest { } @Test - fun `getId should return id empty when state passed have empty id`() { + fun `getId should return null when state passed have empty id`() { //when val result = stateSchema.getId(invalidStateEmptyId) //then - assertThat(result).isEqualTo("") + assertThat(result).isNull() } //endregion diff --git a/src/test/kotlin/data/schema/TaskSchemaTest.kt b/src/test/kotlin/data/schema/TaskSchemaTest.kt index e108021..b4ca238 100644 --- a/src/test/kotlin/data/schema/TaskSchemaTest.kt +++ b/src/test/kotlin/data/schema/TaskSchemaTest.kt @@ -46,14 +46,6 @@ class TaskSchemaTest { assertThat(result).isEqualTo(validRow) } - @Test - fun `toRow should return list of valid task attributes when task with empty auditLogs passed`() { - //when - val result = taskSchema.toRow(validTaskEmptyAuditLogs) - //then - assertThat(result).isEqualTo(validRowEmptyAuditLogs) - } - @Test fun `toRow should return list of valid task attributes when task with empty description passed`() { //when @@ -94,6 +86,22 @@ class TaskSchemaTest { assertThat(result).isEmpty() } + @Test + fun `toRow should return empty list when invalid task passed miss assign to attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyAssignTo) + //then + assertThat(result).isEmpty() + } + + @Test + fun `toRow should return empty list when invalid task passed miss create by attribute`() { + //when + val result = taskSchema.toRow(invalidTaskEmptyCreateBy) + //then + assertThat(result).isEmpty() + } + //endregion //region fromRow @@ -114,14 +122,6 @@ class TaskSchemaTest { assertThat(result).isEqualTo(validTaskEmptyDescription) } - @Test - fun `fromRow should return task when valid row empty auditLogs passed`() { - //when - val result = taskSchema.fromRow(validRowEmptyAuditLogs) - //then - assertThat(result).isEqualTo(validTaskEmptyAuditLogs) - } - @Test fun `fromRow should return null when invalid row passed miss id column`() { //when @@ -154,6 +154,22 @@ class TaskSchemaTest { assertThat(result).isNull() } + @Test + fun `fromRow should return null when invalid row passed miss assign to column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyAssignTo) + //then + assertThat(result).isNull() + } + + @Test + fun `fromRow should return null when invalid row passed miss create by column`() { + //when + val result = taskSchema.fromRow(invalidRowEmptyCreateBy) + //then + assertThat(result).isNull() + } + //endregion //region getId @@ -167,19 +183,20 @@ class TaskSchemaTest { } @Test - fun `getId should return id empty when task passed have empty id`() { + fun `getId should return null when task passed have empty id`() { //when val result = taskSchema.getId(invalidTaskEmptyId) //then - assertThat(result).isEqualTo("") + assertThat(result).isNull() } //endregion - private fun fakeTaskSchema() = TaskSchema("test.csv", listOf("a", "b", "c", "d", "e", "f", "g", "h")) + private fun fakeTaskSchema() = TaskSchema("test.csv", listOf("a", "b", "c", "d", "e", "f", "g")) private companion object { - //region Some Users + + //region Some Users id val testUserId ="u1" val testUserId2 ="u2" @@ -205,15 +222,6 @@ class TaskSchemaTest { assignedToUserId = testUserId, createByUserId = testUserId2, ) - val validTaskEmptyAuditLogs = Task( - id = "t1", - projectId = "p1", - title = "task1", - description = "desc", - stateId = "s1", - assignedToUserId = testUserId, - createByUserId = testUserId2, - ) val invalidTaskEmptyId = Task( id = "", projectId = "p1", @@ -250,6 +258,24 @@ class TaskSchemaTest { assignedToUserId = testUserId, createByUserId = testUserId2, ) + val invalidTaskEmptyAssignTo = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "s1", + assignedToUserId = "", + createByUserId = testUserId2, + ) + val invalidTaskEmptyCreateBy = Task( + id = "t1", + projectId = "p1", + title = "task1", + description = "desc", + stateId = "s1", + assignedToUserId = testUserId, + createByUserId = "", + ) //endregion @@ -261,9 +287,6 @@ class TaskSchemaTest { val validRowEmptyDescription = listOf( "t1", "p1", "task1", "", "s1", "u1", "u2" ) - val validRowEmptyAuditLogs = listOf( - "t1", "p1", "task1", "desc", "s1", "u1", "u2" - ) val invalidRowEmptyId = listOf( "", "p1", "task1", "desc", "s1", "u1", "u2" ) @@ -276,6 +299,12 @@ class TaskSchemaTest { val invalidRowEmptyStateId = listOf( "t1", "p1", "task1", "desc", "", "u1", "u2" ) + val invalidRowEmptyAssignTo = listOf( + "t1", "p1", "task1", "desc", "s1", "", "u2" + ) + val invalidRowEmptyCreateBy = listOf( + "t1", "p1", "task1", "desc", "s1", "u1", "" + ) //endregion } diff --git a/src/test/kotlin/data/schema/UserSchemaTest.kt b/src/test/kotlin/data/schema/UserSchemaTest.kt index fcee1bc..ab09d88 100644 --- a/src/test/kotlin/data/schema/UserSchemaTest.kt +++ b/src/test/kotlin/data/schema/UserSchemaTest.kt @@ -48,6 +48,14 @@ class UserSchemaTest { assertThat(result).isEqualTo(validRowAdmin) } + @Test + fun `toRow should return list of valid user attributes when valid user mate passed`() { + //when + val result=userSchema.toRow(validUserMate) + //then + assertThat(result).isEqualTo(validRowMate) + } + @Test fun `toRow should return empty list when invalid user passed miss id attribute`() { //when @@ -137,11 +145,11 @@ class UserSchemaTest { } @Test - fun `getId should return id empty when user passed have empty id`() { + fun `getId should return null when user passed have empty id`() { //when val result=userSchema.getId(invalidUserEmptyId) //then - assertThat(result).isEqualTo("") + assertThat(result).isNull() } //endregion @@ -149,6 +157,7 @@ class UserSchemaTest { private fun fakeUserSchema() = UserSchema("test.csv", listOf("a", "b", "c", "d")) private companion object { + //region Some Users val validUserMate = User( From 4236a07a05009b710f7253e0b18978e0be9dc59e Mon Sep 17 00:00:00 2001 From: Marwan Qashwa Date: Fri, 2 May 2025 12:52:58 +0300 Subject: [PATCH 15/15] remove csv data source --- .../data/csv_data_source/CsvDataSource.kt | 57 --- .../data/csv_data_source/CsvDataSourceTest.kt | 342 ------------------ 2 files changed, 399 deletions(-) delete mode 100644 src/main/kotlin/data/csv_data_source/CsvDataSource.kt delete mode 100644 src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt diff --git a/src/main/kotlin/data/csv_data_source/CsvDataSource.kt b/src/main/kotlin/data/csv_data_source/CsvDataSource.kt deleted file mode 100644 index aa24a46..0000000 --- a/src/main/kotlin/data/csv_data_source/CsvDataSource.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.berlin.data.csv_data_source - -import com.berlin.data.BaseDataSource -import com.berlin.data.BaseSchema -import com.opencsv.CSVReaderBuilder -import java.io.File -import java.io.FileNotFoundException -import java.io.FileReader - -class CsvDataSource( - private val rootDirectory: String, private val schema: BaseSchema -) : BaseDataSource { - - private val csvFile: File - get() = File(rootDirectory, schema.fileName) - - override fun getAll(): List { - if (!csvFile.exists()) - throw FileNotFoundException("File not found: ${csvFile.name}") - - return FileReader(csvFile).use { reader -> - CSVReaderBuilder(reader) - .withSkipLines(1) - .build() - .use { csvReader -> - csvReader - .asSequence() - .filter { row -> row.isNotEmpty() } - .mapNotNull { row -> schema.fromRow(row.toList()) } - .toList() - } - } - - } - - override fun getById(id: String): T? { - return null - } - - override fun update(id: String, entity: T): Boolean { - return false - } - - override fun delete(id: String): Boolean { - return false - } - - override fun write(entity: T): Boolean { - return false - } - - override fun writeAll(entities: List): Boolean { - return false - } - - -} \ No newline at end of file diff --git a/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt b/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt deleted file mode 100644 index 9129dec..0000000 --- a/src/test/kotlin/data/csv_data_source/CsvDataSourceTest.kt +++ /dev/null @@ -1,342 +0,0 @@ -package com.berlin.data.csv_data_source - -import com.berlin.data.BaseSchema -import com.berlin.domain.model.User -import com.berlin.domain.model.UserRole -import com.google.common.truth.Truth.assertThat -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.io.TempDir -import java.io.File -import java.io.FileNotFoundException -import java.nio.file.Path - -class CsvDataSourceTest { - - private lateinit var csvDataSource: CsvDataSource - private lateinit var mockSchema: BaseSchema - - private val testUser = User( - id = "u1", - userName = "testUser", - password = "password123", - role = UserRole.MATE - ) - - private val testUsers = listOf( - testUser, - User("u2", "user2", "pass2", UserRole.ADMIN), - User("u3", "user3", "pass3", UserRole.MATE) - ) - - @TempDir - lateinit var tempDir: Path - - @BeforeEach - fun setup() { - mockSchema = mockk(relaxed = true) - - // Set up mock schema behavior - every { mockSchema.fileName } returns "users.csv" - every { mockSchema.header } returns listOf("id", "userName", "password", "role") - every { mockSchema.getId(any()) } answers { - val user = firstArg() - user.id - } - - csvDataSource = CsvDataSource(tempDir.toString(), mockSchema) - } - - //region initialization tests - - @Test - fun `constructor should properly initialize with valid parameters`() { - // Given: valid parameters passed to constructor - // When: constructor is called (in setup) - // Then: object should be created successfully - assertThat(csvDataSource).isNotNull() - } - - //endregion - - //region getAll tests - - @Test - fun `getAll should throw FileNotFoundException when file does not exist`() { - // Given: CSV file does not exist - - // When // Then: getAll is called - assertThrows { csvDataSource.getAll() } - } - - @Test - fun `getAll should return list of entities when file exists with valid data`() { - // Given: CSV file exists with test data - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - - // When: getAll is called - val result = csvDataSource.getAll() - - // Then: list of all entities should be returned - assertThat(result).hasSize(testUsers.size) - verify(atLeast = 1) { mockSchema.fromRow(any()) } - } - - @Test - fun `getAll should filter out null entities from schema conversion`() { - // Given: CSV file exists but some rows will convert to null entities - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returns testUser andThen null andThen testUsers[2] - - // When: getAll is called - val result = csvDataSource.getAll() - - // Then: only valid entities should be returned - assertThat(result).hasSize(2) - assertThat(result).contains(testUser) - assertThat(result).contains(testUsers[2]) - } - - //endregion - - //region getById tests - - @Test - fun `getById should return null when file does not exist`() { - // Given: CSV file does not exist - - // When: getById is called with an id - val result = csvDataSource.getById("u1") - - // Then: null should be returned - assertThat(result).isNull() - } - - @Test - fun `getById should return entity when file exists and id matches`() { - // Given: CSV file exists with test data - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - - // When: getById is called with an existing id - val result = csvDataSource.getById("u1") - - // Then: matching entity should be returned - assertThat(result).isEqualTo(testUser) - } - - @Test - fun `getById should return null when file exists but id does not match`() { - // Given: CSV file exists with test data - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - - // When: getById is called with a non-existent id - val result = csvDataSource.getById("nonexistent") - - // Then: null should be returned - assertThat(result).isNull() - } - - //endregion - - //region write tests - - @Test - fun `write should create file and return true when writing to non-existent file`() { - // Given: file does not exist and schema returns valid row for entity - val csvFile = File(tempDir.toFile(), mockSchema.fileName) - every { mockSchema.toRow(any()) } returns listOf("u1", "testUser", "password123", "MATE") - - // When: write is called with a valid entity - val result = csvDataSource.write(testUser) - - // Then: file should be created and operation should succeed - assertThat(result).isTrue() - assertThat(csvFile.exists()).isTrue() - verify { mockSchema.toRow(testUser) } - } - - @Test - fun `write should append to file and return true when writing to existing file`() { - // Given: file exists with test data - createCsvWithTestData() - val newUser = User("u4", "user4", "pass4", UserRole.ADMIN) - every { mockSchema.toRow(newUser) } returns listOf("u4", "user4", "pass4", "ADMIN") - - // When: write is called with a new entity - val result = csvDataSource.write(newUser) - - // Then: entity should be appended and operation should succeed - assertThat(result).isTrue() - verify { mockSchema.toRow(newUser) } - } - - @Test - fun `write should return false when schema returns empty row`() { - // Given: schema returns empty row for the entity - every { mockSchema.toRow(any()) } returns emptyList() - - // When: write is called with an invalid entity - val result = csvDataSource.write(testUser) - - // Then: operation should fail - assertThat(result).isFalse() - } - - //endregion - - //region writeAll tests - - @Test - fun `writeAll should create file and return true when writing to non-existent file`() { - // Given: file does not exist and schema converts entities to rows properly - val csvFile = File(tempDir.toFile(), mockSchema.fileName) - every { mockSchema.toRow(any()) } answers { - val user = firstArg() - listOf(user.id, user.userName, user.password, user.role.toString()) - } - - // When: writeAll is called with multiple entities - val result = csvDataSource.writeAll(testUsers) - - // Then: file should be created and operation should succeed - assertThat(result).isTrue() - assertThat(csvFile.exists()).isTrue() - verify(exactly = testUsers.size) { mockSchema.toRow(any()) } - } - - @Test - fun `writeAll should return false when list is empty`() { - // Given: empty list of entities - - // When: writeAll is called with empty list - val result = csvDataSource.writeAll(emptyList()) - - // Then: operation should fail - assertThat(result).isFalse() - } - - @Test - fun `writeAll should filter out entities that produce empty rows`() { - // Given: some entities produce empty rows - every { mockSchema.toRow(testUsers[0]) } returns listOf("u1", "testUser", "password123", "MATE") - every { mockSchema.toRow(testUsers[1]) } returns emptyList() - every { mockSchema.toRow(testUsers[2]) } returns listOf("u3", "user3", "pass3", "MATE") - - // When: writeAll is called with mix of valid/invalid entities - val result = csvDataSource.writeAll(testUsers) - - // Then: operation should succeed with valid entities only - assertThat(result).isTrue() - verify(exactly = testUsers.size) { mockSchema.toRow(any()) } - } - - //endregion - - //region update tests - - @Test - fun `update should return false when file does not exist`() { - // Given: CSV file does not exist - - // When: update is called with an id and entity - val result = csvDataSource.update("u1", testUser) - - // Then: operation should fail - assertThat(result).isFalse() - } - - @Test - fun `update should return false when entity id does not exist`() { - // Given: CSV file exists but doesn't contain the entity with specified id - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - - // When: update is called with non-existent id - val result = csvDataSource.update("nonexistent", testUser) - - // Then: operation should fail - assertThat(result).isFalse() - } - - @Test - fun `update should return true when update is successful`() { - // Given: CSV file exists with entity that matches the id - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - val updatedUser = testUser.copy(userName = "updatedName") - every { mockSchema.toRow(updatedUser) } returns listOf("u1", "updatedName", "password123", "MATE") - - // When: update is called with existing id and modified entity - val result = csvDataSource.update("u1", updatedUser) - - // Then: entity should be updated and operation should succeed - assertThat(result).isTrue() - verify { mockSchema.toRow(updatedUser) } - } - - //endregion - - //region delete tests - - @Test - fun `delete should return false when file does not exist`() { - // Given: CSV file does not exist - - // When: delete is called with an id - val result = csvDataSource.delete("u1") - - // Then: operation should fail - assertThat(result).isFalse() - } - - @Test - fun `delete should return false when entity id does not exist`() { - // Given: CSV file exists but doesn't contain entity with specified id - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - - // When: delete is called with non-existent id - val result = csvDataSource.delete("nonexistent") - - // Then: operation should fail - assertThat(result).isFalse() - } - - @Test - fun `delete should return true when delete is successful`() { - // Given: CSV file exists with entity that matches the id - createCsvWithTestData() - every { mockSchema.fromRow(any()) } returnsMany testUsers - every { mockSchema.toRow(any()) } answers { - val user = firstArg() - listOf(user.id, user.userName, user.password, user.role.toString()) - } - - // When: delete is called with existing id - val result = csvDataSource.delete("u1") - - // Then: entity should be deleted and operation should succeed - assertThat(result).isTrue() - } - - //endregion - - // Helper method to create test CSV file - private fun createCsvWithTestData() { - val csvFile = File(tempDir.toFile(), mockSchema.fileName) - csvFile.createNewFile() - csvFile.writeText( - mockSchema.header.joinToString(",") + "\n" + - "u1,testUser,password123,MATE\n" + - "u2,user2,pass2,ADMIN\n" + - "u3,user3,pass3,MATE\n" - ) - } -} \ No newline at end of file