Skip to content

Commit 0089460

Browse files
committed
test: add ViewModel state and services mapper tests
Add 56 tests across 6 new test files: - WithdrawalViewModel, DepositViewModel, BalanceViewModel, MyAccountScreenViewModel pure state reducer tests - ActivityFeedMessageMapper and UserProfileMapper protobuf mapping tests
1 parent 12c0daa commit 0089460

9 files changed

Lines changed: 697 additions & 0 deletions

File tree

apps/flipcash/features/balance/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ android {
77
}
88

99
dependencies {
10+
testImplementation(kotlin("test"))
11+
1012
implementation(libs.compose.paging)
1113

1214
implementation(project(":apps:flipcash:shared:analytics"))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.flipcash.app.balance.internal
2+
3+
import com.flipcash.services.internal.model.thirdparty.OnRampProvider
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
import kotlin.test.assertNull
7+
8+
class BalanceViewModelStateTest {
9+
10+
private val reduce = BalanceViewModel.Companion.updateStateForEvent
11+
12+
@Test
13+
fun `default state has null provider`() {
14+
assertNull(BalanceViewModel.State().preferredOnRampProvider)
15+
}
16+
17+
@Test
18+
fun `OnPreferredOnRampProviderChanged updates provider`() {
19+
val provider = OnRampProvider.ManualDeposit
20+
val updated = reduce(
21+
BalanceViewModel.Event.OnPreferredOnRampProviderChanged(provider)
22+
)(BalanceViewModel.State())
23+
assertEquals(provider, updated.preferredOnRampProvider)
24+
}
25+
26+
@Test
27+
fun `OnPreferredOnRampProviderChanged with null clears provider`() {
28+
val state = BalanceViewModel.State(
29+
preferredOnRampProvider = OnRampProvider.ManualDeposit
30+
)
31+
val updated = reduce(
32+
BalanceViewModel.Event.OnPreferredOnRampProviderChanged(null)
33+
)(state)
34+
assertNull(updated.preferredOnRampProvider)
35+
}
36+
37+
@Test
38+
fun `OpenCurrencySelection is no-op`() {
39+
val state = BalanceViewModel.State(
40+
preferredOnRampProvider = OnRampProvider.ManualDeposit
41+
)
42+
val updated = reduce(BalanceViewModel.Event.OpenCurrencySelection)(state)
43+
assertEquals(state, updated)
44+
}
45+
}

apps/flipcash/features/deposit/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ android {
77
}
88

99
dependencies {
10+
testImplementation(kotlin("test"))
11+
1012
implementation(project(":libs:messaging"))
1113

1214
implementation(project(":apps:flipcash:shared:featureflags"))
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.flipcash.app.deposit.internal
2+
3+
import com.getcode.solana.keys.PublicKey
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
import kotlin.test.assertFalse
7+
import kotlin.test.assertNull
8+
import kotlin.test.assertTrue
9+
10+
class DepositViewModelStateTest {
11+
12+
private val reduce = DepositViewModel.Companion.updateStateForEvent
13+
private fun mint() = com.getcode.solana.keys.Mint(ByteArray(32) { 1 }.toList())
14+
15+
@Test
16+
fun `default state has null address and token name`() {
17+
val state = DepositViewModel.State()
18+
assertNull(state.selectedTokenAddress)
19+
assertNull(state.tokenName)
20+
assertEquals("", state.depositAddress)
21+
assertFalse(state.isCopied)
22+
}
23+
24+
@Test
25+
fun `OnMintSelected sets selected token address`() {
26+
val mint = mint()
27+
val updated = reduce(DepositViewModel.Event.OnMintSelected(mint))(DepositViewModel.State())
28+
assertEquals(mint, updated.selectedTokenAddress)
29+
}
30+
31+
@Test
32+
fun `OnTokenChanged sets address and name`() {
33+
val updated = reduce(
34+
DepositViewModel.Event.OnTokenChanged(address = "abc123", name = "TestToken")
35+
)(DepositViewModel.State())
36+
assertEquals("abc123", updated.depositAddress)
37+
assertEquals("TestToken", updated.tokenName)
38+
}
39+
40+
@Test
41+
fun `SetCopied true then false roundtrip`() {
42+
val state = DepositViewModel.State()
43+
val copied = reduce(DepositViewModel.Event.SetCopied(true))(state)
44+
assertTrue(copied.isCopied)
45+
val uncopied = reduce(DepositViewModel.Event.SetCopied(false))(copied)
46+
assertFalse(uncopied.isCopied)
47+
}
48+
49+
@Test
50+
fun `CopyAddress is no-op`() {
51+
val state = DepositViewModel.State(depositAddress = "addr", tokenName = "T")
52+
val updated = reduce(DepositViewModel.Event.CopyAddress)(state)
53+
assertEquals(state, updated)
54+
}
55+
56+
@Test
57+
fun `Exit is no-op`() {
58+
val state = DepositViewModel.State(depositAddress = "addr")
59+
val updated = reduce(DepositViewModel.Event.Exit)(state)
60+
assertEquals(state, updated)
61+
}
62+
}

apps/flipcash/features/myaccount/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ android {
77
}
88

99
dependencies {
10+
testImplementation(kotlin("test"))
11+
1012
implementation(project(":apps:flipcash:shared:authentication"))
1113
implementation(project(":apps:flipcash:shared:featureflags"))
1214
implementation(project(":apps:flipcash:shared:menu"))
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.flipcash.app.myaccount.internal
2+
3+
import kotlin.test.Test
4+
import kotlin.test.assertEquals
5+
import kotlin.test.assertFalse
6+
import kotlin.test.assertNull
7+
import kotlin.test.assertTrue
8+
9+
class MyAccountScreenViewModelStateTest {
10+
11+
private val reduce = MyAccountScreenViewModel.Companion.updateStateForEvent
12+
13+
@Test
14+
fun `default state has null user info and beta disabled`() {
15+
val state = MyAccountScreenViewModel.State()
16+
assertNull(state.accountId)
17+
assertNull(state.publicKey)
18+
assertNull(state.pushToken)
19+
assertFalse(state.isBetaEnabled)
20+
assertFalse(state.showAccountInfo)
21+
}
22+
23+
@Test
24+
fun `OnUserAssociated sets user fields`() {
25+
val updated = reduce(
26+
MyAccountScreenViewModel.Event.OnUserAssociated(
27+
userId = "user-123",
28+
publicKey = "pk-abc",
29+
pushToken = "token-xyz"
30+
)
31+
)(MyAccountScreenViewModel.State())
32+
assertEquals("user-123", updated.accountId)
33+
assertEquals("pk-abc", updated.publicKey)
34+
assertEquals("token-xyz", updated.pushToken)
35+
}
36+
37+
@Test
38+
fun `OnUserAssociated with null values`() {
39+
val updated = reduce(
40+
MyAccountScreenViewModel.Event.OnUserAssociated(
41+
userId = null,
42+
publicKey = null,
43+
pushToken = null
44+
)
45+
)(MyAccountScreenViewModel.State())
46+
assertNull(updated.accountId)
47+
assertNull(updated.publicKey)
48+
assertNull(updated.pushToken)
49+
}
50+
51+
@Test
52+
fun `OnBetaFeaturesUnlocked true enables beta and shows all menu items`() {
53+
val updated = reduce(
54+
MyAccountScreenViewModel.Event.OnBetaFeaturesUnlocked(true)
55+
)(MyAccountScreenViewModel.State())
56+
assertTrue(updated.isBetaEnabled)
57+
assertTrue(updated.items.any { it is VerifyPhone })
58+
assertTrue(updated.items.any { it is VerifyEmail })
59+
}
60+
61+
@Test
62+
fun `OnBetaFeaturesUnlocked false disables beta and hides verification items`() {
63+
val state = MyAccountScreenViewModel.State(isBetaEnabled = true)
64+
val updated = reduce(
65+
MyAccountScreenViewModel.Event.OnBetaFeaturesUnlocked(false)
66+
)(state)
67+
assertFalse(updated.isBetaEnabled)
68+
assertFalse(updated.items.any { it is VerifyPhone })
69+
assertFalse(updated.items.any { it is VerifyEmail })
70+
}
71+
72+
@Test
73+
fun `menu always contains AccessKey LogOut and DeleteAccount`() {
74+
val withBeta = reduce(
75+
MyAccountScreenViewModel.Event.OnBetaFeaturesUnlocked(true)
76+
)(MyAccountScreenViewModel.State())
77+
assertTrue(withBeta.items.any { it is AccessKey })
78+
assertTrue(withBeta.items.any { it is LogOut })
79+
assertTrue(withBeta.items.any { it is DeleteAccount })
80+
81+
val withoutBeta = reduce(
82+
MyAccountScreenViewModel.Event.OnBetaFeaturesUnlocked(false)
83+
)(MyAccountScreenViewModel.State())
84+
assertTrue(withoutBeta.items.any { it is AccessKey })
85+
assertTrue(withoutBeta.items.any { it is LogOut })
86+
assertTrue(withoutBeta.items.any { it is DeleteAccount })
87+
}
88+
89+
@Test
90+
fun `ToggleAccountInfo sets showAccountInfo`() {
91+
val shown = reduce(
92+
MyAccountScreenViewModel.Event.ToggleAccountInfo(true)
93+
)(MyAccountScreenViewModel.State())
94+
assertTrue(shown.showAccountInfo)
95+
96+
val hidden = reduce(
97+
MyAccountScreenViewModel.Event.ToggleAccountInfo(false)
98+
)(shown)
99+
assertFalse(hidden.showAccountInfo)
100+
}
101+
102+
@Test
103+
fun `no-op events return state unchanged`() {
104+
val state = MyAccountScreenViewModel.State(accountId = "test", isBetaEnabled = true)
105+
val noOpEvents = listOf(
106+
MyAccountScreenViewModel.Event.OnLogOutClicked,
107+
MyAccountScreenViewModel.Event.OnLoggedOutCompletely,
108+
MyAccountScreenViewModel.Event.OnVerifyPhoneClicked,
109+
MyAccountScreenViewModel.Event.OnVerifyEmailClicked,
110+
MyAccountScreenViewModel.Event.OnViewAccessKey,
111+
MyAccountScreenViewModel.Event.CopyPublicKey,
112+
MyAccountScreenViewModel.Event.CopyAccountId,
113+
MyAccountScreenViewModel.Event.CopyPushToken,
114+
MyAccountScreenViewModel.Event.OnTitleClicked,
115+
MyAccountScreenViewModel.Event.OnDeleteAccountClicked,
116+
MyAccountScreenViewModel.Event.OnAccountDeleted,
117+
MyAccountScreenViewModel.Event.OnAccessKeyClicked,
118+
)
119+
noOpEvents.forEach { event ->
120+
assertEquals(state, reduce(event)(state), "Event $event should be no-op")
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)