From 478d37ced1a4c122f79256d823020bffbae999ba Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:17:03 +0900 Subject: [PATCH 01/20] feat: add Goal property createdAt, compledAt --- .../chipichipi/dobedobe/core/model/Goal.kt | 11 +++ .../preview/GoalPreviewParameterProvider.kt | 73 +++++++++++++++++-- .../feature/goal/component/GoalRow.kt | 7 ++ 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt b/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt index 1defab5a..e79359b8 100644 --- a/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt +++ b/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt @@ -1,11 +1,22 @@ package com.chipichipi.dobedobe.core.model +import kotlinx.datetime.Instant + data class Goal( val id: Long, val title: String, val isPinned: Boolean, val state: State, + val createdAt: Instant, + val completedAt: Instant?, ) { + init { + when (state) { + State.Todo -> require(completedAt == null) { "$state should not have completedAt" } + State.Done -> require(completedAt != null) { "$state should have completedAt" } + } + } + val isDone: Boolean get() = state == State.Done diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt index 302b6cfb..64857287 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt @@ -2,6 +2,7 @@ package com.chipichipi.dobedobe.feature.dashboard.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.chipichipi.dobedobe.core.model.Goal +import kotlinx.datetime.Instant internal class GoalPreviewParameterProvider : PreviewParameterProvider> { override val values: Sequence> @@ -17,14 +18,70 @@ internal class GoalPreviewParameterProvider : PreviewParameterProvider = listOf( - Goal(1, "3대 500 달성하기", true, Goal.State.Todo), - Goal(2, "영어 단어 하루 10개 외우기", false, Goal.State.Done), - Goal(3, "두비두비 출시", false, Goal.State.Todo), - Goal(4, "주간 러닝 3회", true, Goal.State.Todo), - Goal(5, "하루 2L 물 마시기", false, Goal.State.Todo), - Goal(6, "책 5권 읽기", false, Goal.State.Todo), - Goal(7, "일본 여행 다녀오기", false, Goal.State.Done), - Goal(8, "운동 루틴 정착", true, Goal.State.Done), + Goal( + id = 1, + title = "3대 500 달성하기", + isPinned = true, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 2, + title = "영어 단어 하루 10개 외우기", + isPinned = false, + state = Goal.State.Done, + createdAt = Instant.DISTANT_PAST, + completedAt = Instant.DISTANT_PAST, + ), + Goal( + id = 3, + title = "두비두비 출시", + isPinned = false, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 4, + title = "주간 러닝 3회", + isPinned = true, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 5, + title = "하루 2L 물 마시기", + isPinned = false, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 6, + title = "책 5권 읽기", + isPinned = false, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 7, + title = "일본 여행 다녀오기", + isPinned = false, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), + Goal( + id = 8, + title = "운동 루틴 정착", + isPinned = true, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), ) fun fakeGoals(size: Int): List = diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt index ed2274ab..876ff5e8 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt @@ -26,6 +26,7 @@ import com.chipichipi.dobedobe.core.designsystem.component.DobeDobeCheckBox import com.chipichipi.dobedobe.core.designsystem.component.ThemePreviews import com.chipichipi.dobedobe.core.designsystem.theme.DobeDobeTheme import com.chipichipi.dobedobe.core.model.Goal +import kotlinx.datetime.Instant @Composable fun GoalRow( @@ -83,6 +84,8 @@ private fun GoalRowPreview() { title = "Todo", isPinned = false, state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, ), onGoalDone = {}, onGoalClicked = {}, @@ -94,6 +97,8 @@ private fun GoalRowPreview() { title = "Done", isPinned = false, state = Goal.State.Done, + createdAt = Instant.DISTANT_PAST, + completedAt = Instant.DISTANT_PAST, ), onGoalDone = {}, onGoalClicked = {}, @@ -105,6 +110,8 @@ private fun GoalRowPreview() { title = "Pinned", isPinned = true, state = Goal.State.Done, + createdAt = Instant.DISTANT_PAST, + completedAt = Instant.DISTANT_PAST, ), onGoalDone = {}, onGoalClicked = {}, From dc91014255aa1e0c5ff90fac1d1256a7cd4b7072 Mon Sep 17 00:00:00 2001 From: murjune Date: Fri, 24 Jan 2025 01:06:13 +0900 Subject: [PATCH 02/20] build: add room dependency --- core/database/build.gradle.kts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index aaa22512..e843ba75 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -1,13 +1,28 @@ plugins { alias(libs.plugins.dobedobe.android.library) + alias(libs.plugins.room) + alias(libs.plugins.ksp) } android { namespace = "com.chipichipi.dobedobe.core.database" } +ksp { + arg("room.generateKotlin", "true") +} + +room { + schemaDirectory("$projectDir/schemas") +} + dependencies { api(projects.core.model) implementation(platform(libs.koin.bom)) implementation(libs.koin.core) + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) + + androidTestImplementation(libs.androidx.room.testing) } From ab3cf4514db3584a9998e41c5dbb0547f25e9b59 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:17:14 +0900 Subject: [PATCH 03/20] feat: add GoalEntity --- .../core/database/entity/GoalEntity.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt new file mode 100644 index 00000000..d74a9a31 --- /dev/null +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt @@ -0,0 +1,21 @@ +package com.chipichipi.dobedobe.core.database.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.chipichipi.dobedobe.core.database.entity.GoalEntity.Companion.TABLE_NAME +import com.chipichipi.dobedobe.core.model.Goal.State +import kotlinx.datetime.Instant + +@Entity(tableName = TABLE_NAME) +data class GoalEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val title: String, + val isPinned: Boolean, + val state: State, + val createdAt: Instant, + val completedAt: Instant?, +) { + companion object { + const val TABLE_NAME = "Goal" + } +} From 40329603b9dfa2023df712bf10a6a040b3c11358 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:17:29 +0900 Subject: [PATCH 04/20] feat: add InstantConvertor --- .../core/database/convertor/InstantConverter.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/convertor/InstantConverter.kt diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/convertor/InstantConverter.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/convertor/InstantConverter.kt new file mode 100644 index 00000000..f62c6133 --- /dev/null +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/convertor/InstantConverter.kt @@ -0,0 +1,14 @@ +package com.chipichipi.dobedobe.core.database.convertor + +import androidx.room.TypeConverter +import kotlinx.datetime.Instant + +internal class InstantConverter { + @TypeConverter + fun longToInstant(value: Long?): Instant? = + value?.let(Instant::fromEpochMilliseconds) + + @TypeConverter + fun instantToLong(instant: Instant?): Long? = + instant?.toEpochMilliseconds() +} From 0eee984ac8407cbea6ea4f8d93f012c406bf436d Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:17:44 +0900 Subject: [PATCH 05/20] feat: add db, dao --- .../dobedobe/core/database/dao/GoalDao.kt | 33 +++++++++++++++++++ .../core/database/db/DobeDobeDatabase.kt | 23 +++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt create mode 100644 core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt new file mode 100644 index 00000000..2c2648e0 --- /dev/null +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt @@ -0,0 +1,33 @@ +package com.chipichipi.dobedobe.core.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.chipichipi.dobedobe.core.database.entity.GoalEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface GoalDao { + @Query("SELECT * FROM Goal") + fun getGoals(): Flow> + + @Query("SELECT * FROM Goal WHERE id = :id") + fun getGoal(id: Long): Flow + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun saveGoal(goals: GoalEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun saveGoals(goals: List) + + @Query("DELETE FROM Goal WHERE id = :goalId") + suspend fun deleteGoal(goalId: Long) + + @Update + suspend fun updateGoal(goal: GoalEntity) + + @Query("DELETE FROM Goal") + suspend fun clear() +} diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt new file mode 100644 index 00000000..c31346eb --- /dev/null +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt @@ -0,0 +1,23 @@ +package com.chipichipi.dobedobe.core.database.db + +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.chipichipi.dobedobe.core.database.convertor.InstantConverter +import com.chipichipi.dobedobe.core.database.dao.GoalDao +import com.chipichipi.dobedobe.core.database.entity.GoalEntity + +@TypeConverters( + InstantConverter::class, +) +@Database( + entities = [GoalEntity::class], + version = 1, +) +abstract class DobeDobeDatabase : RoomDatabase() { + abstract fun goalDao(): GoalDao + + companion object { + const val DATABASE_NAME = "dobedobe.db" + } +} From 3331b2f3e2b16d358a440c47db1cd22a1fe4ebce Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:18:00 +0900 Subject: [PATCH 06/20] feat: load to koinModule --- .../chipichipi/dobedobe/core/database/di/DaoModule.kt | 4 ++++ .../dobedobe/core/database/di/DatabaseModule.kt | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DaoModule.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DaoModule.kt index 1f0ab210..38c11bb8 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DaoModule.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DaoModule.kt @@ -1,8 +1,12 @@ package com.chipichipi.dobedobe.core.database.di +import com.chipichipi.dobedobe.core.database.dao.GoalDao +import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase import org.koin.dsl.module val daoModule = module { includes(databaseModule) + + single { get().goalDao() } } diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt index 012213eb..9fa38067 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt @@ -1,7 +1,18 @@ package com.chipichipi.dobedobe.core.database.di +import androidx.room.Room +import androidx.room.RoomDatabase +import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase import org.koin.dsl.module internal val databaseModule = module { + single { + Room.databaseBuilder( + get(), + DobeDobeDatabase::class.java, + DobeDobeDatabase.DATABASE_NAME, + ).fallbackToDestructiveMigration() // TODO: 최초 배포 시 삭제 + .build() + } } From 47cdd8fe45ab80852e27bd791a9122a955dc2625 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:18:07 +0900 Subject: [PATCH 07/20] chore: delete .gitkeep --- .../main/kotlin/com/chipichipi/dobedobe/core/database/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/.gitkeep diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/.gitkeep b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 9ba121f15992205b3bba92a9fdccc3c2056ed634 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:18:22 +0900 Subject: [PATCH 08/20] test: add goalEntity fixtures --- .../core/database/fixtures/FakeGoalEntity.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt new file mode 100644 index 00000000..00bc6a72 --- /dev/null +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt @@ -0,0 +1,35 @@ +package com.chipichipi.dobedobe.core.database.fixtures + +import com.chipichipi.dobedobe.core.database.entity.GoalEntity +import com.chipichipi.dobedobe.core.model.Goal +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant + +fun fakeGoalEntities( + vararg titles: String, +): List { + return titles.mapIndexed { index, title -> + fakeGoalEntity( + id = (index + 1).toLong(), + title = title, + ) + } +} + +fun fakeGoalEntity( + id: Long, + title: String, + isPinned: Boolean = false, + state: Goal.State = Goal.State.Todo, + createdAt: Instant = Clock.System.now(), + completedAt: Instant? = null, +): GoalEntity { + return GoalEntity( + id = id, + title = title, + isPinned = isPinned, + state = state, + createdAt = createdAt, + completedAt = completedAt, + ) +} From ec9cfc7be1852d776780bece61cef5a8307d6422 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:18:39 +0900 Subject: [PATCH 09/20] test: GoalDao Test --- .../core/database/ExampleInstrumentedTest.kt | 22 ------ .../dobedobe/core/database/dao/GoalDaoTest.kt | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 22 deletions(-) delete mode 100644 core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/ExampleInstrumentedTest.kt create mode 100644 core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/ExampleInstrumentedTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/ExampleInstrumentedTest.kt deleted file mode 100644 index 23887c51..00000000 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.chipichipi.dobedobe.core.database - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.chipichipi.dobedobe.core.database.test", appContext.packageName) - } -} diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt new file mode 100644 index 00000000..d21e4474 --- /dev/null +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt @@ -0,0 +1,68 @@ +package com.chipichipi.dobedobe.core.database.dao + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase +import com.chipichipi.dobedobe.core.database.entity.GoalEntity +import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntities +import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntity +import com.chipichipi.dobedobe.core.model.Goal +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Before +import kotlin.test.Test + +class GoalDaoTest { + private lateinit var goalDao: GoalDao + + @Before + fun setUp() { + goalDao = + Room.inMemoryDatabaseBuilder( + ApplicationProvider.getApplicationContext(), + DobeDobeDatabase::class.java, + ).build().goalDao() + } + + @Test + fun 목표_저장_테스트() = runTest { + // given + val goals: List = fakeGoalEntities("준원", "준혁") + + // when + goalDao.saveGoals(goals) + + // then + val retrievedGoals: List = goalDao.getGoals().first() + val retrievedGoalTitles = retrievedGoals.map { it.title } + retrievedGoals.shouldHaveSize(2) + retrievedGoalTitles shouldBe listOf("준원", "준혁") + } + + @Test + fun 목표_저장후_삭제_테스트() = runTest { + // given + val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원") + + // when + goalDao.saveGoal(goal) + goalDao.deleteGoal(goalId = 1L) + // then + val retrievedGoals: List = goalDao.getGoals().first() + retrievedGoals.shouldHaveSize(0) + } + + @Test + fun 목표_완료하기_테스트() = runTest { + // given + val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", state = Goal.State.Todo) + // when + goalDao.saveGoal(goal) + goalDao.updateGoal(goal.copy(state = Goal.State.Done)) + // then + val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() + retrievedGoal.state shouldBe Goal.State.Done + } +} From 5cbf5244411e6a7002f6b746bc5040d20b403794 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:28:41 +0900 Subject: [PATCH 10/20] feat: db migration base --- core/database/build.gradle.kts | 4 ++++ .../chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt | 3 +++ .../chipichipi/dobedobe/core/database/di/DatabaseModule.kt | 1 + 3 files changed, 8 insertions(+) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index e843ba75..951b0376 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -6,6 +6,10 @@ plugins { android { namespace = "com.chipichipi.dobedobe.core.database" + + sourceSets { + getByName("androidTest").assets.srcDir("$projectDir/schemas") + } } ksp { diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt index c31346eb..f56ed05f 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt @@ -3,6 +3,7 @@ package com.chipichipi.dobedobe.core.database.db import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters +import androidx.room.migration.Migration import com.chipichipi.dobedobe.core.database.convertor.InstantConverter import com.chipichipi.dobedobe.core.database.dao.GoalDao import com.chipichipi.dobedobe.core.database.entity.GoalEntity @@ -13,11 +14,13 @@ import com.chipichipi.dobedobe.core.database.entity.GoalEntity @Database( entities = [GoalEntity::class], version = 1, + ) abstract class DobeDobeDatabase : RoomDatabase() { abstract fun goalDao(): GoalDao companion object { const val DATABASE_NAME = "dobedobe.db" + val MIGRATIONS: Array = arrayOf() } } diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt index 9fa38067..a0158b67 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt @@ -13,6 +13,7 @@ internal val databaseModule = DobeDobeDatabase::class.java, DobeDobeDatabase.DATABASE_NAME, ).fallbackToDestructiveMigration() // TODO: 최초 배포 시 삭제 + .addMigrations(*DobeDobeDatabase.MIGRATIONS) .build() } } From 020e8394cbb6952ee36b5821162f4d9034577d63 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:28:50 +0900 Subject: [PATCH 11/20] test: db migration All test --- .../1.json | 64 +++++++++++++++++++ .../core/database/db/DobeDobeMigrationTest.kt | 44 +++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json create mode 100644 core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt diff --git a/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json b/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json new file mode 100644 index 00000000..ff133afe --- /dev/null +++ b/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json @@ -0,0 +1,64 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "12a643d55ca1f7cd0240a2b873dfe241", + "entities": [ + { + "tableName": "Goal", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, `state` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `completedAt` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPinned", + "columnName": "isPinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completedAt", + "columnName": "completedAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '12a643d55ca1f7cd0240a2b873dfe241')" + ] + } +} \ No newline at end of file diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt new file mode 100644 index 00000000..7f513128 --- /dev/null +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt @@ -0,0 +1,44 @@ +package com.chipichipi.dobedobe.core.database.db + +import android.content.Context +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Rule +import org.junit.Test +import org.junit.jupiter.api.DisplayName +import java.io.IOException + +class DobeDobeMigrationTest { + @get:Rule + val helper = + MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + DobeDobeDatabase::class.java, + ) + + @Test + @DisplayName("모든 database 버전 마이그레이션 테스트") + @Throws(IOException::class) + fun test_migrateAll() { + // given + val dbName = "TEST_DB" + val oldestDbVersion = 1 + val testContext = ApplicationProvider.getApplicationContext() + + // when : 가장 오래된 버전의 DB를 생성하고 닫음 + helper.createDatabase(dbName, oldestDbVersion) + .close() + + // then : 최신 버전 DB로 마이그레이션 후 검증 + Room.databaseBuilder( + testContext, + DobeDobeDatabase::class.java, + dbName, + ).addMigrations(*DobeDobeDatabase.MIGRATIONS) + .build().apply { + openHelper.writableDatabase.close() + } + } +} \ No newline at end of file From c28453b713e8d99e0ada31dabcbb0f2cc6360828 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 01:29:57 +0900 Subject: [PATCH 12/20] style: ktFormat --- .../dobedobe/core/database/dao/GoalDaoTest.kt | 65 ++++++++++--------- .../core/database/db/DobeDobeMigrationTest.kt | 2 +- .../core/database/db/DobeDobeDatabase.kt | 1 - 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt index d21e4474..931858f1 100644 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt @@ -27,42 +27,45 @@ class GoalDaoTest { } @Test - fun 목표_저장_테스트() = runTest { - // given - val goals: List = fakeGoalEntities("준원", "준혁") + fun 목표_저장_테스트() = + runTest { + // given + val goals: List = fakeGoalEntities("준원", "준혁") - // when - goalDao.saveGoals(goals) + // when + goalDao.saveGoals(goals) - // then - val retrievedGoals: List = goalDao.getGoals().first() - val retrievedGoalTitles = retrievedGoals.map { it.title } - retrievedGoals.shouldHaveSize(2) - retrievedGoalTitles shouldBe listOf("준원", "준혁") - } + // then + val retrievedGoals: List = goalDao.getGoals().first() + val retrievedGoalTitles = retrievedGoals.map { it.title } + retrievedGoals.shouldHaveSize(2) + retrievedGoalTitles shouldBe listOf("준원", "준혁") + } @Test - fun 목표_저장후_삭제_테스트() = runTest { - // given - val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원") + fun 목표_저장후_삭제_테스트() = + runTest { + // given + val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원") - // when - goalDao.saveGoal(goal) - goalDao.deleteGoal(goalId = 1L) - // then - val retrievedGoals: List = goalDao.getGoals().first() - retrievedGoals.shouldHaveSize(0) - } + // when + goalDao.saveGoal(goal) + goalDao.deleteGoal(goalId = 1L) + // then + val retrievedGoals: List = goalDao.getGoals().first() + retrievedGoals.shouldHaveSize(0) + } @Test - fun 목표_완료하기_테스트() = runTest { - // given - val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", state = Goal.State.Todo) - // when - goalDao.saveGoal(goal) - goalDao.updateGoal(goal.copy(state = Goal.State.Done)) - // then - val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() - retrievedGoal.state shouldBe Goal.State.Done - } + fun 목표_완료하기_테스트() = + runTest { + // given + val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", state = Goal.State.Todo) + // when + goalDao.saveGoal(goal) + goalDao.updateGoal(goal.copy(state = Goal.State.Done)) + // then + val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() + retrievedGoal.state shouldBe Goal.State.Done + } } diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt index 7f513128..1e879ce8 100644 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeMigrationTest.kt @@ -41,4 +41,4 @@ class DobeDobeMigrationTest { openHelper.writableDatabase.close() } } -} \ No newline at end of file +} diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt index f56ed05f..9a833c7a 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt @@ -14,7 +14,6 @@ import com.chipichipi.dobedobe.core.database.entity.GoalEntity @Database( entities = [GoalEntity::class], version = 1, - ) abstract class DobeDobeDatabase : RoomDatabase() { abstract fun goalDao(): GoalDao From 2ab8f8853720b985412bd2926f67a18f946e9653 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 23 Jan 2025 18:58:07 +0900 Subject: [PATCH 13/20] chore: rename dao insert Method --- .../chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt | 6 +++--- .../com/chipichipi/dobedobe/core/database/dao/GoalDao.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt index 931858f1..17b92dcf 100644 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt @@ -33,7 +33,7 @@ class GoalDaoTest { val goals: List = fakeGoalEntities("준원", "준혁") // when - goalDao.saveGoals(goals) + goalDao.insertGoals(goals) // then val retrievedGoals: List = goalDao.getGoals().first() @@ -49,7 +49,7 @@ class GoalDaoTest { val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원") // when - goalDao.saveGoal(goal) + goalDao.insertGoal(goal) goalDao.deleteGoal(goalId = 1L) // then val retrievedGoals: List = goalDao.getGoals().first() @@ -62,7 +62,7 @@ class GoalDaoTest { // given val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", state = Goal.State.Todo) // when - goalDao.saveGoal(goal) + goalDao.insertGoal(goal) goalDao.updateGoal(goal.copy(state = Goal.State.Done)) // then val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt index 2c2648e0..42224e6a 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDao.kt @@ -17,10 +17,10 @@ interface GoalDao { fun getGoal(id: Long): Flow @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun saveGoal(goals: GoalEntity) + suspend fun insertGoal(goals: GoalEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun saveGoals(goals: List) + suspend fun insertGoals(goals: List) @Query("DELETE FROM Goal WHERE id = :goalId") suspend fun deleteGoal(goalId: Long) From cc4c25bec626597fc6d8df01f10b713ce21c3198 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:21:11 +0900 Subject: [PATCH 14/20] chore: DobeDobeDatabase add internal modifier --- .../chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt index 9a833c7a..90eef240 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt @@ -15,7 +15,7 @@ import com.chipichipi.dobedobe.core.database.entity.GoalEntity entities = [GoalEntity::class], version = 1, ) -abstract class DobeDobeDatabase : RoomDatabase() { +internal abstract class DobeDobeDatabase : RoomDatabase() { abstract fun goalDao(): GoalDao companion object { From 904c9e6dedf0dbe59553b36f901a81d72ac429c7 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:21:49 +0900 Subject: [PATCH 15/20] chore: rearrange db annotation --- .../dobedobe/core/database/db/DobeDobeDatabase.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt index 90eef240..ff2a0f7b 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/db/DobeDobeDatabase.kt @@ -8,13 +8,13 @@ import com.chipichipi.dobedobe.core.database.convertor.InstantConverter import com.chipichipi.dobedobe.core.database.dao.GoalDao import com.chipichipi.dobedobe.core.database.entity.GoalEntity -@TypeConverters( - InstantConverter::class, -) @Database( entities = [GoalEntity::class], version = 1, ) +@TypeConverters( + InstantConverter::class, +) internal abstract class DobeDobeDatabase : RoomDatabase() { abstract fun goalDao(): GoalDao From bea10a086d3148bed73540273145a261a7804216 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:24:09 +0900 Subject: [PATCH 16/20] fix: change to generic type DobeDobeDatabase --- .../com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt index a0158b67..3b5c58b8 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/di/DatabaseModule.kt @@ -1,13 +1,12 @@ package com.chipichipi.dobedobe.core.database.di import androidx.room.Room -import androidx.room.RoomDatabase import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase import org.koin.dsl.module internal val databaseModule = module { - single { + single { Room.databaseBuilder( get(), DobeDobeDatabase::class.java, From ea4297213174c4295e33c5d6281a35a56e56a276 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:28:52 +0900 Subject: [PATCH 17/20] chore: import Goal.State --- .../chipichipi/dobedobe/core/database/entity/GoalEntity.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt index d74a9a31..84f7e67f 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt @@ -3,15 +3,16 @@ package com.chipichipi.dobedobe.core.database.entity import androidx.room.Entity import androidx.room.PrimaryKey import com.chipichipi.dobedobe.core.database.entity.GoalEntity.Companion.TABLE_NAME -import com.chipichipi.dobedobe.core.model.Goal.State +import com.chipichipi.dobedobe.core.model.Goal import kotlinx.datetime.Instant @Entity(tableName = TABLE_NAME) data class GoalEntity( - @PrimaryKey(autoGenerate = true) val id: Long = 0, + @PrimaryKey(autoGenerate = true) + val id: Long = 0, val title: String, val isPinned: Boolean, - val state: State, + val state: Goal.State, val createdAt: Instant, val completedAt: Instant?, ) { From 12575b8a39d211b654e1c69b8a92c9f3390d2613 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:32:03 +0900 Subject: [PATCH 18/20] style: ktFormat --- .../feature/dashboard/DashboardScreen.kt | 6 +-- .../component/DashboardPhotoFrameBox.kt | 6 +-- .../feature/goal/component/GoalRow.kt | 42 +++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/DashboardScreen.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/DashboardScreen.kt index f4194e7a..e727c8e0 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/DashboardScreen.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/DashboardScreen.kt @@ -81,12 +81,12 @@ private fun DashboardScreen( // Dim 처리로 인해 innerPadding은 Box에 적용 안하고 우선 component별로 각각 적용하도록 처리 Box( modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { when (uiState) { is DashboardUiState.Error, is DashboardUiState.Loading, - -> { + -> { CircularProgressIndicator( modifier = Modifier.size(24.dp), ) @@ -96,7 +96,7 @@ private fun DashboardScreen( uiState = uiState, photoFramesState = photoFramesState, innerPadding = innerPadding, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } } diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/component/DashboardPhotoFrameBox.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/component/DashboardPhotoFrameBox.kt index ecc940aa..40e51e1f 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/component/DashboardPhotoFrameBox.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/component/DashboardPhotoFrameBox.kt @@ -150,7 +150,7 @@ private fun SharedTransitionScope.CollapsedPhotoFrame( .rotate(rotation) .clip(RoundedCornerShape(10.dp)) .background(Color(0xFF616161)) - .clickable(onClick = onEmptyFrameClick) + .clickable(onClick = onEmptyFrameClick), ) } } @@ -216,7 +216,7 @@ private fun SharedTransitionScope.ExpandedPhotoFrame( @Composable private fun EmptyPhotoFrame( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { // TODO : 디자인 나오면 변경 필요 Box( @@ -246,7 +246,7 @@ private fun DashboardPhotoFrameBoxPreview() { innerPadding = PaddingValues(10.dp), isExpanded = false, toggleExpansion = {}, - onEmptyFrameClick = {} + onEmptyFrameClick = {}, ) } } diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt index eac361d9..c9854688 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalRow.kt @@ -78,37 +78,37 @@ private fun GoalRowPreview() { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { GoalRow( goal = Goal( - id = 1L, - title = "Todo", - isPinned = false, - state = Goal.State.Todo, - createdAt = Instant.DISTANT_PAST, - completedAt = null, - ), + id = 1L, + title = "Todo", + isPinned = false, + state = Goal.State.Todo, + createdAt = Instant.DISTANT_PAST, + completedAt = null, + ), onGoalDone = {}, onGoalClicked = {}, ) GoalRow( goal = Goal( - id = 1L, - title = "Done", - isPinned = false, - state = Goal.State.Done, - createdAt = Instant.DISTANT_PAST, - completedAt = Instant.DISTANT_PAST, - ), + id = 1L, + title = "Done", + isPinned = false, + state = Goal.State.Done, + createdAt = Instant.DISTANT_PAST, + completedAt = Instant.DISTANT_PAST, + ), onGoalDone = {}, onGoalClicked = {}, ) GoalRow( goal = Goal( - id = 1L, - title = "Pinned", - isPinned = true, - state = Goal.State.Done, - createdAt = Instant.DISTANT_PAST, - completedAt = Instant.DISTANT_PAST, - ), + id = 1L, + title = "Pinned", + isPinned = true, + state = Goal.State.Done, + createdAt = Instant.DISTANT_PAST, + completedAt = Instant.DISTANT_PAST, + ), onGoalDone = {}, onGoalClicked = {}, ) From 6b45575b86c699f98acb762f1dda18aaaad0b152 Mon Sep 17 00:00:00 2001 From: murjune Date: Sat, 25 Jan 2025 00:40:12 +0900 Subject: [PATCH 19/20] refactor: migrate Goal.State to isCompleted --- .../1.json | 12 ++++++------ .../dobedobe/core/database/dao/GoalDaoTest.kt | 9 +++++---- .../core/database/fixtures/FakeGoalEntity.kt | 5 ++--- .../dobedobe/core/database/entity/GoalEntity.kt | 3 +-- .../com/chipichipi/dobedobe/core/model/Goal.kt | 17 +++++------------ .../feature/dashboard/GoalBottomSheet.kt | 7 ++++--- .../preview/GoalPreviewParameterProvider.kt | 16 ++++++++-------- .../dobedobe/feature/goal/component/GoalRow.kt | 8 ++++---- 8 files changed, 35 insertions(+), 42 deletions(-) diff --git a/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json b/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json index ff133afe..3eba0414 100644 --- a/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json +++ b/core/database/schemas/com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase/1.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "12a643d55ca1f7cd0240a2b873dfe241", + "identityHash": "8d37e040dbba463eff631f177a3d3b67", "entities": [ { "tableName": "Goal", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, `state` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `completedAt` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, `isCompleted` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `completedAt` INTEGER)", "fields": [ { "fieldPath": "id", @@ -27,9 +27,9 @@ "notNull": true }, { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", + "fieldPath": "isCompleted", + "columnName": "isCompleted", + "affinity": "INTEGER", "notNull": true }, { @@ -58,7 +58,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '12a643d55ca1f7cd0240a2b873dfe241')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8d37e040dbba463eff631f177a3d3b67')" ] } } \ No newline at end of file diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt index 17b92dcf..419b8a8c 100644 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/dao/GoalDaoTest.kt @@ -6,11 +6,12 @@ import com.chipichipi.dobedobe.core.database.db.DobeDobeDatabase import com.chipichipi.dobedobe.core.database.entity.GoalEntity import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntities import com.chipichipi.dobedobe.core.database.fixtures.fakeGoalEntity -import com.chipichipi.dobedobe.core.model.Goal +import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant import org.junit.Before import kotlin.test.Test @@ -60,12 +61,12 @@ class GoalDaoTest { fun 목표_완료하기_테스트() = runTest { // given - val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", state = Goal.State.Todo) + val goal: GoalEntity = fakeGoalEntity(id = 1L, title = "준원", isCompleted = false) // when goalDao.insertGoal(goal) - goalDao.updateGoal(goal.copy(state = Goal.State.Done)) + goalDao.updateGoal(goal.copy(isCompleted = true, completedAt = Instant.DISTANT_FUTURE)) // then val retrievedGoal: GoalEntity = goalDao.getGoal(1L).first() - retrievedGoal.state shouldBe Goal.State.Done + retrievedGoal.isCompleted.shouldBeTrue() } } diff --git a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt index 00bc6a72..ba102c07 100644 --- a/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt +++ b/core/database/src/androidTest/kotlin/com/chipichipi/dobedobe/core/database/fixtures/FakeGoalEntity.kt @@ -1,7 +1,6 @@ package com.chipichipi.dobedobe.core.database.fixtures import com.chipichipi.dobedobe.core.database.entity.GoalEntity -import com.chipichipi.dobedobe.core.model.Goal import kotlinx.datetime.Clock import kotlinx.datetime.Instant @@ -20,7 +19,7 @@ fun fakeGoalEntity( id: Long, title: String, isPinned: Boolean = false, - state: Goal.State = Goal.State.Todo, + isCompleted: Boolean = false, createdAt: Instant = Clock.System.now(), completedAt: Instant? = null, ): GoalEntity { @@ -28,7 +27,7 @@ fun fakeGoalEntity( id = id, title = title, isPinned = isPinned, - state = state, + isCompleted = isCompleted, createdAt = createdAt, completedAt = completedAt, ) diff --git a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt index 84f7e67f..6f207d0a 100644 --- a/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt +++ b/core/database/src/main/kotlin/com/chipichipi/dobedobe/core/database/entity/GoalEntity.kt @@ -3,7 +3,6 @@ package com.chipichipi.dobedobe.core.database.entity import androidx.room.Entity import androidx.room.PrimaryKey import com.chipichipi.dobedobe.core.database.entity.GoalEntity.Companion.TABLE_NAME -import com.chipichipi.dobedobe.core.model.Goal import kotlinx.datetime.Instant @Entity(tableName = TABLE_NAME) @@ -12,7 +11,7 @@ data class GoalEntity( val id: Long = 0, val title: String, val isPinned: Boolean, - val state: Goal.State, + val isCompleted: Boolean, val createdAt: Instant, val completedAt: Instant?, ) { diff --git a/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt b/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt index e79359b8..bbb99bf8 100644 --- a/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt +++ b/core/model/src/main/kotlin/com/chipichipi/dobedobe/core/model/Goal.kt @@ -6,22 +6,15 @@ data class Goal( val id: Long, val title: String, val isPinned: Boolean, - val state: State, + val isCompleted: Boolean, val createdAt: Instant, val completedAt: Instant?, ) { init { - when (state) { - State.Todo -> require(completedAt == null) { "$state should not have completedAt" } - State.Done -> require(completedAt != null) { "$state should have completedAt" } + if (isCompleted) { + require(completedAt != null) { "completedAt should not be null when isCompleted is true" } + } else { + require(completedAt == null) { "completedAt should be null when isCompleted is false" } } } - - val isDone: Boolean - get() = state == State.Done - - enum class State { - Todo, - Done, - } } diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt index 2558ff35..1b06150d 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.unit.sp import com.chipichipi.dobedobe.core.model.Goal import com.chipichipi.dobedobe.feature.dashboard.preview.GoalPreviewParameterProvider import com.chipichipi.dobedobe.feature.goal.component.GoalRow +import kotlinx.datetime.Instant @Composable internal fun GoalBottomSheetContent( @@ -118,10 +119,10 @@ private fun GoalBottomSheetContentPreview( val onGoalDone: (Goal) -> Unit = { goals = goals.map { goal -> if (goal.id == it.id) { - return@map if (goal.state == Goal.State.Done) { - goal.copy(state = Goal.State.Todo) + return@map if (goal.isCompleted) { + goal.copy(isCompleted = false) } else { - goal.copy(state = Goal.State.Done) + goal.copy(isCompleted = true, completedAt = Instant.DISTANT_FUTURE) } } goal diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt index 64857287..956a8388 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/preview/GoalPreviewParameterProvider.kt @@ -22,7 +22,7 @@ internal class GoalPreviewParameterProvider : PreviewParameterProvider Date: Sat, 25 Jan 2025 00:42:00 +0900 Subject: [PATCH 20/20] chore: reformat --- .../dobedobe/feature/dashboard/GoalBottomSheet.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt index 1b06150d..8ec0d9b6 100644 --- a/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt +++ b/feature/dashboard/src/main/kotlin/com/chipichipi/dobedobe/feature/dashboard/GoalBottomSheet.kt @@ -57,10 +57,9 @@ internal fun GoalBottomSheetContent( private fun GoalBottomSheetHeader() { // TODO: 검색 기능 추가, parameter 는 그때 추가 Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) {