Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1] 지출내역 Database 구현 #2

Merged
merged 10 commits into from
Nov 28, 2024
15 changes: 11 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.ksp)
}

android {
Expand All @@ -18,6 +19,9 @@ android {
vectorDrawables {
useSupportLibrary = true
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
}

buildTypes {
Expand All @@ -30,11 +34,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_19
targetCompatibility = JavaVersion.VERSION_19
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "19"
jvmTarget = "17"
}
buildFeatures {
compose = true
Expand All @@ -50,7 +54,6 @@ android {
}

dependencies {

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand All @@ -59,6 +62,10 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)

implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down

This file was deleted.

173 changes: 173 additions & 0 deletions app/src/androidTest/java/kr/ksw/mybudget/data/local/SpendingDaoTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package kr.ksw.mybudget.data.local

import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import kr.ksw.mybudget.R
import kr.ksw.mybudget.data.local.dao.SpendingDao
import kr.ksw.mybudget.data.local.databases.MyBudgetDatabase
import kr.ksw.mybudget.data.local.entity.SpendingEntity
import kr.ksw.mybudget.data.local.mock.MAJOR_CATEGORY_FOOD
import kr.ksw.mybudget.data.local.mock.MAJOR_CATEGORY_LIFE_STYLE
import kr.ksw.mybudget.data.local.mock.spendingList
import kr.ksw.mybudget.data.local.mock.spendingListForBetween
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
import java.time.LocalDate

@RunWith(AndroidJUnit4::class)
class SpendingDaoTest {
private lateinit var db: MyBudgetDatabase
private lateinit var dao: SpendingDao

@Before
fun createDB() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(
context,
MyBudgetDatabase::class.java
).build()
dao = db.spendingDao
}

@After
@Throws(IOException::class)
fun closeDB() {
db.close()
}

@Test
fun getAllSpendingEntitiesIsEmpty() = runTest {
val result = dao.getAllSpendingEntities().first()
assert(result.isEmpty())
}

@Test
fun upsertSpendingEntity() = runTest {
val context = ApplicationProvider.getApplicationContext<Context>()
dao.upsertSpendingEntity(SpendingEntity(
title = "스타벅스",
date = LocalDate.now(),
majorCategory = context.resources.getInteger(R.integer.category_food),
subCategory = context.resources.getInteger(R.integer.category_cafe),
price = 5_000
))
val result = dao.getAllSpendingEntities().first()
assert(result.isNotEmpty())
}

@Test
fun deleteSpendingEntity() = runTest {
val context = ApplicationProvider.getApplicationContext<Context>()
val spending = SpendingEntity(
id = 1,
title = "스타벅스",
date = LocalDate.now(),
majorCategory = context.resources.getInteger(R.integer.category_food),
subCategory = context.resources.getInteger(R.integer.category_cafe),
price = 5_000
)
dao.upsertSpendingEntity(spending)
assert(dao.getAllSpendingEntities().first().isNotEmpty())
dao.deleteSpendingEntity(spending)
assert(dao.getAllSpendingEntities().first().isEmpty())
}

@Test
fun `getSpendingEntitiesByMajorCategory Test Food Result Count is 2`() = runTest {
spendingList.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
assert(dao.getAllSpendingEntities().first().isNotEmpty())

val result = dao.getSpendingEntitiesByMajorCategory(MAJOR_CATEGORY_FOOD)
assert(result.isNotEmpty())
assert(result.size == 2)
}

@Test
fun `getSpendingEntitiesBySubCategory Test LifeStyle And Transportation Category`() = runTest {
spendingList.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
assert(dao.getAllSpendingEntities().first().isNotEmpty())

val resultLifeStyle = dao.getSpendingEntitiesByMajorCategory(MAJOR_CATEGORY_LIFE_STYLE)
assert(resultLifeStyle.size == 2)

val categoryTransportation = 31
val resultTransportation = dao.getSpendingEntitiesBySubCategory(categoryTransportation)
assert(resultTransportation.size == 1)
}

@Test
fun `getSpendingEntitiesBetween Today`() = runTest {
spendingList.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
val from = LocalDate.now()
val to = LocalDate.now()
val result = dao.getSpendingEntitiesBetween(from, to)
assert(result.isNotEmpty())
assert(result.size == spendingList.size)
}

@Test
fun `getSpendingEntitiesBetween Week`() = runTest {
val now = LocalDate.now()
val from = now.plusDays(-((now.dayOfWeek.value - 1).toLong()))
val to = now.plusDays((7 - now.dayOfWeek.value).toLong())
spendingListForBetween.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
val result = dao.getSpendingEntitiesBetween(from, to)
assert(result.isNotEmpty())
assert(result.size == 1)
}

@Test
fun `getSpendingEntitiesBetween Month`() = runTest {
val now = LocalDate.now()
val from = now.withDayOfMonth(1)
val to = now.withDayOfMonth(now.lengthOfMonth())
spendingListForBetween.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
val result = dao.getSpendingEntitiesBetween(from, to)
assert(result.isNotEmpty())
assert(result.size == 2)
}

@Test
fun `getSpendingEntitiesBetween One Month Before`() = runTest {
val month = LocalDate.now().plusMonths(-1L)
val from = month.withDayOfMonth(1)
val to = month.withDayOfMonth(month.lengthOfMonth())
println("$from, $to")
spendingListForBetween.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
val result = dao.getSpendingEntitiesBetween(from, to)
assert(result.isNotEmpty())
assert(result.size == 1)
}

@Test
fun `getSpendingEntitiesBetween Year`() = runTest {
val now = LocalDate.now()
val from = now.withDayOfYear(1)
println("$from, $now")
spendingListForBetween.forEach { spending ->
dao.upsertSpendingEntity(spending)
}
val result = dao.getSpendingEntitiesBetween(from, now)
assert(result.isNotEmpty())
assert(result.size == spendingListForBetween.size)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package kr.ksw.mybudget.data.local.mock

import kr.ksw.mybudget.data.local.entity.SpendingEntity
import java.time.LocalDate

const val MAJOR_CATEGORY_FOOD = 1
const val MAJOR_CATEGORY_CULTURE = 2
const val MAJOR_CATEGORY_LIFE_STYLE = 3
const val MAJOR_CATEGORY_CASH = 4

val spendingList = listOf(
SpendingEntity(
title = "스타벅스",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_FOOD,
subCategory = 12,
price = 6_000
),
SpendingEntity(
title = "할리스",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_FOOD,
subCategory = 12,
price = 5_000
),
SpendingEntity(
title = "영화",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_CULTURE,
subCategory = 22,
price = 15_000
),
SpendingEntity(
title = "교통비",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_LIFE_STYLE,
subCategory = 31,
price = 100_000
),
SpendingEntity(
title = "통신비",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_LIFE_STYLE,
subCategory = 32,
price = 50_000
),
SpendingEntity(
title = "송금",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_CASH,
subCategory = 41,
price = 200_000
),
)

val spendingListForBetween = listOf(
SpendingEntity(
title = "송금",
date = LocalDate.now().plusWeeks(-1L),
majorCategory = MAJOR_CATEGORY_CASH,
subCategory = 41,
price = 200_000
),
SpendingEntity(
title = "송금",
date = LocalDate.now().plusMonths(-1L),
majorCategory = MAJOR_CATEGORY_CASH,
subCategory = 41,
price = 200_000
),
SpendingEntity(
title = "송금",
date = LocalDate.now(),
majorCategory = MAJOR_CATEGORY_CASH,
subCategory = 41,
price = 200_000
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package kr.ksw.mybudget.data.local.converter

import androidx.room.TypeConverter
import java.time.LocalDate

class Converters {
@TypeConverter
fun fromTimestamp(value: Int?): LocalDate? {
return value?.let { LocalDate.ofYearDay(LocalDate.now().year, value) }
}

@TypeConverter
fun dateToTimestamp(date: LocalDate?): Int? {
return date?.dayOfYear
}
}
38 changes: 38 additions & 0 deletions app/src/main/java/kr/ksw/mybudget/data/local/dao/SpendingDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kr.ksw.mybudget.data.local.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Query
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
import kr.ksw.mybudget.data.local.entity.SpendingEntity
import java.time.LocalDate
import java.util.Date

@Dao
interface SpendingDao {
@Upsert
suspend fun upsertSpendingEntity(spendingEntity: SpendingEntity)

@Delete
suspend fun deleteSpendingEntity(spendingEntity: SpendingEntity)

@Query("SELECT * FROM spending_table")
fun getAllSpendingEntities(): Flow<List<SpendingEntity>>

@Query("SELECT * FROM spending_table WHERE majorCategory = :majorCategory")
suspend fun getSpendingEntitiesByMajorCategory(
majorCategory: Int
): List<SpendingEntity>

@Query("SELECT * FROM spending_table WHERE subCategory = :subCategory")
suspend fun getSpendingEntitiesBySubCategory(
subCategory: Int
): List<SpendingEntity>

@Query("SELECT * FROM spending_table WHERE date BETWEEN :from AND :to")
suspend fun getSpendingEntitiesBetween(
from: LocalDate,
to: LocalDate
): List<SpendingEntity>
}
Loading
Loading