chore: tests for :core:persistence (#592)

This commit is contained in:
Diego Beraldin 2024-03-13 07:46:58 +01:00 committed by GitHub
parent e72a8d04c4
commit 33bcd7cd33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1163 additions and 10 deletions

View File

@ -18,7 +18,7 @@ A Kotlin Multiplatform client for Lemmy (mainly Android).
<div align="center">
<div style="display: flex; flex-flow: row wrap; justify-content: center; align-items: center;">
<a href="https://github.com/ImranR98/Obtainium/releases"><img width="200" src="https://github.com/diegoberaldin/RaccoonForLemmy/assets/2738294/c341aeee-f779-41f0-b230-0c3081da3de5" /></a>
<a href="https://github.com/ImranR98/Obtainium/releases"><img width="200" src="https://github.com/diegoberaldin/RaccoonForLemmy/assets/2738294/c341aeee-f779-41f0-b230-0c3081da3de5" /></a>
<a href="https://play.google.com/store/apps/details?id=com.github.diegoberaldin.raccoonforlemmy.android"><img width="200" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" /></a>
<a href="https://apt.izzysoft.de/fdroid/index/apk/com.github.diegoberaldin.raccoonforlemmy.android"><img width="200" src="https://github.com/diegoberaldin/RaccoonForLemmy/assets/2738294/5d8c0e3d-552f-488e-b032-987aeadd4c4a" /></a>
</div>
@ -136,19 +136,19 @@ with a platform like Lemmy.
This involves a high level of discretion and personal taste, I know, but this project _is_ all
about experimenting and learning.
## Techical notes:
## Technical notes:
The project uses the following technologies:
- [Koin](https://github.com/InsertKoinIO/koin) for dependency injection
- [Voyager](https://github.com/adrielcafe/voyager) for screen navigation
- [Ktor](https://github.com/ktorio/ktor) and [Ktorfit](https://github.com/Foso/Ktorfit) for
networking
- [Ktor](https://github.com/ktorio/ktor) and [Ktorfit](https://github.com/Foso/Ktorfit) for networking
- [Lyricist](https://github.com/adrielcafe/lyricist) for l10n
- [Multiplatform settings](https://github.com/russhwolf/multiplatform-settings) for encrypted
preferences
- [SQLDelight](https://github.com/cashapp/sqldelight)
and [SQLCipher](https://github.com/sqlcipher/sqlcipher) for local persistence
- [Multiplatform settings](https://github.com/russhwolf/multiplatform-settings) for encrypted preferences
- [SQLDelight](https://github.com/cashapp/sqldelight) and [SQLCipher](https://github.com/sqlcipher/sqlcipher) for local
persistence
- [Multiplatform Markdown Renderer](https://github.com/mikepenz/multiplatform-markdown-renderer) for Markdown
rendering
More info about the technologies used in the project can be found in
the [CONTRIBUTING.md](https://github.com/diegoberaldin/RaccoonForLemmy/blob/master/CONTRIBUTING.md).

View File

@ -55,9 +55,12 @@ kotlin {
implementation(projects.core.utils)
}
}
val commonTest by getting {
val androidUnitTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
implementation(kotlin("test-junit"))
implementation(libs.mockk)
implementation(projects.core.testutils)
}
}
}

View File

@ -0,0 +1,193 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import app.cash.sqldelight.Query
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.AccountEntity
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.AccountsQueries
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DatabaseProvider
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.entities.AppDatabase
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
class DefaultAccountRepositoryTest {
@get:Rule
val dispatcherRule = DispatcherTestRule()
private val query = mockk<Query<AccountEntity>>()
private val queries = mockk<AccountsQueries>(relaxUnitFun = true) {
every { getAll() } returns query
every { getActive() } returns query
every { getBy(username = any(), instance = any()) } returns query
every { getActive() } returns query
}
private val provider = mockk<DatabaseProvider> {
every { getDatabase() } returns mockk<AppDatabase> {
every { accountsQueries } returns queries
}
}
private val sut = DefaultAccountRepository(
provider = provider,
)
@Test
fun givenNoAccounts_whenGetAll_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns listOf()
val res = sut.getAll()
assertTrue(res.isEmpty())
verify {
queries.getAll()
}
}
@Test
fun givenExitingAccounts_whenGetAll_thenResultIsAsExpected() = runTest {
val accounts = listOf(createFakeEntity())
every { query.executeAsList() } returns accounts
val res = sut.getAll()
assertTrue(res.size == 1)
verify {
queries.getAll()
}
}
@Test
fun givenNoActiveAccount_whenGetActive_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns null
val res = sut.getActive()
assertNull(res)
verify {
queries.getActive()
}
}
@Test
fun givenActiveAccount_whenGetActive_thenResultIsAsExpected() = runTest {
val account = createFakeEntity(active = true)
every { query.executeAsOneOrNull() } returns account
val res = sut.getActive()
assertNotNull(res)
verify {
queries.getActive()
}
}
@Test
fun givenNoAccount_whenGetBy_thenResultIsAsExpected() = runTest {
val username = "username"
val instance = "instance"
every { query.executeAsOneOrNull() } returns null
val res = sut.getBy(username, instance)
assertNull(res)
verify {
queries.getBy(username, instance)
}
}
@Test
fun givenAccount_whenGetBy_thenResultIsAsExpected() = runTest {
val username = "username"
val instance = "instance"
val account = createFakeEntity(active = true, username = username, instance = instance)
every { query.executeAsOneOrNull() } returns account
val res = sut.getBy(username, instance)
assertNotNull(res)
verify {
queries.getBy(username, instance)
}
}
@Test
fun whenCreate_thenIdIsReturned() = runTest {
val username = "username"
val instance = "instance"
val jwt = "jwt"
val account = AccountModel(username = username, instance = instance, jwt = jwt)
every { query.executeAsList() } returns listOf(createFakeEntity(id = 1, jwt = jwt))
val id = sut.createAccount(account)
assertEquals(1, id)
verify {
queries.create(username, instance, jwt, null)
}
}
@Test
fun whenSetActive_thenResultIsAsExpected() = runTest {
sut.setActive(id = 1, active = true)
verify {
queries.setActive(id = 1)
}
}
@Test
fun whenSetInactive_thenInteractionsAreAsExpected() = runTest {
sut.setActive(id = 1, active = false)
verify {
queries.setInactive(id = 1)
}
}
@Test
fun whenUpdate_thenInteractionsAreAsExpected() = runTest {
val jwt = "jwt"
val avatar = "avatar"
sut.update(1, avatar = avatar, jwt = jwt)
verify {
queries.update(jwt = jwt, avatar = avatar, id = 1)
}
}
@Test
fun whenDelete_thenInteractionsAreAsExpected() = runTest {
sut.delete(1)
verify {
queries.delete(1)
}
}
private fun createFakeEntity(
id: Long = 0,
active: Boolean = false,
username: String = "",
instance: String = "",
jwt: String? = null,
avatar: String? = null,
) = AccountEntity(
id = id,
active = if (active) 1 else 0,
username = username,
instance = instance,
jwt = jwt,
avatar = avatar,
)
}

View File

@ -0,0 +1,81 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertNull
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
class DefaultCommunitySortRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val keyStore = mockk<TemporaryKeyStore>(relaxUnitFun = true)
private val sut = DefaultCommunitySortRepository(keyStore = keyStore)
@Test
fun givenEmptyInitialState_whenSaveSort_thenValueIsStored() {
every { keyStore.get("communitySort", listOf()) } returns listOf()
sut.saveSort("!raccoonforlemmy@lemmy.world", 1)
verify {
keyStore.save("communitySort", listOf("!raccoonforlemmy@lemmy.world:1"))
}
}
@Test
fun givenCommunityAlreadyExisting_whenSaveSort_thenValueIsStored() {
every { keyStore.get("communitySort", listOf()) } returns listOf("!raccoonforlemmy@lemmy.world:0")
sut.saveSort("!raccoonforlemmy@lemmy.world", 1)
verify {
keyStore.save("communitySort", listOf("!raccoonforlemmy@lemmy.world:1"))
}
}
@Test
fun givenOtherCommunityAlreadyExisting_whenSaveSort_thenBothValuesAreStored() {
every { keyStore.get("communitySort", listOf()) } returns listOf("!test@lemmy.world:1")
sut.saveSort("!raccoonforlemmy@lemmy.world", 1)
verify {
keyStore.save("communitySort", listOf("!test@lemmy.world:1", "!raccoonforlemmy@lemmy.world:1"))
}
}
@Test
fun givenEmptyInitialState_whenGet_thenResultIsAsExpected() {
every { keyStore.get("communitySort", listOf()) } returns listOf()
val res = sut.getSort("!raccoonforlemmy@lemmy.world")
assertNull(res)
}
@Test
fun givenCommunityExists_whenGet_thenResultIsAsExpected() {
every { keyStore.get("communitySort", listOf()) } returns listOf("!raccoonforlemmy@lemmy.world:2")
val res = sut.getSort("!raccoonforlemmy@lemmy.world")
assertEquals(2, res)
}
@Test
fun givenCommunityDoesNotExist_whenGet_thenResultIsAsExpected() {
every { keyStore.get("communitySort", listOf()) } returns listOf("!test@lemmy.world:2")
val res = sut.getSort("!raccoonforlemmy@lemmy.world")
assertNull(res)
}
}

View File

@ -0,0 +1,196 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import app.cash.sqldelight.Query
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DatabaseProvider
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DraftEntity
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DraftsQueries
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.DraftModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.DraftType
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.entities.AppDatabase
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
class DefaultDraftRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val query = mockk<Query<DraftEntity>>()
private val queries = mockk<DraftsQueries>(relaxUnitFun = true) {
every { getBy(any()) } returns query
every { getAllBy(type = any(), account_id = any()) } returns query
}
private val provider = mockk<DatabaseProvider> {
every { getDatabase() } returns mockk<AppDatabase> {
every { draftsQueries } returns queries
}
}
private val sut = DefaultDraftRepository(provider)
@Test
fun givenNotExisting_whenGetBy_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns null
val res = sut.getBy(1)
assertNull(res)
verify {
queries.getBy(1)
}
}
@Test
fun givenExisting_whenGetBy_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFakeDraftEntity(1)
val res = sut.getBy(1)
assertNotNull(res)
assertEquals(1, res.id)
verify {
queries.getBy(1)
}
}
@Test
fun givenEmpty_whenGetAllByPosts_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns emptyList()
val res = sut.getAll(type = DraftType.Post, accountId = 1)
assertTrue(res.isEmpty())
verify {
queries.getAllBy(0, 1)
}
}
@Test
fun givenEmpty_whenGetAllByComments_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns emptyList()
val res = sut.getAll(type = DraftType.Comment, accountId = 1)
assertTrue(res.isEmpty())
verify {
queries.getAllBy(1, 1)
}
}
@Test
fun givenNotEmpty_whenGetAllByPosts_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns listOf(createFakeDraftEntity(id = 2))
val res = sut.getAll(type = DraftType.Post, accountId = 1)
assertTrue(res.isNotEmpty())
assertEquals(2, res.first().id)
verify {
queries.getAllBy(0, 1)
}
}
@Test
fun whenCreate_thenInteractionsAreAsExpected() = runTest {
val text = "text"
val model = DraftModel(
body = text,
type = DraftType.Post,
communityId = 3,
)
sut.create(model, 1)
verify {
queries.create(
type = 0,
body = text,
title = null,
url = null,
postId = null,
parentId = null,
communityId = 3,
languageId = null,
nsfw = null,
date = null,
info = null,
account_id = 1,
)
}
}
@Test
fun whenUpdate_thenInteractionsAreAsExpected() = runTest {
val text = "text"
val model = DraftModel(
id = 1,
body = text,
type = DraftType.Post,
communityId = 3,
)
sut.update(model)
verify {
queries.update(
id = 1,
body = text,
title = null,
url = null,
communityId = 3,
languageId = null,
nsfw = null,
date = null,
info = null,
)
}
}
@Test
fun whenDelete_thenInteractionsAreAsExpected() = runTest {
sut.delete(1)
verify {
queries.delete(1)
}
}
private fun createFakeDraftEntity(
id: Long = 0,
accountId: Long = 0,
type: DraftType = DraftType.Post,
title: String? = null,
body: String = "",
postId: Long? = null,
parentId: Long? = null,
communityId: Long? = null,
languageId: Long? = null,
url: String? = null,
nsfw: Boolean = false,
info: String? = null,
date: Long? = null,
) = DraftEntity(
id = id,
title = title,
body = body,
type = if (type == DraftType.Post) 0 else 1,
postId = postId,
parentId = parentId,
account_id = accountId,
communityId = communityId,
languageId = languageId,
url = url,
nsfw = if (nsfw) 1 else 0,
info = info,
date = date,
)
}

View File

@ -0,0 +1,124 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import app.cash.sqldelight.Query
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DatabaseProvider
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.FavoriteCommunityEntity
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.FavoritecommunitiesQueries
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.FavoriteCommunityModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.entities.AppDatabase
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertNull
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class DefaultFavoriteCommunityRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val query = mockk<Query<FavoriteCommunityEntity>>()
private val queries = mockk<FavoritecommunitiesQueries>(relaxUnitFun = true) {
every { getAll(any()) } returns query
every { getBy(communityId = any(), account_id = any()) } returns query
}
private val provider = mockk<DatabaseProvider> {
every { getDatabase() } returns mockk<AppDatabase> {
every { favoritecommunitiesQueries } returns queries
}
}
private val sut = DefaultFavoriteCommunityRepository(provider)
@Test
fun givenEmpty_whenGetAll_thenResultsAreAsExpected() = runTest {
every { query.executeAsList() } returns emptyList()
val res = sut.getAll(1)
assertTrue(res.isEmpty())
verify {
queries.getAll(1)
}
}
@Test
fun givenNotEmpty_whenGetAll_thenResultsAreAsExpected() = runTest {
every { query.executeAsList() } returns listOf(createFakeFavoriteCommunityEntity(id = 1, accountId = 1))
val res = sut.getAll(1)
assertTrue(res.isNotEmpty())
assertEquals(1, res.first().id)
verify {
queries.getAll(1)
}
}
@Test
fun givenEmpty_whenGetBy_thenResultsAreAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns null
val res = sut.getBy(accountId = 1, communityId = 2)
assertNull(res)
verify {
queries.getBy(communityId = 2, account_id = 1)
}
}
@Test
fun givenNotEmpty_whenGetBy_thenResultsAreAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFakeFavoriteCommunityEntity(id = 2, accountId = 1)
val res = sut.getBy(accountId = 1, communityId = 3)
assertNotNull(res)
assertEquals(2, res.id)
verify {
queries.getBy(communityId = 3, account_id = 1)
}
}
@Test
fun whenCreate_thenInteractionsAreAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFakeFavoriteCommunityEntity(id = 2, accountId = 1)
val model = FavoriteCommunityModel(communityId = 3)
val res = sut.create(model = model, accountId = 1)
assertEquals(2, res)
verify {
queries.create(communityId = 3, account_id = 1)
}
}
@Test
fun whenDelete_thenInteractionsAreAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFakeFavoriteCommunityEntity(id = 2, accountId = 1)
val model = FavoriteCommunityModel(communityId = 3)
sut.delete(accountId = 1, model = model)
verify {
queries.delete(2)
}
}
private fun createFakeFavoriteCommunityEntity(
id: Long = 0,
communityId: Long = 0,
accountId: Long = 0,
) = FavoriteCommunityEntity(
id = id,
communityId = communityId,
account_id = accountId,
)
}

