11package to.bitkit.repositories
22
3+ import android.content.Context
4+ import com.synonym.bitkitcore.Activity
35import com.synonym.bitkitcore.IBtOrder
6+ import com.synonym.bitkitcore.OnchainActivity
7+ import com.synonym.bitkitcore.PaymentType
48import kotlinx.coroutines.flow.MutableStateFlow
59import org.junit.Before
610import org.junit.Test
711import org.mockito.kotlin.doReturn
812import org.mockito.kotlin.mock
913import org.mockito.kotlin.whenever
14+ import to.bitkit.R
1015import to.bitkit.data.SettingsStore
1116import to.bitkit.test.BaseUnitTest
1217import to.bitkit.viewmodels.ActivityDetailViewModel
1318import kotlin.test.assertEquals
1419import kotlin.test.assertNull
20+ import kotlin.test.assertTrue
1521
1622class ActivityDetailViewModelTest : BaseUnitTest () {
1723
24+ private val context = mock<Context >()
1825 private val activityRepo = mock<ActivityRepo >()
1926 private val blocktankRepo = mock<BlocktankRepo >()
2027 private val settingsStore = mock<SettingsStore >()
@@ -24,9 +31,15 @@ class ActivityDetailViewModelTest : BaseUnitTest() {
2431
2532 @Before
2633 fun setUp () {
34+ whenever(context.getString(R .string.wallet__activity_error_not_found))
35+ .thenReturn(" Activity not found" )
36+ whenever(context.getString(R .string.wallet__activity_error_load_failed))
37+ .thenReturn(" Failed to load activity" )
2738 whenever(blocktankRepo.blocktankState).thenReturn(MutableStateFlow (BlocktankState ()))
39+ whenever(activityRepo.activitiesChanged).thenReturn(MutableStateFlow (System .currentTimeMillis()))
2840
2941 sut = ActivityDetailViewModel (
42+ context = context,
3043 bgDispatcher = testDispatcher,
3144 activityRepo = activityRepo,
3245 blocktankRepo = blocktankRepo,
@@ -84,4 +97,130 @@ class ActivityDetailViewModelTest : BaseUnitTest() {
8497
8598 assertNull(result)
8699 }
100+
101+ @Test
102+ fun `loadActivity starts observation of activity changes` () = test {
103+ val activityId = " test-activity-1"
104+ val initialActivity = createTestActivity(activityId, confirmed = false )
105+ val updatedActivity = createTestActivity(activityId, confirmed = true )
106+ val activitiesChangedFlow = MutableStateFlow (System .currentTimeMillis())
107+
108+ whenever(activityRepo.activitiesChanged).thenReturn(activitiesChangedFlow)
109+ whenever(activityRepo.getActivity(activityId))
110+ .thenReturn(Result .success(initialActivity))
111+ whenever(activityRepo.getActivityTags(activityId))
112+ .thenReturn(Result .success(emptyList()))
113+
114+ // Load activity
115+ sut.loadActivity(activityId)
116+
117+ // Verify initial state loaded
118+ val initialState = sut.uiState.value.activityLoadState
119+ assertTrue(initialState is ActivityDetailViewModel .ActivityLoadState .Success )
120+ assertEquals(initialActivity, (initialState as ActivityDetailViewModel .ActivityLoadState .Success ).activity)
121+
122+ // Simulate activity update
123+ whenever(activityRepo.getActivity(activityId))
124+ .thenReturn(Result .success(updatedActivity))
125+ activitiesChangedFlow.value = System .currentTimeMillis()
126+
127+ // Verify ViewModel reflects updated activity
128+ val updatedState = sut.uiState.value.activityLoadState
129+ assertTrue(updatedState is ActivityDetailViewModel .ActivityLoadState .Success )
130+ assertEquals(updatedActivity, (updatedState as ActivityDetailViewModel .ActivityLoadState .Success ).activity)
131+ }
132+
133+ @Test
134+ fun `clearActivityState stops observation` () = test {
135+ val activityId = " test-activity-1"
136+ val activity = createTestActivity(activityId)
137+ val activitiesChangedFlow = MutableStateFlow (System .currentTimeMillis())
138+
139+ whenever(activityRepo.activitiesChanged).thenReturn(activitiesChangedFlow)
140+ whenever(activityRepo.getActivity(activityId))
141+ .thenReturn(Result .success(activity))
142+ whenever(activityRepo.getActivityTags(activityId))
143+ .thenReturn(Result .success(emptyList()))
144+
145+ // Load activity
146+ sut.loadActivity(activityId)
147+
148+ // Clear state
149+ sut.clearActivityState()
150+
151+ // Trigger activity change
152+ val callCountBefore = org.mockito.kotlin.mockingDetails(activityRepo).invocations.size
153+ activitiesChangedFlow.value = System .currentTimeMillis()
154+
155+ // Verify no reload after clear (getActivity not called again)
156+ val callCountAfter = org.mockito.kotlin.mockingDetails(activityRepo).invocations.size
157+ assertEquals(callCountBefore, callCountAfter)
158+ }
159+
160+ @Test
161+ fun `reloadActivity keeps last state on failure` () = test {
162+ val activityId = " test-activity-1"
163+ val activity = createTestActivity(activityId)
164+ val activitiesChangedFlow = MutableStateFlow (System .currentTimeMillis())
165+
166+ whenever(activityRepo.activitiesChanged).thenReturn(activitiesChangedFlow)
167+ whenever(activityRepo.getActivity(activityId))
168+ .thenReturn(Result .success(activity))
169+ whenever(activityRepo.getActivityTags(activityId))
170+ .thenReturn(Result .success(emptyList()))
171+
172+ // Load activity
173+ sut.loadActivity(activityId)
174+
175+ // Simulate reload failure
176+ whenever(activityRepo.getActivity(activityId))
177+ .thenReturn(Result .failure(Exception (" Network error" )))
178+ activitiesChangedFlow.value = System .currentTimeMillis()
179+
180+ // Verify last known state is preserved
181+ val state = sut.uiState.value.activityLoadState
182+ assertTrue(state is ActivityDetailViewModel .ActivityLoadState .Success )
183+ assertEquals(activity, (state as ActivityDetailViewModel .ActivityLoadState .Success ).activity)
184+ }
185+
186+ @Test
187+ fun `loadActivity handles error gracefully` () = test {
188+ val activityId = " test-activity-1"
189+
190+ whenever(activityRepo.getActivity(activityId))
191+ .thenReturn(Result .failure(Exception (" Database error" )))
192+
193+ sut.loadActivity(activityId)
194+
195+ val state = sut.uiState.value.activityLoadState
196+ assertTrue(state is ActivityDetailViewModel .ActivityLoadState .Error )
197+ }
198+
199+ private fun createTestActivity (
200+ id : String ,
201+ confirmed : Boolean = false,
202+ ): Activity .Onchain {
203+ return Activity .Onchain (
204+ v1 = OnchainActivity (
205+ id = id,
206+ txType = PaymentType .RECEIVED ,
207+ txId = " tx-$id " ,
208+ value = 100000UL ,
209+ fee = 500UL ,
210+ feeRate = 8UL ,
211+ address = " bc1..." ,
212+ confirmed = confirmed,
213+ timestamp = (System .currentTimeMillis() / 1000 ).toULong(),
214+ isBoosted = false ,
215+ boostTxIds = emptyList(),
216+ isTransfer = false ,
217+ doesExist = true ,
218+ confirmTimestamp = if (confirmed) (System .currentTimeMillis() / 1000 ).toULong() else null ,
219+ channelId = null ,
220+ transferTxId = null ,
221+ createdAt = null ,
222+ updatedAt = null ,
223+ )
224+ )
225+ }
87226}
0 commit comments