mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
test(upgrade): Cover PENDING purchases granting no Pro and no ack
This commit is contained in:
+23
@@ -6,17 +6,40 @@ import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.junit.jupiter.api.Test
|
||||
import testhelpers.BaseTest
|
||||
import testhelpers.coroutine.runTest2
|
||||
|
||||
class BillingClientConnectionTest : BaseTest() {
|
||||
|
||||
private fun mockPurchase(
|
||||
productId: String = CapodSku.Iap.PRO_UPGRADE.id,
|
||||
purchaseTime: Long = 1_000,
|
||||
state: Int = Purchase.PurchaseState.PURCHASED,
|
||||
): Purchase = mockk {
|
||||
every { products } returns listOf(productId)
|
||||
every { this@mockk.purchaseTime } returns purchaseTime
|
||||
every { purchaseState } returns state
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pending purchases are filtered out of the push-based purchases flow`() = runTest2 {
|
||||
// A PENDING purchase (e.g. a slow cash/deferred payment) must never reach the entitlement
|
||||
// layer — only PURCHASED grants Pro. This guards the #628 fix at the connection boundary.
|
||||
val pending = mockPurchase(purchaseTime = 2_000, state = Purchase.PurchaseState.PENDING)
|
||||
val purchased = mockPurchase(purchaseTime = 1_000, state = Purchase.PurchaseState.PURCHASED)
|
||||
|
||||
val connection = BillingClientConnection(
|
||||
client = mockk(relaxed = true),
|
||||
purchasesGlobal = MutableStateFlow(listOf(pending, purchased)),
|
||||
freshObservations = MutableSharedFlow(),
|
||||
purchaseFailuresGlobal = MutableSharedFlow(),
|
||||
)
|
||||
|
||||
connection.purchases.first() shouldBe listOf(purchased)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+38
@@ -2,6 +2,7 @@ package eu.darken.capod.common.upgrade.core.data
|
||||
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.Purchase
|
||||
import eu.darken.capod.common.AppForegroundState
|
||||
import eu.darken.capod.common.upgrade.core.client.BillingClientConnection
|
||||
import eu.darken.capod.common.upgrade.core.client.BillingClientConnectionProvider
|
||||
@@ -212,6 +213,43 @@ class BillingDataRepoTest : BaseTest() {
|
||||
mapped shouldBe original
|
||||
}
|
||||
|
||||
private fun mockPurchase(state: Int, acknowledged: Boolean): Purchase = mockk {
|
||||
every { purchaseState } returns state
|
||||
every { isAcknowledged } returns acknowledged
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ack pipeline acknowledges only unacknowledged PURCHASED purchases`() = runTest2 {
|
||||
val testScope = TestScope(UnconfinedTestDispatcher(testScheduler))
|
||||
// A PENDING purchase must not be acknowledged (acking it fails and spins the retry loop);
|
||||
// an already-acknowledged one must not be re-acked; only a settled, unacked one is acked.
|
||||
val pending = mockPurchase(Purchase.PurchaseState.PENDING, acknowledged = false)
|
||||
val purchasedUnacked = mockPurchase(Purchase.PurchaseState.PURCHASED, acknowledged = false)
|
||||
val purchasedAcked = mockPurchase(Purchase.PurchaseState.PURCHASED, acknowledged = true)
|
||||
|
||||
val clientConnection = mockk<BillingClientConnection> {
|
||||
every { purchases } returns flowOf(listOf(pending, purchasedUnacked, purchasedAcked))
|
||||
coEvery { acknowledgePurchase(any()) } returns Unit
|
||||
}
|
||||
val provider = mockk<BillingClientConnectionProvider> {
|
||||
every { connection } returns flowOf(clientConnection)
|
||||
}
|
||||
val foregroundState = mockk<AppForegroundState> {
|
||||
every { isForeground } returns MutableStateFlow(false)
|
||||
}
|
||||
|
||||
try {
|
||||
BillingDataRepo(provider, testScope, foregroundState, TestTimeSource())
|
||||
testScope.testScheduler.runCurrent()
|
||||
|
||||
coVerify(exactly = 1) { clientConnection.acknowledgePurchase(purchasedUnacked) }
|
||||
coVerify(exactly = 0) { clientConnection.acknowledgePurchase(pending) }
|
||||
coVerify(exactly = 0) { clientConnection.acknowledgePurchase(purchasedAcked) }
|
||||
} finally {
|
||||
testScope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
private class ForegroundRefreshHarness(testScope: TestScope) {
|
||||
val clientConnection = mockk<BillingClientConnection> {
|
||||
every { purchases } returns emptyFlow()
|
||||
|
||||
Reference in New Issue
Block a user