View File

@ -0,0 +1,86 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DefaultInstanceSelectionRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val keyStore = mockk<TemporaryKeyStore>(relaxUnitFun = true) {
every { containsKey("customInstances") } returns true
}
private val sut = DefaultInstanceSelectionRepository(keyStore)
@Test
fun givenEmpty_whenGetAll_thenResultIsAsExpected() = runTest {
every { keyStore.get("customInstances", any<List<String>>()) } returns listOf()
val res = sut.getAll()
assertTrue(res.isEmpty())
}
@Test
fun givenNotEmpty_whenGetAll_thenResultIsAsExpected() = runTest {
every { keyStore.get("customInstances", any<List<String>>()) } returns listOf("lemmy.world")
val res = sut.getAll()
assertTrue(res.isNotEmpty())
assertEquals("lemmy.world", res.first())
}
@Test
fun whenUpdateAll_thenResultIsAsExpected() = runTest {
val values = listOf("lemmy.world", "lemmy.ml")
sut.updateAll(values)
verify {
keyStore.save("customInstances", values)
}
}
@Test
fun givenNotAlreadyPresent_whenAdd_thenResultIsAsExpected() = runTest {
every { keyStore.get("customInstances", any<List<String>>()) } returns listOf("lemmy.world")
sut.add("lemmy.ml")
verify {
keyStore.save("customInstances", listOf("lemmy.ml", "lemmy.world"))
}
}
@Test
fun givenAlreadyPresent_whenAdd_thenResultIsAsExpected() = runTest {
every { keyStore.get("customInstances", any<List<String>>()) } returns listOf("lemmy.world")
sut.add("lemmy.world")
verify {
keyStore.save("customInstances", listOf("lemmy.world"))
}
}
@Test
fun whenRemove_thenResultIsAsExpected() = runTest {
every { keyStore.get("customInstances", any<List<String>>()) } returns listOf("lemmy.world", "lemmy.ml")
sut.remove("lemmy.ml")
verify {
keyStore.save("customInstances", listOf("lemmy.world"))
}
}
}

