-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor data and domain code and tests
- Loading branch information
1 parent
a9b7e9d
commit 8c09f8e
Showing
12 changed files
with
309 additions
and
235 deletions.
There are no files selected for viewing
File renamed without changes.
72 changes: 72 additions & 0 deletions
72
libraries/data/src/main/java/com/smarttoolfactory/data/model/local/SavedProperty.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.smarttoolfactory.data.model.local | ||
|
||
import androidx.room.Embedded | ||
import androidx.room.Entity | ||
import androidx.room.ForeignKey | ||
import androidx.room.Index | ||
import androidx.room.PrimaryKey | ||
import androidx.room.Relation | ||
|
||
@Entity(tableName = "post") | ||
data class PostEntity( | ||
@PrimaryKey | ||
val id: Int, | ||
val userId: Int, | ||
val title: String, | ||
val body: String | ||
) | ||
|
||
/** | ||
* * Data class that contains [PostStatus] data. | ||
* [PostEntity.id] is in [PostEntity] class, [PostStatus.postId] is in [PostStatus] | ||
* both points to same value. | ||
* | ||
* * [PostStatus.id] is auto generated by insertion to table. | ||
* | ||
* * Index let's this table to be sorted by postId which makes all | ||
* rows with same postId to be found faster. | ||
* | ||
* * Status of the [PostEntity] with [PostEntity.id] or [PostStatus.postId] belong to current user | ||
* logged in with [PostStatus.userAccountId] or -1 if any user hasn't logged in | ||
*/ | ||
@Entity( | ||
tableName = "post_status", | ||
indices = [Index(value = ["userAccountId", "postId"])], | ||
foreignKeys = [ | ||
ForeignKey( | ||
entity = PostEntity::class, | ||
parentColumns = ["id"], | ||
childColumns = ["postId"], | ||
onDelete = ForeignKey.NO_ACTION | ||
) | ||
] | ||
) | ||
data class PostStatus( | ||
@PrimaryKey(autoGenerate = true) | ||
val id: Int = 0, | ||
val userAccountId: Int = -1, | ||
val postId: Int, | ||
val displayCount: Int = 0, | ||
val isFavorite: Boolean = false | ||
) | ||
|
||
/** | ||
* @Embedded tag is for having nested entities that are contained inside another entity. For | ||
* instance Songs are embedded inside an Album. | ||
* | ||
* @Relation is for having relation between entities based on pairing one or more properties, | ||
* such as ids. For instance Person with id, having Pets that has userId that is exactly same | ||
* with each other. | ||
* | ||
* * ParentColumn name from [PostEntity] class is matched with entityColumn | ||
* from [PostStatus.postId] | ||
*/ | ||
data class PostAndStatus( | ||
|
||
@Embedded | ||
val postEntity: PostEntity, | ||
|
||
// 🔥 'id' comes from Post, 'postId' comes from Post. Both are the same ids | ||
@Relation(parentColumn = "id", entityColumn = "postId") | ||
var postStatus: PostStatus? = null | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
...ata/src/test/java/com/smarttoolfactory/data/repository/PagedPropertyRepositoryImplTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package com.smarttoolfactory.data.repository | ||
|
||
import com.google.common.truth.Truth | ||
import com.smarttoolfactory.data.constant.ORDER_BY_NONE | ||
import com.smarttoolfactory.data.mapper.MapperFactory | ||
import com.smarttoolfactory.data.mapper.PropertyDTOtoPagedEntityListMapper | ||
import com.smarttoolfactory.data.model.local.PagedPropertyEntity | ||
import com.smarttoolfactory.data.model.remote.PropertyResponse | ||
import com.smarttoolfactory.data.source.LocalPagedPropertyDataSource | ||
import com.smarttoolfactory.data.source.RemotePropertyDataSourceCoroutines | ||
import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH | ||
import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH_PAGE_1 | ||
import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH_PAGE_2 | ||
import com.smarttoolfactory.test_utils.util.convertToObjectFromJson | ||
import com.smarttoolfactory.test_utils.util.getResourceAsText | ||
import io.mockk.clearMocks | ||
import io.mockk.coEvery | ||
import io.mockk.coVerify | ||
import io.mockk.coVerifyOrder | ||
import io.mockk.every | ||
import io.mockk.just | ||
import io.mockk.mockk | ||
import io.mockk.runs | ||
import io.mockk.slot | ||
import kotlinx.coroutines.test.runBlockingTest | ||
import org.junit.jupiter.api.AfterEach | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
|
||
internal class PagedPropertyRepositoryImplTest { | ||
|
||
private lateinit var repository: PagedPropertyRepository | ||
|
||
private val localDataSource: LocalPagedPropertyDataSource = mockk() | ||
private val remoteDataSource: RemotePropertyDataSourceCoroutines = mockk() | ||
private val mapper: PropertyDTOtoPagedEntityListMapper = mockk() | ||
|
||
companion object { | ||
|
||
private val propertyResponse = convertToObjectFromJson<PropertyResponse>( | ||
getResourceAsText(RESPONSE_JSON_PATH) | ||
)!! | ||
|
||
private val propertyDTOList = propertyResponse.res | ||
|
||
private val entityList = | ||
MapperFactory.createListMapper<PropertyDTOtoPagedEntityListMapper>() | ||
.map(propertyDTOList) | ||
|
||
// FIXME Cannot convert from Json to Entity even with wrapper, check out Moshi or Jackson | ||
|
||
private val propertyResponsePage1 = convertToObjectFromJson<PropertyResponse>( | ||
getResourceAsText(RESPONSE_JSON_PATH_PAGE_1) | ||
)!! | ||
|
||
private val propertyResponsePage2 = convertToObjectFromJson<PropertyResponse>( | ||
getResourceAsText(RESPONSE_JSON_PATH_PAGE_2) | ||
)!! | ||
|
||
private val propertyDTOListPage1 = propertyResponsePage1.res | ||
private val propertyDTOListPage2 = propertyResponsePage2.res | ||
|
||
private val entityListPage1 = | ||
MapperFactory.createListMapper<PropertyDTOtoPagedEntityListMapper>() | ||
.map( | ||
convertToObjectFromJson<PropertyResponse>( | ||
getResourceAsText(RESPONSE_JSON_PATH_PAGE_1) | ||
)!!.res | ||
) | ||
|
||
private val entityListPage2 = | ||
MapperFactory.createListMapper<PropertyDTOtoPagedEntityListMapper>() | ||
.map( | ||
convertToObjectFromJson<PropertyResponse>( | ||
getResourceAsText(RESPONSE_JSON_PATH_PAGE_1) | ||
)!!.res | ||
) | ||
} | ||
|
||
@Test | ||
fun `given page 2 returned data returned should have current page number 2 with Pagination`() = | ||
runBlockingTest { | ||
|
||
// GIVEN | ||
val slot = slot<String>() | ||
|
||
// Page 1 Pagination | ||
val page1DTO = propertyDTOListPage1 | ||
val page1Data = entityListPage1 | ||
|
||
coEvery { | ||
remoteDataSource.getPropertyDTOsWithPagination(1) | ||
} returns page1DTO | ||
|
||
every { mapper.map(page1DTO) } returns page1Data | ||
coEvery { localDataSource.saveOrderKey(capture(slot)) } just runs | ||
|
||
// Page 2 Pagination | ||
val page2DTO = propertyDTOListPage2 | ||
val page2Data = entityListPage2 | ||
|
||
coEvery { | ||
remoteDataSource.getPropertyDTOsWithPagination(2) | ||
} returns page2DTO | ||
|
||
every { mapper.map(page2DTO) } returns page2Data | ||
|
||
// WHEN | ||
val page1 = repository.getCurrentPageNumber() | ||
val expected1 = repository.fetchEntitiesFromRemoteByPage() | ||
|
||
val page2 = repository.getCurrentPageNumber() | ||
val expected2 = repository.fetchEntitiesFromRemoteByPage() | ||
|
||
// THEN | ||
Truth.assertThat(expected1).isEqualTo(page1Data) | ||
Truth.assertThat(page1).isEqualTo(1) | ||
|
||
Truth.assertThat(expected2).isEqualTo(page2Data) | ||
Truth.assertThat(page2).isEqualTo(2) | ||
|
||
coVerifyOrder { | ||
remoteDataSource.getPropertyDTOsWithPagination(1) | ||
localDataSource.saveOrderKey(ORDER_BY_NONE) | ||
mapper.map(page1DTO) | ||
remoteDataSource.getPropertyDTOsWithPagination(2) | ||
localDataSource.saveOrderKey(ORDER_BY_NONE) | ||
mapper.map(page2DTO) | ||
} | ||
} | ||
|
||
@Test | ||
fun `given DB is empty should return an empty list`() = runBlockingTest { | ||
|
||
// GIVEN | ||
val expected = listOf<PagedPropertyEntity>() | ||
coEvery { localDataSource.getPropertyEntities() } returns expected | ||
|
||
// WHEN | ||
val actual = repository.getPropertyEntitiesFromLocal() | ||
|
||
// THEN | ||
Truth.assertThat(actual).isEmpty() | ||
coVerify(exactly = 1) { localDataSource.getPropertyEntities() } | ||
} | ||
|
||
@Test | ||
fun `given DB is populated should return data list`() = runBlockingTest { | ||
|
||
// GIVEN | ||
coEvery { localDataSource.getPropertyEntities() } returns entityList | ||
|
||
// WHEN | ||
val actual = repository.getPropertyEntitiesFromLocal() | ||
|
||
// THEN | ||
Truth.assertThat(actual) | ||
.containsExactlyElementsIn(entityList) | ||
coVerify(exactly = 1) { localDataSource.getPropertyEntities() } | ||
} | ||
|
||
@Test | ||
fun `given entities, should save entities`() = runBlockingTest { | ||
|
||
// GIVEN | ||
val idList = entityList.map { | ||
it.id.toLong() | ||
} | ||
|
||
coEvery { | ||
localDataSource.saveEntities(entityList) | ||
} returns idList | ||
|
||
// WHEN | ||
repository.savePropertyEntities(entityList) | ||
|
||
// THEN | ||
coVerify(exactly = 1) { localDataSource.saveEntities(entityList) } | ||
} | ||
|
||
@Test | ||
fun `given no error should delete entities`() = runBlockingTest { | ||
|
||
// GIVEN | ||
coEvery { localDataSource.deletePropertyEntities() } just runs | ||
|
||
// WHEN | ||
repository.deletePropertyEntities() | ||
|
||
// THEN | ||
coVerify(exactly = 1) { | ||
localDataSource.deletePropertyEntities() | ||
} | ||
} | ||
|
||
@BeforeEach | ||
fun setUp() { | ||
repository = PagedPropertyRepositoryImpl(localDataSource, remoteDataSource, mapper) | ||
} | ||
|
||
@AfterEach | ||
fun tearDown() { | ||
clearMocks(localDataSource, remoteDataSource, mapper) | ||
} | ||
} |
Oops, something went wrong.