Skip to content

Commit 553ea2d

Browse files
Adds tests.
1 parent a063e79 commit 553ea2d

File tree

7 files changed

+173
-6
lines changed

7 files changed

+173
-6
lines changed

financial-connections-lite/build.gradle

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ dependencies {
3232

3333
testImplementation testLibs.json
3434
testImplementation testLibs.junit
35+
testImplementation testLibs.androidx.archCore
36+
testImplementation testLibs.androidx.core
37+
testImplementation testLibs.androidx.junit
38+
testImplementation testLibs.kotlin.annotations
39+
testImplementation testLibs.kotlin.coroutines
40+
testImplementation testLibs.kotlin.junit
41+
testImplementation testLibs.mockito.core
42+
testImplementation testLibs.mockito.inline
43+
testImplementation testLibs.mockito.kotlin
44+
testImplementation testLibs.turbine
45+
testImplementation testLibs.truth
3546

3647
androidTestUtil testLibs.testOrchestrator
3748
}

financial-connections-lite/src/main/java/com/stripe/android/financialconnections/lite/FinancialConnectionsLiteViewModel.kt

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import kotlinx.coroutines.CoroutineDispatcher
2828
import kotlinx.coroutines.flow.MutableSharedFlow
2929
import kotlinx.coroutines.flow.MutableStateFlow
3030
import kotlinx.coroutines.flow.SharedFlow
31+
import kotlinx.coroutines.flow.StateFlow
3132
import kotlinx.coroutines.flow.update
3233
import kotlinx.coroutines.launch
3334

