@@ -20,16 +20,20 @@ import io.customer.messaginginapp.gist.GistEnvironment
2020import io.customer.messaginginapp.gist.data.listeners.GistQueue
2121import io.customer.messaginginapp.gist.data.model.Message
2222import io.customer.messaginginapp.gist.data.model.MessagePosition
23+ import io.customer.messaginginapp.gist.utilities.ModalMessageExtras
2324import io.customer.messaginginapp.gist.utilities.ModalMessageParser
2425import io.customer.messaginginapp.state.InAppMessagingAction
2526import io.customer.messaginginapp.state.InAppMessagingManager
27+ import io.customer.messaginginapp.state.MessageBuilderMock
28+ import io.customer.messaginginapp.state.ModalMessageState
2629import io.customer.messaginginapp.testutils.core.IntegrationTest
2730import io.customer.sdk.core.di.SDKComponent
2831import io.customer.sdk.core.util.DispatchersProvider
2932import io.customer.sdk.core.util.Logger
3033import io.customer.sdk.core.util.ScopeProvider
3134import io.customer.sdk.data.model.Region
3235import io.mockk.coEvery
36+ import io.mockk.every
3337import io.mockk.mockk
3438import io.mockk.spyk
3539import io.mockk.verify
@@ -171,7 +175,129 @@ class GistModalActivityTest : IntegrationTest() {
171175 scenario.close()
172176 }
173177
174- private fun initializeModuleMessagingInApp () {
178+ // region onDestroy race condition prevention tests
179+
180+ @Test
181+ fun onDestroy_givenActivityMessageMatchesMessageInQueue_expectDismissDispatched () {
182+ // Initialize ModuleMessagingInApp
183+ val messagingManager = initializeModuleMessagingInApp()
184+ val testMessage = MessageBuilderMock .createMessage(
185+ queueId = " test-queue-id" ,
186+ persistent = false
187+ )
188+
189+ // Setup parser to return the test message
190+ coEvery { mockMessageParser.parseExtras(any()) } returns ModalMessageExtras (
191+ message = testMessage,
192+ messagePosition = MessagePosition .CENTER
193+ )
194+
195+ // Set the modal state to Displayed with the SAME message (matching queueId)
196+ every { messagingManager.getCurrentState() } returns mockk(relaxed = true ) {
197+ every { modalMessageState } returns ModalMessageState .Displayed (testMessage)
198+ }
199+
200+ val intent = createActivityIntent(testMessage)
201+ val scenario = ActivityScenario .launch<GistModalActivity >(intent)
202+ flushCoroutines(scopeProviderStub.inAppLifecycleScope)
203+
204+ // Destroy the activity
205+ scenario.moveToState(Lifecycle .State .DESTROYED )
206+
207+ // Verify dismiss was dispatched since the activity's message matches the one in queue
208+ assertCalledOnce {
209+ messagingManager.dispatch(
210+ match<InAppMessagingAction .DismissMessage > {
211+ it.message == testMessage && it.shouldLog
212+ }
213+ )
214+ }
215+
216+ scenario.close()
217+ }
218+
219+ @Test
220+ fun onDestroy_givenActivityMessageDifferentFromMessageInQueue_expectDismissNotDispatched () {
221+ // Initialize ModuleMessagingInApp
222+ val messagingManager = initializeModuleMessagingInApp()
223+ val activityMessage = MessageBuilderMock .createMessage(
224+ queueId = " activity-queue-id" ,
225+ persistent = false
226+ )
227+ val queueMessage = MessageBuilderMock .createMessage(
228+ queueId = " different-queue-id" ,
229+ persistent = false
230+ )
231+
232+ // Setup parser to return the activity's message
233+ coEvery { mockMessageParser.parseExtras(any()) } returns ModalMessageExtras (
234+ message = activityMessage,
235+ messagePosition = MessagePosition .CENTER
236+ )
237+
238+ // Set the modal state to Displayed with a DIFFERENT message (different queueId)
239+ // This simulates the race condition where Activity2 is already showing
240+ every { messagingManager.getCurrentState() } returns mockk(relaxed = true ) {
241+ every { modalMessageState } returns ModalMessageState .Displayed (queueMessage)
242+ }
243+
244+ val intent = createActivityIntent(activityMessage)
245+ val scenario = ActivityScenario .launch<GistModalActivity >(intent)
246+ flushCoroutines(scopeProviderStub.inAppLifecycleScope)
247+
248+ // Destroy the activity
249+ scenario.moveToState(Lifecycle .State .DESTROYED )
250+
251+ // Verify dismiss was NOT dispatched since the messages don't match (race condition prevention)
252+ verify(exactly = 0 ) {
253+ messagingManager.dispatch(match<InAppMessagingAction .DismissMessage > { true })
254+ }
255+
256+ scenario.close()
257+ }
258+
259+ @Test
260+ fun onDestroy_givenPersistentMessage_expectDismissDispatchedWithShouldLogFalse () {
261+ // Initialize ModuleMessagingInApp
262+ val messagingManager = initializeModuleMessagingInApp()
263+ val testMessage = MessageBuilderMock .createMessage(
264+ queueId = " test-queue-id" ,
265+ persistent = true // Persistent message
266+ )
267+
268+ // Setup parser to return the test message
269+ coEvery { mockMessageParser.parseExtras(any()) } returns ModalMessageExtras (
270+ message = testMessage,
271+ messagePosition = MessagePosition .CENTER
272+ )
273+
274+ // Set the modal state to Displayed with the SAME message
275+ every { messagingManager.getCurrentState() } returns mockk(relaxed = true ) {
276+ every { modalMessageState } returns ModalMessageState .Displayed (testMessage)
277+ }
278+
279+ val intent = createActivityIntent(testMessage)
280+ val scenario = ActivityScenario .launch<GistModalActivity >(intent)
281+ flushCoroutines(scopeProviderStub.inAppLifecycleScope)
282+
283+ // Destroy the activity
284+ scenario.moveToState(Lifecycle .State .DESTROYED )
285+
286+ // Verify dismiss was dispatched with shouldLog = false for persistent message
287+ assertCalledOnce {
288+ messagingManager.dispatch(
289+ match<InAppMessagingAction .DismissMessage > {
290+ it.message == testMessage && ! it.shouldLog
291+ }
292+ )
293+ }
294+
295+ scenario.close()
296+ }
297+
298+ // endregion
299+
300+ private fun initializeModuleMessagingInApp (): InAppMessagingManager {
175301 val moduleConfig = MessagingInAppModuleConfig .Builder (
176302 siteId = " test-site-id" ,
177303 region = Region .US
@@ -188,6 +314,8 @@ class GistModalActivityTest : IntegrationTest() {
188314 environment = GistEnvironment .LOCAL
189315 )
190316 ).flushCoroutines(scopeProviderStub.inAppLifecycleScope)
317+
318+ return messagingManager
191319 }
192320
193321 private fun createTestMessage (): Message = Message (
0 commit comments