View File

@ -0,0 +1,150 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import app.cash.sqldelight.Query
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DatabaseProvider
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.MultiCommunityEntity
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.MulticommunitiesQueries
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.MultiCommunityModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.entities.AppDatabase
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
class DefaultMultiCommunityRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val query = mockk<Query<MultiCommunityEntity>>()
private val queries = mockk<MulticommunitiesQueries>(relaxUnitFun = true) {
every { getAll(any()) } returns query
every { getBy(name = any(), account_id = any()) } returns query
every { getById(any()) } returns query
}
private val provider = mockk<DatabaseProvider> {
every { getDatabase() } returns mockk<AppDatabase> {
every { multicommunitiesQueries } returns queries
}
}
private val sut = DefaultMultiCommunityRepository(provider)
@Test
fun givenEmpty_whenGetAll_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns listOf()
val res = sut.getAll(1)
assertTrue(res.isEmpty())
verify {
queries.getAll(1)
}
}
@Test
fun givenNotEmpty_whenGetAll_thenResultIsAsExpected() = runTest {
every { query.executeAsList() } returns listOf(createFakeMultiCommunityEntity(id = 2))
val res = sut.getAll(1)
assertTrue(res.isNotEmpty())
assertEquals(2, res.first().id)
verify {
queries.getAll(1)
}
}
@Test
fun givenEmpty_whenGetById_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns null
val res = sut.getById(1)
assertNull(res)
verify {
queries.getById(1)
}
}
@Test
fun givenNotEmpty_whenGetById_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFakeMultiCommunityEntity(id = 2)
val res = sut.getById(2)
assertNotNull(res)
assertEquals(2, res.id)
verify {
queries.getById(2)
}
}
@Test
fun whenCreate_thenInteractionsAreAsExpected() = runTest {
val model = MultiCommunityModel(name = "test", communityIds = listOf(1, 2, 3))
every { query.executeAsOneOrNull() } returns createFakeMultiCommunityEntity(id = 2)
val res = sut.create(model = model, accountId = 1)
assertEquals(2, res)
verify {
queries.create(
name = "test",
icon = null,
communityIds = "1,2,3",
account_id = 1
)
}
}
@Test
fun whenUpdate_thenInteractionsAreAsExpected() = runTest {
val model = MultiCommunityModel(name = "test", id = 2, icon = "fake-icon", communityIds = listOf(1, 2, 3))
every { query.executeAsOneOrNull() } returns createFakeMultiCommunityEntity(id = 2)
sut.update(model = model)
verify {
queries.update(
id = 2,
name = "test",
icon = "fake-icon",
communityIds = "1,2,3",
)
}
}
@Test
fun whenDelete_thenInteractionsAreAsExpected() = runTest {
val model = MultiCommunityModel(name = "test", id = 2)
every { query.executeAsOneOrNull() } returns createFakeMultiCommunityEntity(id = 2)
sut.delete(model)
verify {
queries.delete(id = 2)
}
}
private fun createFakeMultiCommunityEntity(
id: Long,
name: String = "",
icon: String? = null,
communityIds: String = "",
accountId: Long? = null,
) = MultiCommunityEntity(
id = id,
name = name,
icon = icon,
communityIds = communityIds,
account_id = accountId,
)
}

