Skip to content

Commit 01cc272

Browse files
committed
chore: add additional test coverage for Estimator and opencode financial and transactors
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 06a8f5f commit 01cc272

10 files changed

Lines changed: 1726 additions & 0 deletions

File tree

libs/currency-math/src/test/java/com/flipcash/libs/currency/math/EstimatorValueExchangeTests.kt

Lines changed: 700 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.getcode.opencode.internal.transactors
2+
3+
import com.getcode.opencode.model.accounts.AccountInfo
4+
import io.mockk.every
5+
import io.mockk.mockk
6+
import org.junit.Test
7+
import kotlin.test.assertIs
8+
import kotlin.test.assertNull
9+
10+
class ClaimValidationTest {
11+
12+
private fun accountInfo(
13+
claimState: AccountInfo.ClaimState = AccountInfo.ClaimState.NotClaimed,
14+
isGiftCardIssuer: Boolean = false,
15+
): AccountInfo = mockk(relaxed = true) {
16+
every { this@mockk.claimState } returns claimState
17+
every { this@mockk.isGiftCardIssuer } returns isGiftCardIssuer
18+
}
19+
20+
// region claimState checks
21+
22+
@Test
23+
fun `NotClaimed and not issuer returns null (eligible)`() {
24+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
25+
info = accountInfo(claimState = AccountInfo.ClaimState.NotClaimed),
26+
claimIfOwned = false,
27+
)
28+
assertNull(result)
29+
}
30+
31+
@Test
32+
fun `Claimed returns AlreadyClaimed`() {
33+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
34+
info = accountInfo(claimState = AccountInfo.ClaimState.Claimed),
35+
claimIfOwned = false,
36+
)
37+
assertIs<ReceiveGiftTransactorError.AlreadyClaimed>(result)
38+
}
39+
40+
@Test
41+
fun `Expired returns Expired`() {
42+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
43+
info = accountInfo(claimState = AccountInfo.ClaimState.Expired),
44+
claimIfOwned = false,
45+
)
46+
assertIs<ReceiveGiftTransactorError.Expired>(result)
47+
}
48+
49+
@Test
50+
fun `Unknown claimState returns Expired`() {
51+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
52+
info = accountInfo(claimState = AccountInfo.ClaimState.Unknown),
53+
claimIfOwned = false,
54+
)
55+
assertIs<ReceiveGiftTransactorError.Expired>(result)
56+
}
57+
58+
// endregion
59+
60+
// region issuer checks
61+
62+
@Test
63+
fun `issuer with claimIfOwned false returns UsersGiftCard`() {
64+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
65+
info = accountInfo(
66+
claimState = AccountInfo.ClaimState.NotClaimed,
67+
isGiftCardIssuer = true,
68+
),
69+
claimIfOwned = false,
70+
)
71+
assertIs<ReceiveGiftTransactorError.UsersGiftCard>(result)
72+
}
73+
74+
@Test
75+
fun `issuer with claimIfOwned true returns null (eligible)`() {
76+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
77+
info = accountInfo(
78+
claimState = AccountInfo.ClaimState.NotClaimed,
79+
isGiftCardIssuer = true,
80+
),
81+
claimIfOwned = true,
82+
)
83+
assertNull(result)
84+
}
85+
86+
@Test
87+
fun `non-issuer with claimIfOwned false returns null (eligible)`() {
88+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
89+
info = accountInfo(
90+
claimState = AccountInfo.ClaimState.NotClaimed,
91+
isGiftCardIssuer = false,
92+
),
93+
claimIfOwned = false,
94+
)
95+
assertNull(result)
96+
}
97+
98+
// endregion
99+
100+
// region priority (claimState checked before issuer)
101+
102+
@Test
103+
fun `Claimed takes priority over issuer check`() {
104+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
105+
info = accountInfo(
106+
claimState = AccountInfo.ClaimState.Claimed,
107+
isGiftCardIssuer = true,
108+
),
109+
claimIfOwned = false,
110+
)
111+
assertIs<ReceiveGiftTransactorError.AlreadyClaimed>(result)
112+
}
113+
114+
@Test
115+
fun `Expired takes priority over issuer check`() {
116+
val result = ReceiveGiftCardTransactor.validateClaimEligibility(
117+
info = accountInfo(
118+
claimState = AccountInfo.ClaimState.Expired,
119+
isGiftCardIssuer = true,
120+
),
121+
claimIfOwned = true,
122+
)
123+
assertIs<ReceiveGiftTransactorError.Expired>(result)
124+
}
125+
126+
// endregion
127+
}

