Skip to content

Commit

Permalink
Merge pull request #9 from larmie56/develop
Browse files Browse the repository at this point in the history
Add cache module and setup database
  • Loading branch information
larmie56 committed Aug 31, 2021
2 parents 71fef84 + 6044a97 commit b6bad85
Show file tree
Hide file tree
Showing 23 changed files with 244 additions and 61 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ plugins {

dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation(project(Project.cache))
}
5 changes: 0 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,3 @@ subprojects {
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
buildscript {
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21")
}
}
9 changes: 7 additions & 2 deletions buildSrc/src/main/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ fun DependencyHandler.testImplementation(vararg dependencies: Any) {
dependencies.forEach(::testImplementation)
}

fun DependencyHandler.kapt(dependencyNotation: String): Dependency? =
add("kapt", dependencyNotation)
fun DependencyHandler.kapt(dependency: Any): Dependency? = add(
"kapt", dependency
)

fun DependencyHandler.kapt(vararg dependencies: Any) {
dependencies.forEach(::kapt)
}

// endregion
4 changes: 3 additions & 1 deletion buildSrc/src/main/kotlin/Library.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ object Library {
const val navigationUiKtx: String =
"androidx.navigation:navigation-ui-ktx:${Version.navigation}"
const val room: String = "androidx.room:room-ktx:${Version.room}"
const val roomCompiler: String = "androidx.room:room-compiler:${Version.room}"
const val androidxJUnit: String = "androidx.test.ext:junit:${Version.androidxJUnit}"
const val espresso: String = "androidx.test.espresso:espresso-core:${Version.espresso}"
const val viewModel: String =
Expand Down Expand Up @@ -53,11 +54,12 @@ object Library {
const val truth: String = "com.google.truth:truth:${Version.truth}"

// Mockito
const val mockito: String = "org.mockito:mockito-core:${Version.mockito}"
const val mockito = "org.mockito.kotlin:mockito-kotlin:${Version.mockito}"
}

object Project {
const val executor: String = ":executor"
const val libExpense: String = ":lib_expense"
const val testUtils: String = ":test_utils"
const val cache: String = ":cache"
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Version.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Version {
const val truth: String = "1.1.3"

// Mockito
const val mockito: String = "3.11.2"
const val mockito: String = "3.2.0"

// ktlint
const val ktlint: String = "0.42.1"
Expand Down
2 changes: 0 additions & 2 deletions buildSrc/src/main/kotlin/extensions/KotlinJvmExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ private class KotlinJvmExtension : ProjectExtension {
override fun configure(extension: Any) {
if (extension !is KotlinJvmOptions) return
extension.apply {
freeCompilerArgs += "-Xuse-experimental=" +
"kotlinx.coroutines.ExperimentalCoroutinesApi"
freeCompilerArgs += "-Xexplicit-api=strict"
}
}
Expand Down
8 changes: 6 additions & 2 deletions buildSrc/src/main/kotlin/plugin/AndroidLibraryPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ class AndroidLibraryPlugin : BasePlugin() {
get() = {
implementation(
Library.daggerHiltAndroid,
Library.coroutines
Library.coroutines,
Library.room
)
testImplementation(
Library.junit,
Library.truth,
Library.mockito,
Library.coroutinesTest
)
kapt(Library.daggerHiltCompiler)
kapt(
Library.daggerHiltCompiler,
Library.roomCompiler
)
}

override val extensions: Array<ProjectExtension>
Expand Down
1 change: 1 addition & 0 deletions cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
3 changes: 3 additions & 0 deletions cache/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
androidLib
}
Empty file added cache/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions cache/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
5 changes: 5 additions & 0 deletions cache/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.expenselogger.cache">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.expenselogger.cache

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.expenselogger.cache.dao.ExpenseDao
import com.example.expenselogger.cache.entity.ExpenseEntity

@Database(entities = [ExpenseEntity::class], version = 1, exportSchema = false)
internal abstract class ExpenseDatabase : RoomDatabase() {

abstract val expenseDao: ExpenseDao

companion object {
private const val DATABASE_NAME: String = "expense_db"
fun build(context: Context): ExpenseDatabase = Room.databaseBuilder(
context.applicationContext,
ExpenseDatabase::class.java,
DATABASE_NAME
).build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.expenselogger.cache.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.expenselogger.cache.entity.ExpenseEntity

@Dao
internal interface ExpenseDao {
@Insert
fun insertExpense(expense: ExpenseEntity)

@Update
fun updateExpense(expense: ExpenseEntity)

@Query("SELECT * FROM expense WHERE id = :id")
fun getExpense(id: Long): ExpenseEntity

@Query("SELECT * FROM expense ORDER BY date ASC")
public fun getExpenses(): List<ExpenseEntity>

@Query("DELETE FROM expense WHERE id = :id")
fun deleteExpense(id: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.expenselogger.cache.di

import android.content.Context
import com.example.expenselogger.cache.ExpenseDatabase
import com.example.expenselogger.cache.dao.ExpenseDao
import com.example.expenselogger.cache.repository.ExpenseRepository
import com.example.expenselogger.cache.repository.ExpenseRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@[Module InstallIn(SingletonComponent::class)]
internal interface CacheModule {

@get:Binds
val ExpenseRepositoryImpl.expenseRepository: ExpenseRepository

companion object {
@[Provides Singleton]
fun providesExpenseDatabase(
@ApplicationContext context: Context
): ExpenseDatabase = ExpenseDatabase.build(context)

@[Provides Singleton]
fun providesExpenseDao(
expenseDatabase: ExpenseDatabase
): ExpenseDao = expenseDatabase.expenseDao
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.expenselogger.cache.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "expense")
public data class ExpenseEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Long,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "amount")
val amount: Double,
@ColumnInfo(name = "date")
val date: Long,
@ColumnInfo(name = "info")
val info: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.expenselogger.cache.repository

import com.example.expenselogger.cache.entity.ExpenseEntity

public interface ExpenseRepository {

public fun insertExpense(expenseEntity: ExpenseEntity)
public fun updateExpense(expenseEntity: ExpenseEntity)
public fun getExpense(id: Long): ExpenseEntity?
public fun getExpenses(): List<ExpenseEntity>
public fun deleteExpense(id: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.expenselogger.cache.repository

import com.example.expenselogger.cache.dao.ExpenseDao
import com.example.expenselogger.cache.entity.ExpenseEntity
import javax.inject.Inject

internal class ExpenseRepositoryImpl @Inject constructor(
private val expenseDao: ExpenseDao
) : ExpenseRepository {

override fun insertExpense(expenseEntity: ExpenseEntity) {
expenseDao.insertExpense(expenseEntity)
}

override fun updateExpense(expenseEntity: ExpenseEntity) {
expenseDao.updateExpense(expenseEntity)
}

override fun getExpense(id: Long): ExpenseEntity? {
return expenseDao.getExpense(id)
}

override fun getExpenses(): List<ExpenseEntity> {
return expenseDao.getExpenses()
}

override fun deleteExpense(id: Long) {
expenseDao.deleteExpense(id)
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
package com.example.expenselogger.lib_expense.domain.usecase

import com.example.expenselogger.lib_expense.domain.fakes.FakeExpenseContract
import com.example.expenselogger.lib_expense.domain.contract.ExpenseContract
import com.example.expenselogger.lib_expense.domain.model.DummyData
import com.example.expenselogger_test_utils.TestAsyncExecutor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.verify
import org.mockito.kotlin.mock

public class DeleteExpenseTest {

private val expenseContract = FakeExpenseContract()
private val deleteExpense = DeleteExpense(
expenseContract,
TestAsyncExecutor()
)
private lateinit var expenseContract: ExpenseContract
private lateinit var deleteExpense: DeleteExpense

@Before
public fun setup() {
expenseContract = mock()
deleteExpense = DeleteExpense(expenseContract, TestAsyncExecutor())
}

@Test
public fun `verify that deleteExpense usecase deletes an expense if it exists`(): Unit =
runBlockingTest {
val expense = DummyData.expense
expenseContract.saveExpense(expense)
deleteExpense.invoke(expense)
assertThat(expenseContract.getExpenses().size).isEqualTo(0)
}
public fun `verify that deleteExpense usecase deletes an expense`(): Unit = runBlockingTest {
val expense = DummyData.expense
deleteExpense.invoke(expense)
verify(expenseContract).deleteExpense(expense)
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
package com.example.expenselogger.lib_expense.domain.usecase

import com.example.expenselogger.lib_expense.domain.fakes.FakeExpenseContract
import com.example.expenselogger.lib_expense.domain.contract.ExpenseContract
import com.example.expenselogger.lib_expense.domain.model.DummyData
import com.example.expenselogger_test_utils.TestAsyncExecutor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

public class GetExpenseTest {

private val expenseContract = FakeExpenseContract()
private val getExpense = GetExpense(
expenseContract,
TestAsyncExecutor()
)

@Test
public fun `verify that getExpense usecase returns an expense`(): Unit =
runBlockingTest {
val expense = DummyData.expense
expenseContract.saveExpense(expense)
val expectedExpense = getExpense.invoke(expense.id)
assertThat(expectedExpense).isEqualTo(expense)
public fun `verify that getExpense usecase gets an expense`(): Unit = runBlockingTest {
val expense = DummyData.expense
val expenseContract = mock<ExpenseContract>().apply {
whenever(getExpense(0)).thenReturn(expense)
}
val getExpense = GetExpense(expenseContract, TestAsyncExecutor())
val expectedExpense = getExpense.invoke(0)
assertThat(expectedExpense).isEqualTo(expense)
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package com.example.expenselogger.lib_expense.domain.usecase

import com.example.expenselogger.lib_expense.domain.fakes.FakeExpenseContract
import com.example.expenselogger.lib_expense.domain.contract.ExpenseContract
import com.example.expenselogger.lib_expense.domain.model.DummyData
import com.example.expenselogger_test_utils.TestAsyncExecutor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

public class GetExpensesTest {

private val expenseContract = FakeExpenseContract()
private val getExpenses = GetExpenses(
expenseContract,
TestAsyncExecutor()
)

@Test
public fun `verify that expenses returns a list of expenses`(): Unit = runBlockingTest {
val expense = DummyData.expense
expenseContract.saveExpense(expense)
public fun `verify that getExpenses usecase gets a list of expenses`(): Unit = runBlockingTest {
val expenses = listOf(DummyData.expense)
val expenseContract = mock<ExpenseContract>().apply {
whenever(getExpenses()).thenReturn(expenses)
}
val getExpenses = GetExpenses(expenseContract, TestAsyncExecutor())
val expectedExpenses = getExpenses.invoke()
assertThat(expectedExpenses.size).isEqualTo(1)
assertThat(expectedExpenses).isEqualTo(expenses)
}
}

0 comments on commit b6bad85

Please sign in to comment.