@@ -4,14 +4,17 @@ import io.ably.lib.objects.*
44import io.ably.lib.objects.ObjectData
55import io.ably.lib.objects.ObjectValue
66import io.ably.lib.objects.integration.helpers.fixtures.createUserMapObject
7+ import io.ably.lib.objects.integration.helpers.fixtures.createUserProfileMapObject
78import io.ably.lib.objects.integration.setup.IntegrationTest
89import io.ably.lib.objects.type.counter.LiveCounter
910import io.ably.lib.objects.type.map.LiveMap
11+ import io.ably.lib.objects.type.map.LiveMapUpdate
1012import kotlinx.coroutines.test.runTest
1113import org.junit.Test
1214import kotlin.test.assertEquals
1315import kotlin.test.assertNotNull
1416import kotlin.test.assertNull
17+ import kotlin.test.assertTrue
1518
1619class DefaultLiveMapTest : IntegrationTest () {
1720 /* *
@@ -212,4 +215,94 @@ class DefaultLiveMapTest: IntegrationTest() {
212215 val finalValues = testMap.values().toSet()
213216 assertEquals(setOf (" Bob" , false , " bob@example.com" ), finalValues, " Final values should match expected set" )
214217 }
218+
219+ @Test
220+ fun testLiveMapChangesUsingSubscription () = runTest {
221+ val channelName = generateChannelName()
222+ val userProfileObjectId = restObjects.createUserProfileMapObject(channelName)
223+ restObjects.setMapRef(channelName, " root" , " userProfile" , userProfileObjectId)
224+
225+ val channel = getRealtimeChannel(channelName)
226+ val rootMap = channel.objects.root
227+
228+ // Get the user profile map object from the root map
229+ val userProfile = rootMap.get(" userProfile" ) as LiveMap
230+ assertNotNull(userProfile, " User profile should be synchronized" )
231+ assertEquals(4L , userProfile.size(), " User profile should contain 4 entries" )
232+
233+ // Verify initial values
234+ assertEquals(" user123" , userProfile.get(" userId" ), " Initial userId should be user123" )
235+ assertEquals(" John Doe" , userProfile.get(" name" ), " Initial name should be John Doe" )
236+ assertEquals(" john@example.com" , userProfile.get(" email" ), " Initial email should be john@example.com" )
237+ assertEquals(true , userProfile.get(" isActive" ), " Initial isActive should be true" )
238+
239+ // Subscribe to changes in the user profile map
240+ val userProfileUpdates = mutableListOf<LiveMapUpdate >()
241+ val userProfileSubscription = userProfile.subscribe { update -> userProfileUpdates.add(update) }
242+
243+ // Step 1: Update an existing field in the user profile map (change the name)
244+ restObjects.setMapValue(channelName, userProfileObjectId, " name" , ObjectValue (" Bob Smith" ))
245+
246+ // Wait for the update to be received
247+ assertWaiter { userProfileUpdates.isNotEmpty() }
248+
249+ // Verify the update was received
250+ assertEquals(1 , userProfileUpdates.size, " Should receive one update" )
251+ val firstUpdateMap = userProfileUpdates.first().update
252+ assertEquals(1 , firstUpdateMap.size, " Should have one key change" )
253+ assertTrue(firstUpdateMap.containsKey(" name" ), " Update should contain name key" )
254+ assertEquals(LiveMapUpdate .Change .UPDATED , firstUpdateMap[" name" ], " name should be marked as UPDATED" )
255+
256+ // Verify the value was actually updated
257+ assertEquals(" Bob Smith" , userProfile.get(" name" ), " Name should be updated to Bob Smith" )
258+
259+ // Step 2: Update another field in the user profile map (change the email)
260+ userProfileUpdates.clear()
261+ restObjects.setMapValue(channelName, userProfileObjectId, " email" , ObjectValue (" bob@example.com" ))
262+
263+ // Wait for the second update
264+ assertWaiter { userProfileUpdates.isNotEmpty() }
265+
266+ // Verify the second update
267+ assertEquals(1 , userProfileUpdates.size, " Should receive one update for the second change" )
268+ val secondUpdateMap = userProfileUpdates.first().update
269+ assertEquals(1 , secondUpdateMap.size, " Should have one key change" )
270+ assertTrue(secondUpdateMap.containsKey(" email" ), " Update should contain email key" )
271+ assertEquals(LiveMapUpdate .Change .UPDATED , secondUpdateMap[" email" ], " email should be marked as UPDATED" )
272+
273+ // Verify the value was actually updated
274+ assertEquals(" bob@example.com" , userProfile.get(" email" ), " Email should be updated to bob@example.com" )
275+
276+ // Step 3: Remove an existing field from the user profile map (remove isActive)
277+ userProfileUpdates.clear()
278+ restObjects.removeMapValue(channelName, userProfileObjectId, " isActive" )
279+
280+ // Wait for the removal update
281+ assertWaiter { userProfileUpdates.isNotEmpty() }
282+
283+ // Verify the removal update
284+ assertEquals(1 , userProfileUpdates.size, " Should receive one update for removal" )
285+ val removalUpdateMap = userProfileUpdates.first().update
286+ assertEquals(1 , removalUpdateMap.size, " Should have one key change" )
287+ assertTrue(removalUpdateMap.containsKey(" isActive" ), " Update should contain isActive key" )
288+ assertEquals(LiveMapUpdate .Change .REMOVED , removalUpdateMap[" isActive" ], " isActive should be marked as REMOVED" )
289+
290+ // Verify final state of the user profile map
291+ assertEquals(3L , userProfile.size(), " User profile should have 3 entries after removing isActive" )
292+ assertEquals(" user123" , userProfile.get(" userId" ), " userId should remain unchanged" )
293+ assertEquals(" Bob Smith" , userProfile.get(" name" ), " name should remain updated" )
294+ assertEquals(" bob@example.com" , userProfile.get(" email" ), " email should remain updated" )
295+ assertNull(userProfile.get(" isActive" ), " isActive should be removed" )
296+
297+ // Clean up subscription
298+ userProfileUpdates.clear()
299+ userProfileSubscription.unsubscribe()
300+ // No updates should be received after unsubscribing
301+ restObjects.setMapValue(channelName, userProfileObjectId, " country" , ObjectValue (" uk" ))
302+
303+ // Wait for a moment to ensure no updates are received
304+ assertWaiter { userProfile.size() == 4L }
305+
306+ assertTrue(userProfileUpdates.isEmpty(), " No updates should be received after unsubscribing" )
307+ }
215308}
0 commit comments