services/opencode/src/test/kotlin/com/getcode/opencode/internal/transactors/GrabBillTransactorTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.mockk.mockkStatic
1515
import io.mockk.unmockkStatic
1616
import kotlinx.coroutines.ExperimentalCoroutinesApi
1717
import kotlinx.coroutines.test.TestScope
18+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
1819
import kotlinx.coroutines.test.runTest
1920
import org.junit.After
2021
import org.junit.Before
@@ -99,6 +100,25 @@ class GrabBillTransactorTest {
99100

100101
// endregion
101102

103+
// region dispose
104+
105+
@Test
106+
fun `dispose clears state so start fails`() = runTest {
107+
val childScope = TestScope(UnconfinedTestDispatcher(testScheduler))
108+
val transactor = createTransactor(childScope)
109+
setupWith(transactor, PayloadKind.MultiMintCash)
110+
111+
transactor.dispose()
112+
113+
// After dispose, owner and payload are null.
114+
// start() is a suspend function that doesn't use the scope directly,
115+
// but the scope is cancelled so we catch any CancellationException too.
116+
val result = runCatching { transactor.start() }
117+
assertTrue(result.isFailure || result.getOrNull()?.isFailure == true)
118+
}
119+
120+
// endregion
121+
102122
// region helpers
103123

104124
private fun setupWith(transactor: GrabBillTransactor, kind: PayloadKind) {

services/opencode/src/test/kotlin/com/getcode/opencode/internal/transactors/ReceiveGiftCardTransactorTest.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ class ReceiveGiftCardTransactorTest {
7979

8080
// endregion
8181

82+
// region dispose
83+
84+
@Test
85+
fun `dispose clears state so subsequent start fails`() = runTest {
86+
setupWithOwner()
87+
88+
transactor.dispose()
89+
90+
val result = transactor.start(claimIfOwned = false)
91+
assertTrue(result.isFailure)
92+
}
93+
94+
// endregion
95+
8296
// region helpers
8397

8498
private fun setupWithOwner() {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.getcode.opencode.internal.transactors
2+
3+
import com.getcode.opencode.controllers.TransactionController
4+
import com.getcode.opencode.model.accounts.AccountCluster
5+
import com.getcode.opencode.model.accounts.GiftCardAccount
6+
import com.getcode.opencode.model.financial.CurrencyCode
7+
import com.getcode.opencode.model.financial.Fiat
8+
import com.getcode.opencode.model.financial.LocalFiat
9+
import com.getcode.opencode.model.financial.Rate
10+
import com.getcode.opencode.model.financial.Token
11+
import com.getcode.solana.keys.Key32
12+
import com.getcode.solana.keys.Mint
13+
import io.mockk.every
14+
import io.mockk.mockk
15+
import io.mockk.mockkStatic
16+
import io.mockk.unmockkStatic
17+
import kotlinx.coroutines.ExperimentalCoroutinesApi
18+
import kotlinx.coroutines.test.runTest
19+
import org.junit.After
20+
import org.junit.Before
21+
import org.junit.Test
22+
import kotlin.test.assertTrue
23+
24+
@OptIn(ExperimentalCoroutinesApi::class)
25+
class SendGiftCardTransactorTest {
26+
27+
private val transactionController = mockk<TransactionController>(relaxed = true)
28+
private val payloadFactory = PayloadFactory { _, _, _ ->
29+
PayloadResult(rendezvous = mockk(relaxed = true), codeData = emptyList())
30+
}
31+
32+
private val transactor = SendGiftCardTransactor(
33+
transactionController = transactionController,
34+
payloadFactory = payloadFactory,
35+
)
36+
37+
@Before
38+
fun setUp() {
39+
mockkStatic("com.getcode.utils.LoggingKt")
40+
every { com.getcode.utils.trace(any(), any(), any(), any(), any()) } returns Unit
41+
}
42+
43+
@After
44+
fun tearDown() {
45+
unmockkStatic("com.getcode.utils.LoggingKt")
46+
}
47+
48+
// region preconditions
49+
50+
@Test
51+
fun `start fails when with() not called (no state)`() = runTest {
52+
val result = transactor.start()
53+
assertTrue(result.isFailure)
54+
}
55+
56+
// endregion
57+
58+
// region dispose
59+
60+
@Test
61+
fun `dispose clears state so subsequent start fails`() = runTest {
62+
setupWithDefaults()
63+
64+
transactor.dispose()
65+
66+
val result = transactor.start()
67+
assertTrue(result.isFailure)
68+
}
69+
70+
// endregion
71+
72+
// region helpers
73+
74+
private fun setupWithDefaults() {
75+
val token = mockk<Token>(relaxed = true) {
76+
every { address } returns Mint.usdf
77+
}
78+
val amount = LocalFiat(
79+
underlyingTokenAmount = Fiat(quarks = 500L),
80+
nativeAmount = Fiat(fiat = 5.0, currencyCode = CurrencyCode.USD),
81+
rate = Rate.oneToOne,
82+
mint = Mint.usdf,
83+
)
84+
val owner = mockk<AccountCluster>(relaxed = true) {
85+
every { withTimelockForToken(any<Token>()) } returns this
86+
every { vaultPublicKey } returns Key32.mock
87+
}
88+
val giftCard = mockk<GiftCardAccount>(relaxed = true)
89+
90+
transactor.with(giftCard, amount, token, owner)
91+
}
92+
93+
// endregion
94+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.getcode.opencode.model.financial
2+
3+
import com.getcode.opencode.tests.generateRandomPublicKeyForTest
4+
import org.junit.Test
5+
import kotlin.test.assertEquals
6+
import kotlin.test.assertNotEquals
7+
8+
class DistributionTest {
9+
10+
@Test
11+
fun `Distribution stores destination and amount`() {
12+
val destination = generateRandomPublicKeyForTest()
13+
val amount = Fiat(fiat = 25.0)
14+
15+
val distribution = Distribution(destination = destination, amount = amount)
16+
17+
assertEquals(destination, distribution.destination)
18+
assertEquals(25.0, distribution.amount.decimalValue, 0.001)
19+
}
20+
21+
@Test
22+
fun `Distribution with zero amount`() {
23+
val destination = generateRandomPublicKeyForTest()
24+
val distribution = Distribution(destination = destination, amount = Fiat.Zero)
25+
26+
assertEquals(0L, distribution.amount.quarks)
27+
}
28+
29+
@Test
30+
fun `two Distributions with same values are equal`() {
31+
val destination = generateRandomPublicKeyForTest()
32+
val amount = Fiat(fiat = 10.0)
33+
34+
val d1 = Distribution(destination = destination, amount = amount)
35+
val d2 = Distribution(destination = destination, amount = amount)
36+
37+
assertEquals(d1, d2)
38+
}
39+
40+
@Test
41+
fun `two Distributions with different destinations are not equal`() {
42+
val amount = Fiat(fiat = 10.0)
43+
val d1 = Distribution(destination = generateRandomPublicKeyForTest(), amount = amount)
44+
val d2 = Distribution(destination = generateRandomPublicKeyForTest(), amount = amount)
45+
46+
assertNotEquals(d1, d2)
47+
}
48+
49+
@Test
50+
fun `two Distributions with different amounts are not equal`() {
51+
val destination = generateRandomPublicKeyForTest()
52+
val d1 = Distribution(destination = destination, amount = Fiat(fiat = 10.0))
53+
val d2 = Distribution(destination = destination, amount = Fiat(fiat = 20.0))
54+
55+
assertNotEquals(d1, d2)
56+
}
57+
58+
@Test
59+
fun `Distribution amount preserves currency`() {
60+
val destination = generateRandomPublicKeyForTest()
61+
val eurAmount = Fiat(fiat = 50.0, currencyCode = CurrencyCode.EUR)
62+
63+
val distribution = Distribution(destination = destination, amount = eurAmount)
64+
65+
assertEquals(CurrencyCode.EUR, distribution.amount.currencyCode)
66+
}
67+
68+
@Test
69+
fun `Distribution copy changes only specified fields`() {
70+
val destination = generateRandomPublicKeyForTest()
71+
val original = Distribution(destination = destination, amount = Fiat(fiat = 10.0))
72+
val newAmount = Fiat(fiat = 20.0)
73+
74+
val modified = original.copy(amount = newAmount)
75+
76+
assertEquals(destination, modified.destination)
77+
assertEquals(20.0, modified.amount.decimalValue, 0.001)
78+
}
79+
}

0 commit comments

Comments
 (0)