Skip to content

Commit

Permalink
Fixes before release (#3114)
Browse files Browse the repository at this point in the history
  • Loading branch information
ILIYANGERMANOV committed Apr 10, 2024
1 parent 78a6d56 commit be76546
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 20 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ paparazzi = "1.3.3"
# Android
min-sdk = "28"
compile-sdk = "34"
version-name = "4.6.2"
version-code = "162"
version-name = "4.6.3"
version-code = "163"
jvm-target = "17"


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ivy.home.customerjourney

import com.ivy.base.legacy.SharedPrefs
import com.ivy.base.legacy.stringRes
import com.ivy.base.model.TransactionType
import com.ivy.data.db.dao.read.PlannedPaymentRuleDao
Expand All @@ -15,7 +16,6 @@ import com.ivy.design.l0_system.Red
import com.ivy.design.l0_system.Red3
import com.ivy.legacy.Constants
import com.ivy.legacy.IvyWalletCtx
import com.ivy.base.legacy.SharedPrefs
import com.ivy.legacy.data.model.MainTab
import com.ivy.navigation.EditPlannedScreen
import com.ivy.navigation.PieChartStatisticScreen
Expand Down Expand Up @@ -65,6 +65,7 @@ class CustomerJourneyCardsProvider @Inject constructor(
rateUsCard_2(),
joinTelegram2(),
ivyWalletIsOpenSource(),
bugsApology(),
)

fun adjustBalanceCard() = CustomerJourneyCardModel(
Expand Down Expand Up @@ -231,5 +232,27 @@ class CustomerJourneyCardsProvider @Inject constructor(
ivyActivity.openUrlInBrowser(Constants.URL_IVY_TELEGRAM_INVITE)
}
)

fun bugsApology(): CustomerJourneyCardModel = CustomerJourneyCardModel(
id = "bugs_apology_1",
condition = { trnCount, _, _ ->
trnCount > 10
},
title = "Apologies for the bugs!",
description = "Ivy Wallet v4.6.2 had some annoying bugs... " +
"We're sorry for that and we hope that we have fixed them.\n\n" +
"Ivy Wallet is an open-source and community-driven project " +
"that is maintained and develop solely by voluntary contributors. " +
"So to help us and make your experience better, " +
"please report any bugs as a GitHub issue. You can also" +
" join our community and become a contributor!",
cta = "Report a bug",
ctaIcon = R.drawable.github_logo,
background = Gradient.solid(Blue),
hasDismiss = true,
onAction = { _, _, ivyActivity ->
ivyActivity.openUrlInBrowser(Constants.URL_GITHUB_NEW_ISSUE)
}
)
}
}
36 changes: 36 additions & 0 deletions shared/data/core/src/test/java/com/ivy/data/ArbAccountEntity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.ivy.data

import com.ivy.data.db.entity.AccountEntity
import com.ivy.data.model.testing.colorInt
import com.ivy.data.model.testing.iconAsset
import com.ivy.data.model.testing.maybe
import com.ivy.data.model.testing.notBlankTrimmedString
import io.kotest.property.Arb
import io.kotest.property.arbitrary.arbitrary
import io.kotest.property.arbitrary.boolean
import io.kotest.property.arbitrary.double
import io.kotest.property.arbitrary.of
import io.kotest.property.arbitrary.removeEdgecases
import io.kotest.property.arbitrary.string
import io.kotest.property.arbitrary.uuid

fun Arb.Companion.invalidAccountEntity(): Arb<AccountEntity> = arbitrary {
val validEntity = validAccountEntity().bind()
validEntity.copy(
name = Arb.of("", " ", " ").bind()
)
}

