From 1d8e95f02d037a4d611b56555260914263b8092d Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:30:38 +0000 Subject: [PATCH 1/7] Start draft PR From 67d2956ee3c60517668ae48b639034db58f9088f Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:30:53 +0000 Subject: [PATCH 2/7] Add .gitignore for Android Kotlin project --- .gitignore | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fc9423 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Android Studio +*.iml +.gradle +/local.properties +/.idea/ +.DS_Store +/build +/captures +.externalNativeBuild +.cxx + +# Gradle files +gradle/ +gradlew +gradlew.bat + +# Dependency directories +/node_modules/ + +# Compiled class files +*.class + +# Log files +*.log + +# Package Files +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# Virtual machine crash logs +hs_err_pid* + +# Android build output +bin/ +out/ \ No newline at end of file From aa8e5f1749c6c6d7a7132227a4401c16afb4b5d7 Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:31:04 +0000 Subject: [PATCH 3/7] Create Todo data model with validation --- .../com/example/todoapp/data/model/Todo.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/com/example/todoapp/data/model/Todo.kt diff --git a/src/main/java/com/example/todoapp/data/model/Todo.kt b/src/main/java/com/example/todoapp/data/model/Todo.kt new file mode 100644 index 0000000..fa4569f --- /dev/null +++ b/src/main/java/com/example/todoapp/data/model/Todo.kt @@ -0,0 +1,25 @@ +package com.example.todoapp.data.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.time.LocalDateTime + +@Entity(tableName = "todos") +data class Todo( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + val title: String, + val description: String? = null, + val isCompleted: Boolean = false, + val createdAt: LocalDateTime = LocalDateTime.now(), + val updatedAt: LocalDateTime = LocalDateTime.now() +) { + // Validate todo item input + init { + require(title.isNotBlank()) { "Todo title cannot be blank" } + require(title.length <= 100) { "Todo title cannot exceed 100 characters" } + description?.let { + require(it.length <= 500) { "Todo description cannot exceed 500 characters" } + } + } +} \ No newline at end of file From 3125038ef11c9cd04cbd1896ffd74c372dac7737 Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:31:10 +0000 Subject: [PATCH 4/7] Create TodoDao with insert functionality --- .../com/example/todoapp/data/dao/TodoDao.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/com/example/todoapp/data/dao/TodoDao.kt diff --git a/src/main/java/com/example/todoapp/data/dao/TodoDao.kt b/src/main/java/com/example/todoapp/data/dao/TodoDao.kt new file mode 100644 index 0000000..f503ab8 --- /dev/null +++ b/src/main/java/com/example/todoapp/data/dao/TodoDao.kt @@ -0,0 +1,17 @@ +package com.example.todoapp.data.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.example.todoapp.data.model.Todo +import kotlinx.coroutines.flow.Flow + +@Dao +interface TodoDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertTodo(todo: Todo): Long + + @Query("SELECT * FROM todos WHERE id = :todoId") + fun getTodoById(todoId: Long): Flow +} \ No newline at end of file From f9851aed4814d1f7f6daf1ae00b9da56af3ee3b1 Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:31:18 +0000 Subject: [PATCH 5/7] Create TodoRepository with add and get methods --- .../todoapp/data/repository/TodoRepository.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/com/example/todoapp/data/repository/TodoRepository.kt diff --git a/src/main/java/com/example/todoapp/data/repository/TodoRepository.kt b/src/main/java/com/example/todoapp/data/repository/TodoRepository.kt new file mode 100644 index 0000000..f2daf44 --- /dev/null +++ b/src/main/java/com/example/todoapp/data/repository/TodoRepository.kt @@ -0,0 +1,18 @@ +package com.example.todoapp.data.repository + +import com.example.todoapp.data.dao.TodoDao +import com.example.todoapp.data.model.Todo +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class TodoRepository @Inject constructor( + private val todoDao: TodoDao +) { + suspend fun addTodo(todo: Todo): Long { + return todoDao.insertTodo(todo) + } + + fun getTodoById(todoId: Long): Flow { + return todoDao.getTodoById(todoId) + } +} \ No newline at end of file From 61976c8b679a8db3ddb332b259f801e73d321a87 Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:31:29 +0000 Subject: [PATCH 6/7] Create TodoRepository test cases --- .../data/repository/TodoRepositoryTest.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/java/com/example/todoapp/data/repository/TodoRepositoryTest.kt diff --git a/src/test/java/com/example/todoapp/data/repository/TodoRepositoryTest.kt b/src/test/java/com/example/todoapp/data/repository/TodoRepositoryTest.kt new file mode 100644 index 0000000..08e1899 --- /dev/null +++ b/src/test/java/com/example/todoapp/data/repository/TodoRepositoryTest.kt @@ -0,0 +1,47 @@ +package com.example.todoapp.data.repository + +import com.example.todoapp.data.dao.TodoDao +import com.example.todoapp.data.model.Todo +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class TodoRepositoryTest { + private val todoDao = mockk() + private val todoRepository = TodoRepository(todoDao) + + @Test + fun `addTodo should save todo item successfully`() = runBlocking { + // Arrange + val todo = Todo(title = "Test Todo") + coEvery { todoDao.insertTodo(todo) } returns 1L + + // Act + val result = todoRepository.addTodo(todo) + + // Assert + assertEquals(1L, result) + } + + @Test(expected = IllegalArgumentException::class) + fun `addTodo should throw exception for blank title`() = runBlocking { + // Arrange + val todo = Todo(title = "") + + // Act & Assert + todoRepository.addTodo(todo) + } + + @Test(expected = IllegalArgumentException::class) + fun `addTodo should throw exception for title exceeding max length`() = runBlocking { + // Arrange + val longTitle = "a".repeat(101) + val todo = Todo(title = longTitle) + + // Act & Assert + todoRepository.addTodo(todo) + } +} \ No newline at end of file From f870b696aef70951432731ae3e1ce9aa341d596f Mon Sep 17 00:00:00 2001 From: Aflame7121 Date: Sat, 28 Jun 2025 17:31:39 +0000 Subject: [PATCH 7/7] Configure project dependencies for Room, Coroutines, and Testing --- build.gradle.kts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 build.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..7172fb3 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-kapt") + id("dagger.hilt.android.plugin") +} + +dependencies { + // Room Database + implementation("androidx.room:room-runtime:2.5.1") + implementation("androidx.room:room-ktx:2.5.1") + kapt("androidx.room:room-compiler:2.5.1") + + // Coroutines + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") + + // Testing + testImplementation("junit:junit:4.13.2") + testImplementation("io.mockk:mockk:1.13.5") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} \ No newline at end of file