View File

@ -0,0 +1,320 @@
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository
import app.cash.sqldelight.Query
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.DatabaseProvider
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.GetBy
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.SettingsQueries
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.SettingsModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.toInt
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.entities.AppDatabase
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.time.DurationUnit
class DefaultSettingsRepositoryTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val query = mockk<Query<GetBy>>()
private val queries = mockk<SettingsQueries>(relaxUnitFun = true) {
every { getBy(any()) } returns query
}
private val provider = mockk<DatabaseProvider> {
every { getDatabase() } returns mockk<AppDatabase> {
every { settingsQueries } returns queries
}
}
private val keyStore = mockk<TemporaryKeyStore>(relaxUnitFun = true)
private val sut = DefaultSettingsRepository(
provider = provider,
keyStore = keyStore,
)
@Test
fun givenAccount_whenGetSettings_thenResultIsAsExpected() = runTest {
every { query.executeAsOneOrNull() } returns createFake(id = 2)
val res = sut.getSettings(1)
assertEquals(2, res.id)
verify {
queries.getBy(account_id = 1)
}
}
@Test
fun givenNoAccount_whenGetSettings_thenResultIsAsExpected() = runTest {
every { keyStore[any(), any<Long>()] } returns 0
every { keyStore[any(), any<Int>()] } returns 0
every { keyStore[any(), any<Float>()] } returns 1f
every { keyStore[any(), any<String>()] } returns ""
every { keyStore[any(), any<Boolean>()] } returns false
every { keyStore.containsKey(any()) } returns true
val res = sut.getSettings(null)
assertNotNull(res)
verify {
queries wasNot Called
}
}
@Test
fun whenChangeCurrentSettings_thenValueIsUpdated() = runTest {
val model = SettingsModel(defaultListingType = 1)
sut.changeCurrentSettings(model)
val value = sut.currentSettings.value
assertEquals(model, value)
}
@Test
fun whenCreateSettings_thenResultIsAsExpected() = runTest {
val model = SettingsModel()
sut.createSettings(model, 1)
verify {
queries.create(
theme = model.theme?.toLong(),
uiFontScale = model.uiFontScale.toDouble(),
uiFontFamily = model.uiFontFamily.toLong(),
titleFontScale = model.contentFontScale.title.toDouble(),
contentFontScale = model.contentFontScale.body.toDouble(),
commentFontScale = model.contentFontScale.comment.toDouble(),
ancillaryFontScale = model.contentFontScale.ancillary.toDouble(),
locale = model.locale,
defaultListingType = model.defaultListingType.toLong(),
defaultPostSortType = model.defaultPostSortType.toLong(),
defaultCommentSortType = model.defaultCommentSortType.toLong(),
defaultInboxType = model.defaultInboxType.toLong(),
includeNsfw = if (model.includeNsfw) 1 else 0,
blurNsfw = if (model.blurNsfw) 1 else 0,
navigationTitlesVisible = if (model.navigationTitlesVisible) 1 else 0,
dynamicColors = if (model.dynamicColors) 1 else 0,
openUrlsInExternalBrowser = if (model.openUrlsInExternalBrowser) 1 else 0,
enableSwipeActions = if (model.enableSwipeActions) 1 else 0,
enableDoubleTapAction = if (model.enableDoubleTapAction) 1 else 0,
customSeedColor = model.customSeedColor?.toLong(),
postLayout = model.postLayout.toLong(),
separateUpAndDownVotes = if (model.voteFormat == VoteFormat.Separated) 1 else 0,
autoLoadImages = if (model.autoLoadImages) 1 else 0,
autoExpandComments = if (model.autoExpandComments) 1 else 0,
fullHeightImages = if (model.fullHeightImages) 1 else 0,
upvoteColor = model.upVoteColor?.toLong(),
downvoteColor = model.downVoteColor?.toLong(),
hideNavigationBarWhileScrolling = if (model.hideNavigationBarWhileScrolling) 1 else 0,
zombieModeInterval = model.zombieModeInterval.toLong(DurationUnit.MILLISECONDS),
zombieModeScrollAmount = model.zombieModeScrollAmount.toDouble(),
markAsReadWhileScrolling = if (model.markAsReadWhileScrolling) 1 else 0,
commentBarTheme = model.commentBarTheme.toLong(),
replyColor = model.replyColor?.toLong(),
saveColor = model.saveColor?.toLong(),
searchPostTitleOnly = if (model.searchPostTitleOnly) 1 else 0,
contentFontFamily = model.contentFontFamily.toLong(),
edgeToEdge = if (model.edgeToEdge) 1 else 0,
postBodyMaxLines = model.postBodyMaxLines?.toLong(),
infiniteScrollEnabled = if (model.infiniteScrollEnabled) 1 else 0,
actionsOnSwipeToStartPosts = model.actionsOnSwipeToStartPosts.serialized(),
actionsOnSwipeToEndPosts = model.actionsOnSwipeToEndPosts.serialized(),
actionsOnSwipeToStartComments = model.actionsOnSwipeToStartComments.serialized(),
actionsOnSwipeToEndComments = model.actionsOnSwipeToEndComments.serialized(),
actionsOnSwipeToStartInbox = model.actionsOnSwipeToStartInbox.serialized(),
actionsOnSwipeToEndInbox = model.actionsOnSwipeToEndInbox.serialized(),
opaqueSystemBars = if (model.opaqueSystemBars) 1 else 0,
showScores = if (model.showScores) 1 else 0,
preferUserNicknames = if (model.preferUserNicknames) 1 else 0,
commentBarThickness = model.commentBarThickness.toLong(),
imageSourcePath = if (model.imageSourcePath) 1 else 0,
defaultExploreType = model.defaultExploreType.toLong(),
account_id = 1,
)
}
}
@Test
fun whenUpdate_thenResultIsAsExpected() = runTest {
val model = SettingsModel(defaultListingType = 1)
sut.updateSettings(model, 1)
verify {
queries.update(
theme = model.theme?.toLong(),
uiFontScale = model.uiFontScale.toDouble(),
uiFontFamily = model.uiFontFamily.toLong(),
titleFontScale = model.contentFontScale.title.toDouble(),
contentFontScale = model.contentFontScale.body.toDouble(),
commentFontScale = model.contentFontScale.comment.toDouble(),
ancillaryFontScale = model.contentFontScale.ancillary.toDouble(),
locale = model.locale,
defaultListingType = model.defaultListingType.toLong(),
defaultPostSortType = model.defaultPostSortType.toLong(),
defaultCommentSortType = model.defaultCommentSortType.toLong(),
defaultInboxType = model.defaultInboxType.toLong(),
includeNsfw = if (model.includeNsfw) 1 else 0,
blurNsfw = if (model.blurNsfw) 1 else 0,
navigationTitlesVisible = if (model.navigationTitlesVisible) 1 else 0,
dynamicColors = if (model.dynamicColors) 1 else 0,
openUrlsInExternalBrowser = if (model.openUrlsInExternalBrowser) 1 else 0,
enableSwipeActions = if (model.enableSwipeActions) 1 else 0,
enableDoubleTapAction = if (model.enableDoubleTapAction) 1 else 0,
customSeedColor = model.customSeedColor?.toLong(),
postLayout = model.postLayout.toLong(),
separateUpAndDownVotes = if (model.voteFormat == VoteFormat.Separated) 1 else 0,
autoLoadImages = if (model.autoLoadImages) 1 else 0,
autoExpandComments = if (model.autoExpandComments) 1 else 0,
fullHeightImages = if (model.fullHeightImages) 1 else 0,
upvoteColor = model.upVoteColor?.toLong(),
downvoteColor = model.downVoteColor?.toLong(),
hideNavigationBarWhileScrolling = if (model.hideNavigationBarWhileScrolling) 1 else 0,
zombieModeInterval = model.zombieModeInterval.toLong(DurationUnit.MILLISECONDS),
zombieModeScrollAmount = model.zombieModeScrollAmount.toDouble(),
markAsReadWhileScrolling = if (model.markAsReadWhileScrolling) 1 else 0,
commentBarTheme = model.commentBarTheme.toLong(),
replyColor = model.replyColor?.toLong(),
saveColor = model.saveColor?.toLong(),
searchPostTitleOnly = if (model.searchPostTitleOnly) 1 else 0,
contentFontFamily = model.contentFontFamily.toLong(),
edgeToEdge = if (model.edgeToEdge) 1 else 0,
postBodyMaxLines = model.postBodyMaxLines?.toLong(),
infiniteScrollEnabled = if (model.infiniteScrollEnabled) 1 else 0,
actionsOnSwipeToStartPosts = model.actionsOnSwipeToStartPosts.serialized(),
actionsOnSwipeToEndPosts = model.actionsOnSwipeToEndPosts.serialized(),
actionsOnSwipeToStartComments = model.actionsOnSwipeToStartComments.serialized(),
actionsOnSwipeToEndComments = model.actionsOnSwipeToEndComments.serialized(),
actionsOnSwipeToStartInbox = model.actionsOnSwipeToStartInbox.serialized(),
actionsOnSwipeToEndInbox = model.actionsOnSwipeToEndInbox.serialized(),
opaqueSystemBars = if (model.opaqueSystemBars) 1 else 0,
showScores = if (model.showScores) 1 else 0,
preferUserNicknames = if (model.preferUserNicknames) 1 else 0,
commentBarThickness = model.commentBarThickness.toLong(),
imageSourcePath = if (model.imageSourcePath) 1 else 0,
defaultExploreType = model.defaultExploreType.toLong(),
account_id = 1,
)
}
}
private fun List<ActionOnSwipe>.serialized(): String = map { it.toInt() }.joinToString(",")
private fun createFake(
id: Long = 0,
theme: Long? = null,
uiFontFamily: Long = 0,
uiFontScale: Double = 1.0,
contentFontScale: Double = 1.0,
ancillaryFontScale: Double = 1.0,
commentFontScale: Double = 1.0,
titleFontScale: Double = 1.0,
contentFontFamily: Long = 0,
locale: String? = null,
defaultListingType: Long = 2,
defaultPostSortType: Long = 1,
defaultInboxType: Long = 0,
defaultCommentSortType: Long = 3,
defaultExploreType: Long = 2,
includeNsfw: Boolean = false,
blurNsfw: Boolean = true,
navigationTitlesVisible: Boolean = true,
dynamicColors: Boolean = false,
openUrlsInExternalBrowser: Boolean = true,
enableSwipeActions: Boolean = true,
enableDoubleTapAction: Boolean = false,
customSeedColor: Long? = null,
upVoteColor: Long? = null,
downVoteColor: Long? = null,
postLayout: Long = 0,
fullHeightImages: Boolean = true,
autoLoadImages: Boolean = true,
autoExpandComments: Boolean = true,
hideNavigationBarWhileScrolling: Boolean = true,
zombieModeInterval: Long = 1,
zombieModeScrollAmount: Double = 55.0,
markAsReadWhileScrolling: Boolean = false,
commentBarTheme: Long = 0,
replyColor: Long? = null,
saveColor: Long? = null,
searchPostTitleOnly: Boolean = false,
edgeToEdge: Boolean = true,
postBodyMaxLines: Long? = null,
infiniteScrollEnabled: Boolean = true,
actionsOnSwipeToStartPosts: String = "",
actionsOnSwipeToEndPosts: String = "",
actionsOnSwipeToStartComments: String = "",
actionsOnSwipeToEndComments: String = "",
actionsOnSwipeToStartInbox: String = "",
actionsOnSwipeToEndInbox: String = "",
opaqueSystemBars: Boolean = false,
showScores: Boolean = true,
preferUserNicknames: Boolean = true,
commentBarThickness: Long = 1,
imageSourcePath: Boolean = false,
separateUpAndDownVotes: Boolean = false,
) = GetBy(
id = id,
theme = theme,
uiFontFamily = uiFontFamily,
uiFontScale = uiFontScale,
contentFontScale = contentFontScale,
contentFontFamily = contentFontFamily,
locale = locale,
defaultListingType = defaultListingType,
defaultPostSortType = defaultPostSortType,
defaultInboxType = defaultInboxType,
defaultCommentSortType = defaultCommentSortType,
defaultExploreType = defaultExploreType,
includeNsfw = if (includeNsfw) 1 else 0,
blurNsfw = if (blurNsfw) 1 else 0,
navigationTitlesVisible = if (navigationTitlesVisible) 1 else 0,
dynamicColors = if (dynamicColors) 1 else 0,
openUrlsInExternalBrowser = if (openUrlsInExternalBrowser) 1 else 0,
enableSwipeActions = if (enableSwipeActions) 1 else 0,
enableDoubleTapAction = if (enableDoubleTapAction) 1 else 0,
customSeedColor = customSeedColor,
upvoteColor = upVoteColor,
downvoteColor = downVoteColor,
postLayout = postLayout,
fullHeightImages = if (fullHeightImages) 1 else 0,
autoLoadImages = if (autoLoadImages) 1 else 0,
autoExpandComments = if (autoExpandComments) 1 else 0,
hideNavigationBarWhileScrolling = if (hideNavigationBarWhileScrolling) 1 else 0,
zombieModeInterval = zombieModeInterval,
zombieModeScrollAmount = zombieModeScrollAmount,
markAsReadWhileScrolling = if (markAsReadWhileScrolling) 1 else 0,
commentBarTheme = commentBarTheme,
replyColor = replyColor,
saveColor = saveColor,
searchPostTitleOnly = if (searchPostTitleOnly) 1 else 0,
edgeToEdge = if (edgeToEdge) 1 else 0,
postBodyMaxLines = postBodyMaxLines,
infiniteScrollEnabled = if (infiniteScrollEnabled) 1 else 0,
actionsOnSwipeToStartPosts = actionsOnSwipeToStartPosts,
actionsOnSwipeToEndPosts = actionsOnSwipeToEndPosts,
actionsOnSwipeToStartComments = actionsOnSwipeToStartComments,
actionsOnSwipeToEndComments = actionsOnSwipeToEndComments,
actionsOnSwipeToStartInbox = actionsOnSwipeToStartInbox,
actionsOnSwipeToEndInbox = actionsOnSwipeToEndInbox,
opaqueSystemBars = if (opaqueSystemBars) 1 else 0,
showScores = if (showScores) 1 else 0,
preferUserNicknames = if (preferUserNicknames) 1 else 0,
commentBarThickness = commentBarThickness,
imageSourcePath = if (imageSourcePath) 1 else 0,
ancillaryFontScale = ancillaryFontScale,
commentFontScale = commentFontScale,
titleFontScale = titleFontScale,
separateUpAndDownVotes = if (separateUpAndDownVotes) 1 else 0,
)
}