fun Arb.Companion.validAccountEntity(): Arb<AccountEntity> = arbitrary {
AccountEntity(
name = Arb.notBlankTrimmedString().bind().value,
currency = Arb.maybe(Arb.string()).bind(),
color = Arb.colorInt().bind().value,
icon = Arb.iconAsset().bind().id,
orderNum = Arb.double().removeEdgecases().bind(),
includeInBalance = Arb.boolean().bind(),
isSynced = Arb.boolean().bind(),
isDeleted = Arb.boolean().bind(),
id = Arb.uuid().bind()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.ivy.data.repository.mapper

import com.ivy.data.invalidAccountEntity
import com.ivy.data.model.primitive.AssetCode
import com.ivy.data.model.testing.account
import com.ivy.data.repository.CurrencyRepository
import com.ivy.data.validAccountEntity
import io.kotest.assertions.arrow.core.shouldBeLeft
import io.kotest.assertions.arrow.core.shouldBeRight
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.checkAll
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test

class AccountMapperPropertyTest {

private val currencyRepository = mockk<CurrencyRepository>()

private lateinit var mapper: AccountMapper

@Before
fun mapper() {
mapper = AccountMapper(
currencyRepository = currencyRepository,
)
}

@Test
fun `property - domain-entity isomorphism`() = runTest {
checkAll(Arb.account()) { accOrig ->
with(mapper) {
// when: domain -> entity -> domain
val entityOne = accOrig.toEntity()
val accTwo = entityOne.toDomain().getOrNull()

// then: the recovered domain trn must be the same
accTwo.shouldNotBeNull() shouldBe accOrig

// and when again: domain -> entity
val entityTwo = accTwo.toEntity()

// then: the recovered entity must be the same
entityTwo shouldBe entityOne
}
}
}

@Test
fun `maps invalid accounts - always fails`() = runTest {
checkAll(Arb.invalidAccountEntity()) { entity ->
// given
coEvery { currencyRepository.getBaseCurrency() } returns AssetCode.EUR

// when
val res = with(mapper) { entity.toDomain() }

// then
res.shouldBeLeft()
}
}

@Test
fun `maps valid accounts - always succeeds`() = runTest {
checkAll(Arb.validAccountEntity()) { entity ->
// given
coEvery { currencyRepository.getBaseCurrency() } returns AssetCode.EUR

// when
val res = with(mapper) { entity.toDomain() }

// then
res.shouldBeRight()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.ivy.data.repository.mapper

import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.ivy.data.db.entity.AccountEntity
import com.ivy.data.model.Account
import com.ivy.data.model.AccountId
import com.ivy.data.model.primitive.AssetCode
import com.ivy.data.model.primitive.ColorInt
import com.ivy.data.model.primitive.IconAsset
import com.ivy.data.model.primitive.NotBlankTrimmedString
import com.ivy.data.model.testing.ModelFixtures
import com.ivy.data.repository.CurrencyRepository
import io.kotest.assertions.arrow.core.shouldBeLeft
import io.kotest.assertions.arrow.core.shouldBeRight
Expand All @@ -16,9 +19,11 @@ import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.time.Instant
import java.util.UUID

@RunWith(TestParameterInjector::class)
class AccountMapperTest {

private val currencyRepository = mockk<CurrencyRepository>(relaxed = true)
Expand All @@ -31,19 +36,21 @@ class AccountMapperTest {
}

@Test
fun `maps domain to entity`() {
fun `maps domain to entity`(
@TestParameter includeInBalance: Boolean,
@TestParameter removed: Boolean,
) {
// given
val id = UUID.randomUUID()
val account = Account(
id = AccountId(id),
id = ModelFixtures.AccountId,
name = NotBlankTrimmedString.unsafe("Test"),
asset = AssetCode.unsafe("USD"),
color = ColorInt(value = 42),
icon = IconAsset.unsafe("icon"),
includeInBalance = true,
includeInBalance = includeInBalance,
orderNum = 3.14,
lastUpdated = Instant.EPOCH,
removed = false
removed = removed
)

// when
Expand All @@ -55,20 +62,25 @@ class AccountMapperTest {
currency = "USD",
color = 42,
icon = "icon",
includeInBalance = true,
includeInBalance = includeInBalance,
orderNum = 3.14,
isSynced = true,
isDeleted = false,
id = id,
isDeleted = removed,
id = ModelFixtures.AccountId.value,
)
}

// region entity to domain
@Test
fun `maps entity to domain - valid entity`() = runTest {
fun `maps entity to domain - valid entity`(
@TestParameter includeInBalance: Boolean,
@TestParameter removed: Boolean,
) = runTest {
// given
val entity = ValidEntity.copy(
orderNum = 42.0
orderNum = 42.0,
includeInBalance = includeInBalance,
isDeleted = removed,
)

// when
Expand All @@ -81,10 +93,10 @@ class AccountMapperTest {
asset = AssetCode.unsafe("USD"),
color = ColorInt(value = 42),
icon = IconAsset.unsafe("icon"),
includeInBalance = true,
includeInBalance = includeInBalance,
orderNum = 42.0,
lastUpdated = Instant.EPOCH,
removed = false
removed = removed
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
package com.ivy.data.model.testing

import arrow.core.None
import arrow.core.Option
import arrow.core.getOrElse
import com.ivy.data.model.Category
import com.ivy.data.model.CategoryId
import io.kotest.property.Arb
import io.kotest.property.arbitrary.arbitrary
import io.kotest.property.arbitrary.boolean
import io.kotest.property.arbitrary.double
import io.kotest.property.arbitrary.instant
import io.kotest.property.arbitrary.map
import io.kotest.property.arbitrary.removeEdgecases
import io.kotest.property.arbitrary.uuid

fun Arb.Companion.category(
categoryId: Option<CategoryId> = None,
): Arb<Category> = arbitrary {
Category(
id = categoryId.getOrElse { Arb.categoryId().bind() },
name = Arb.notBlankTrimmedString().bind(),
color = Arb.colorInt().bind(),
icon = Arb.maybe(Arb.iconAsset()).bind(),
orderNum = Arb.double().removeEdgecases().bind(),
lastUpdated = Arb.instant().bind(),
removed = Arb.boolean().bind()
)
}

fun Arb.Companion.categoryId(): Arb<CategoryId> = Arb.uuid().map(::CategoryId)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ivy.data.model.testing

import arrow.core.Some
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.checkAll
import io.kotest.property.forAll
import kotlinx.coroutines.test.runTest
import org.junit.Test

class ArbCategoryTest {

@Test
fun `generates arb category`() = runTest {
forAll(Arb.category()) {
true
}
}

@Test
fun `arb category respects passed param`() = runTest {
val categoryId = ModelFixtures.CategoryId

checkAll(Arb.category(categoryId = Some(categoryId))) { category ->
category.id shouldBe categoryId
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ class ArbTransactionTest {

@Test
fun `generates arb transfer`() = runTest {
forAll(Arb.transfer()) {
true
forAll(Arb.transfer()) { transfer ->
transfer.fromAccount != transfer.toAccount
}
}

Expand Down
3 changes: 3 additions & 0 deletions shared/domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ dependencies {
implementation(libs.bundles.ktor)
implementation(libs.bundles.opencsv)

testImplementation(projects.shared.data.modelTesting)
testImplementation(projects.shared.data.coreTesting)

androidTestImplementation(libs.bundles.integration.testing)
androidTestImplementation(libs.mockk.android)
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,13 @@ class ExportCsvUseCase @Inject constructor(
}

private fun String.escapeCsvString(): String = try {
StringEscapeUtils.escapeCsv(this)
StringEscapeUtils.escapeCsv(this).escapeSpecialChars()
} catch (e: Exception) {
replace(CSV_SEPARATOR, " ")
.replace(NEWLINE, " ")
escapeSpecialChars()
}

private fun String.escapeSpecialChars(): String = replace("\\", "")

private fun Transaction.toIvyCsvRow(): IvyCsvRow = when (this) {
is Expense -> expenseCsvRow()
is Income -> incomeCsvRow()
Expand Down

0 comments on commit be76546

Please sign in to comment.