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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Gradle files
.gradle/
build/

# Local configuration file
local.properties

# Android Studio generated files
*.iml
.idea/

# Compiled class files
*.class

# Log files
*.log

# Android generated files
bin/
gen/
out/

# Dependency directories
/captures
.externalNativeBuild
.cxx

# macOS system files
.DS_Store

# Backup files
*.bak
*.swp

# Kotlin build artifacts
*.jar
*.war
*.ear

# Virtual machine crash logs
hs_err_pid*

# Android Profiling
*.hprof
55 changes: 55 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
}

android {
namespace = "com.todoapp"
compileSdk = 33

defaultConfig {
applicationId = "com.todoapp"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}

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")

// Kotlin standard library
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.20")

// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20")

// Android testing
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
50 changes: 50 additions & 0 deletions app/src/main/java/com/todoapp/data/entity/Todo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.todoapp.data.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.time.LocalDateTime

/**
* Room database entity representing a Todo item.
*
* @property id Unique identifier for the Todo item
* @property title Title of the Todo item
* @property description Detailed description of the Todo item
* @property isCompleted Indicates whether the Todo item is completed
* @property createdAt Timestamp of when the Todo item was created
* @property dueDate Optional due date for the Todo item
*/
@Entity(tableName = "todos")
data class Todo(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,

@ColumnInfo(name = "title")
val title: String,

@ColumnInfo(name = "description")
val description: String? = null,

@ColumnInfo(name = "is_completed")
val isCompleted: Boolean = false,

@ColumnInfo(name = "created_at")
val createdAt: LocalDateTime = LocalDateTime.now(),

@ColumnInfo(name = "due_date")
val dueDate: LocalDateTime? = null
) {
/**
* Validates the Todo item properties.
*
* @throws IllegalArgumentException if validation fails
*/
init {
require(title.isNotBlank()) { "Title cannot be blank" }
require(title.length <= 100) { "Title cannot exceed 100 characters" }
description?.let {
require(it.length <= 500) { "Description cannot exceed 500 characters" }
}
}
}
66 changes: 66 additions & 0 deletions app/src/test/java/com/todoapp/data/entity/TodoTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.todoapp.data.entity

import org.junit.Test
import java.time.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull

class TodoTest {

@Test
fun `create todo with valid data`() {
val todo = Todo(
title = "Test Todo",
description = "Test description"
)

assertNotNull(todo)
assertEquals("Test Todo", todo.title)
assertEquals("Test description", todo.description)
assertEquals(false, todo.isCompleted)
assertNotNull(todo.createdAt)
}

@Test
fun `create todo with all properties`() {
val dueDate = LocalDateTime.now().plusDays(1)
val todo = Todo(
title = "Complete project",
description = "Finish the todo app implementation",
isCompleted = true,
dueDate = dueDate
)

assertEquals("Complete project", todo.title)
assertEquals("Finish the todo app implementation", todo.description)
assertEquals(true, todo.isCompleted)
assertEquals(dueDate, todo.dueDate)
}

@Test
fun `fail to create todo with blank title`() {
assertFailsWith<IllegalArgumentException> {
Todo(title = "")
}
}

@Test
fun `fail to create todo with title exceeding 100 characters`() {
val longTitle = "a".repeat(101)
assertFailsWith<IllegalArgumentException> {
Todo(title = longTitle)
}
}

@Test
fun `fail to create todo with description exceeding 500 characters`() {
val longDescription = "a".repeat(501)
assertFailsWith<IllegalArgumentException> {
Todo(
title = "Valid Title",
description = longDescription
)
}
}
}