@@ -46,6 +47,7 @@ internal class FinancialConnectionsLiteViewModel(
4647
val viewEffects: SharedFlow<ViewEffect> get() = _viewEffects
4748

4849
private val _state = MutableStateFlow<State?>(null)
50+
val state: StateFlow<State?> get() = _state
4951

5052
init {
5153
viewModelScope.launch(workContext) {

financial-connections-lite/src/main/java/com/stripe/android/financialconnections/lite/di/Di.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.stripe.android.core.networking.ApiRequest
77
import com.stripe.android.core.networking.DefaultStripeNetworkClient
88
import com.stripe.android.financialconnections.lite.network.FinancialConnectionsLiteRequestExecutor
99
import com.stripe.android.financialconnections.lite.repository.FinancialConnectionsLiteRepository
10+
import com.stripe.android.financialconnections.lite.repository.FinancialConnectionsLiteRepositoryImpl
1011
import kotlinx.coroutines.Dispatchers
1112
import kotlinx.serialization.json.Json
1213

@@ -25,7 +26,7 @@ internal object Di {
2526
val workContext = Dispatchers.IO
2627
val logger = Logger.getInstance(enableLogging = BuildConfig.DEBUG)
2728

28-
fun repository(): FinancialConnectionsLiteRepository = FinancialConnectionsLiteRepository(
29+
fun repository(): FinancialConnectionsLiteRepository = FinancialConnectionsLiteRepositoryImpl(
2930
requestExecutor = FinancialConnectionsLiteRequestExecutor(
3031
stripeNetworkClient = DefaultStripeNetworkClient(
3132
workContext = workContext,
+18-5
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,28 @@ import com.stripe.android.financialconnections.lite.repository.model.Synchronize
77
import com.stripe.android.financialconnections.model.FinancialConnectionsSession
88
import java.util.Locale
99

10-
internal class FinancialConnectionsLiteRepository(
10+
internal interface FinancialConnectionsLiteRepository {
11+
suspend fun synchronize(
12+
configuration: FinancialConnectionsSheetConfiguration,
13+
applicationId: String
14+
): Result<SynchronizeSessionResponse>
15+
16+
suspend fun getFinancialConnectionsSession(
17+
configuration: FinancialConnectionsSheetConfiguration
18+
): Result<FinancialConnectionsSession>
19+
}
20+
21+
internal class FinancialConnectionsLiteRepositoryImpl(
1122
private val requestExecutor: FinancialConnectionsLiteRequestExecutor,
1223
private val apiRequestFactory: ApiRequest.Factory,
13-
) {
24+
) : FinancialConnectionsLiteRepository {
1425

1526
fun FinancialConnectionsSheetConfiguration.apiRequestOptions() = ApiRequest.Options(
1627
publishableKeyProvider = { publishableKey },
1728
stripeAccountIdProvider = { stripeAccountId },
1829
)
1930

20-
suspend fun synchronize(
31+
override suspend fun synchronize(
2132
configuration: FinancialConnectionsSheetConfiguration,
2233
applicationId: String,
2334
): Result<SynchronizeSessionResponse> = requestExecutor.execute(
@@ -29,15 +40,16 @@ internal class FinancialConnectionsLiteRepository(
2940
"mobile" to mapOf(
3041
PARAMS_FULLSCREEN to true,
3142
PARAMS_HIDE_CLOSE_BUTTON to false,
32-
PARAMS_APPLICATION_ID to applicationId
43+
PARAMS_APPLICATION_ID to applicationId,
44+
PARAMS_MOBILE_SDK_TYPE to "fc_lite"
3345
),
3446
PARAMS_CLIENT_SECRET to configuration.financialConnectionsSessionClientSecret
3547
)
3648
),
3749
SynchronizeSessionResponse.serializer()
3850
)
3951

40-
suspend fun getFinancialConnectionsSession(
52+
override suspend fun getFinancialConnectionsSession(
4153
configuration: FinancialConnectionsSheetConfiguration,
4254
): Result<FinancialConnectionsSession> {
4355
val financialConnectionsRequest = apiRequestFactory.createGet(
@@ -57,6 +69,7 @@ internal class FinancialConnectionsLiteRepository(
5769
internal const val PARAMS_FULLSCREEN = "fullscreen"
5870
internal const val PARAMS_HIDE_CLOSE_BUTTON = "hide_close_button"
5971
internal const val PARAMS_APPLICATION_ID = "application_id"
72+
internal const val PARAMS_MOBILE_SDK_TYPE = "mobile_sdk_type"
6073
internal const val PARAMS_CLIENT_SECRET = "client_secret"
6174

6275
internal const val synchronizeSessionUrl: String =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.stripe.android.financialconnections.lite
2+
3+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4+
import androidx.lifecycle.SavedStateHandle
5+
import app.cash.turbine.test
6+
import com.stripe.android.core.Logger
7+
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetActivityArgs
8+
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetActivityArgs.ForData
9+
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetActivityResult.Failed
10+
import com.stripe.android.financialconnections.lite.FinancialConnectionsLiteViewModel.ViewEffect
11+
import com.stripe.android.financialconnections.lite.TextFixtures.configuration
12+
import com.stripe.android.financialconnections.lite.TextFixtures.financialConnectionsSessionNoAccounts
13+
import com.stripe.android.financialconnections.lite.TextFixtures.syncResponse
14+
import com.stripe.android.financialconnections.lite.repository.FinancialConnectionsLiteRepository
15+
import kotlinx.coroutines.ExperimentalCoroutinesApi
16+
import kotlinx.coroutines.test.StandardTestDispatcher
17+
import kotlinx.coroutines.test.TestScope
18+
import kotlinx.coroutines.test.runTest
19+
import org.junit.Rule
20+
import org.junit.Test
21+
import kotlin.test.assertEquals
22+
23+
@OptIn(ExperimentalCoroutinesApi::class)
24+
class FinancialConnectionsLiteViewModelTest {
25+
26+
@get:Rule
27+
val instantTaskExecutorRule = InstantTaskExecutorRule()
28+
29+
private val testDispatcher = StandardTestDispatcher()
30+
private val testScope = TestScope(testDispatcher)
31+
32+
private val logger: Logger = Logger.noop()
33+
34+
private fun viewModel(
35+
args: FinancialConnectionsSheetActivityArgs = ForData(configuration),
36+
repository: FinancialConnectionsLiteRepository
37+
) = FinancialConnectionsLiteViewModel(
38+
logger = logger,
39+
savedStateHandle = SavedStateHandle(
40+
mapOf(FinancialConnectionsSheetActivityArgs.EXTRA_ARGS to args)
41+
),
42+
repository = repository,
43+
workContext = testDispatcher,
44+
applicationId = "com.example"
45+
)
46+
47+
@Test
48+
fun `test view model initializes with correct state`() = testScope.runTest {
49+
val viewModel = viewModel(
50+
repository = TestFinancialConnectionsLiteRepository(
51+
synchronizeResponse = Result.success(syncResponse),
52+
sessionResponse = Result.success(financialConnectionsSessionNoAccounts)
53+
)
54+
)
55+
56+
testDispatcher.scheduler.advanceUntilIdle()
57+
58+
val state = viewModel.state.value
59+
assertEquals(state!!.successUrl, syncResponse.manifest.successUrl)
60+
assertEquals(state.cancelUrl, syncResponse.manifest.cancelUrl)
61+
assertEquals(state.hostedAuthUrl, "${syncResponse.manifest.hostedAuthUrl}&launched_by=android_sdk")
62+
}
63+
64+
@Test
65+
fun `test view effect emits error if sync fails`() = testScope.runTest {
66+
// Arrange: Return failure for synchronize response
67+
val expectedError = RuntimeException("Network Error")
68+
viewModel(
69+
repository = TestFinancialConnectionsLiteRepository(
70+
synchronizeResponse = Result.failure(expectedError),
71+
sessionResponse = Result.success(financialConnectionsSessionNoAccounts)
72+
)
73+
).viewEffects.test {
74+
testDispatcher.scheduler.advanceUntilIdle()
75+
val viewEffect = awaitItem() as ViewEffect.FinishWithResult
76+
val failedResult = viewEffect.result as Failed
77+
assertEquals(expectedError, (failedResult.error))
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.stripe.android.financialconnections.lite
2+
3+
import com.stripe.android.financialconnections.FinancialConnectionsSheetConfiguration
4+
import com.stripe.android.financialconnections.lite.repository.FinancialConnectionsLiteRepository
5+
import com.stripe.android.financialconnections.lite.repository.model.SynchronizeSessionResponse
6+
import com.stripe.android.financialconnections.model.FinancialConnectionsSession
7+
8+
internal class TestFinancialConnectionsLiteRepository(
9+
private val synchronizeResponse: Result<SynchronizeSessionResponse>,
10+
private val sessionResponse: Result<FinancialConnectionsSession>
11+
) : FinancialConnectionsLiteRepository {
12+
13+
override suspend fun synchronize(
14+
configuration: FinancialConnectionsSheetConfiguration,
15+
applicationId: String,
16+
): Result<SynchronizeSessionResponse> {
17+
return synchronizeResponse
18+
}
19+
20+
override suspend fun getFinancialConnectionsSession(
21+
configuration: FinancialConnectionsSheetConfiguration,
22+
): Result<FinancialConnectionsSession> {
23+
return sessionResponse
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.stripe.android.financialconnections.lite
2+
3+
import com.stripe.android.financialconnections.FinancialConnectionsSheetConfiguration
4+
import com.stripe.android.financialconnections.lite.repository.model.FinancialConnectionsSessionManifest
5+
import com.stripe.android.financialconnections.lite.repository.model.SynchronizeSessionResponse
6+
import com.stripe.android.financialconnections.model.FinancialConnectionsAccountList
7+
import com.stripe.android.financialconnections.model.FinancialConnectionsSession
8+
9+
internal object TextFixtures {
10+
11+
val configuration = FinancialConnectionsSheetConfiguration(
12+
financialConnectionsSessionClientSecret = "client_secret_123",
13+
publishableKey = "pk_test_123",
14+
)
15+
16+
val syncResponse = SynchronizeSessionResponse(
17+
manifest = FinancialConnectionsSessionManifest(
18+
hostedAuthUrl = "https://example.com/auth",
19+
successUrl = "https://example.com/success",
20+
cancelUrl = "https://example.com/cancel",
21+
)
22+
)
23+
24+
val financialConnectionsSessionNoAccounts = FinancialConnectionsSession(
25+
clientSecret = "las_1234567890",
26+
id = "fcsess_secret",
27+
accountsNew = FinancialConnectionsAccountList(
28+
data = emptyList(),
29+
hasMore = false,
30+
url = "url",
31+
count = 0
32+
),
33+
livemode = true
34+
)
35+
}

0 commit comments

Comments
 (0)