11package io.ably.lib.objects.unit.objects
22
33import io.ably.lib.objects.DefaultLiveObjects
4+ import io.ably.lib.objects.ObjectData
45import io.ably.lib.objects.ROOT_OBJECT_ID
56import io.ably.lib.objects.type.livecounter.DefaultLiveCounter
67import io.ably.lib.objects.type.livemap.DefaultLiveMap
8+ import io.ably.lib.objects.type.livemap.LiveMapEntry
9+ import io.ably.lib.objects.unit.*
710import io.mockk.mockk
11+ import io.mockk.spyk
812import org.junit.Test
913import kotlin.test.assertEquals
1014import kotlin.test.assertNotNull
15+ import kotlin.test.assertNull
1116import kotlin.test.assertTrue
1217
1318class ObjectsPoolTest {
@@ -23,6 +28,8 @@ class ObjectsPoolTest {
2328 assertNotNull(rootLiveMap)
2429 assertTrue(rootLiveMap is DefaultLiveMap )
2530 assertTrue(rootLiveMap.data.isEmpty())
31+ assertEquals(ROOT_OBJECT_ID , rootLiveMap.objectId)
32+ assertEquals(1 , objectsPool.size(), " RTO3 - Should only contain the root object initially" )
2633
2734 // RTO3a - ObjectsPool is a Dict, a map of LiveObjects keyed by objectId string
2835 val testLiveMap = DefaultLiveMap (" testObjectId" , mockk(relaxed = true ), objectsPool)
@@ -32,5 +39,94 @@ class ObjectsPoolTest {
3239 // Assert that the objects are stored in the pool
3340 assertEquals(testLiveMap, objectsPool.get(" testObjectId" ))
3441 assertEquals(testLiveCounter, objectsPool.get(" testCounterId" ))
42+ assertEquals(3 , objectsPool.size(), " RTO3 - Should have 3 objects in pool (root + testLiveMap + testLiveCounter)" )
43+ }
44+
45+ @Test
46+ fun `(RTO6) ObjectsPool should create zero-value objects if not exists` () {
47+ val defaultLiveObjects = DefaultLiveObjects (" dummyChannel" , mockk(relaxed = true ))
48+ val objectsPool = spyk(defaultLiveObjects.objectsPool)
49+ assertEquals(1 , objectsPool.size(), " RTO3 - Should only contain the root object initially" )
50+
51+ // Test creating zero-value map
52+ // RTO6b1, RTO6b2 - Type is parsed from the objectId format (map:hash@timestamp)
53+ val mapId = " map:xyz789@67890"
54+ val map = objectsPool.createZeroValueObjectIfNotExists(mapId)
55+ assertNotNull(map, " Should create a map object" )
56+ assertTrue(map is DefaultLiveMap , " RTO6b2 - Should create a LiveMap for map type" )
57+ assertEquals(mapId, map.objectId)
58+ assertTrue(map.data.isEmpty(), " RTO6b2 - Should create an empty map" )
59+ assertEquals(2 , objectsPool.size(), " RTO6 - root + map should be in pool after creation" )
60+
61+ // Test creating zero-value counter
62+ // RTO6b1, RTO6b3 - Type is parsed from the objectId format (counter:hash@timestamp)
63+ val counterId = " counter:abc123@12345"
64+ val counter = objectsPool.createZeroValueObjectIfNotExists(counterId)
65+ assertNotNull(counter, " Should create a counter object" )
66+ assertTrue(counter is DefaultLiveCounter , " RTO6b3 - Should create a LiveCounter for counter type" )
67+ assertEquals(counterId, counter.objectId)
68+ assertEquals(0L , counter.data, " RTO6b3 - Should create a zero-value counter" )
69+ assertEquals(3 , objectsPool.size(), " RTO6 - root + map + counter should be in pool after creation" )
70+
71+ // RTO6a - If object exists in pool, do not create a new one
72+ val existingMap = objectsPool.createZeroValueObjectIfNotExists(mapId)
73+ assertEquals(map, existingMap, " RTO6a - Should return existing object, not create a new one" )
74+ val existingCounter = objectsPool.createZeroValueObjectIfNotExists(counterId)
75+ assertEquals(counter, existingCounter, " RTO6a - Should return existing object, not create a new one" )
76+ assertEquals(3 , objectsPool.size(), " RTO6 - Should still have 3 objects in pool after re-creation attempt" )
77+ }
78+
79+ @Test
80+ fun `(RTO4b1, RTO4b2) ObjectsPool should reset to initial pool retaining original root map` () {
81+ val defaultLiveObjects = DefaultLiveObjects (" dummyChannel" , mockk(relaxed = true ))
82+ val objectsPool = defaultLiveObjects.objectsPool
83+ assertEquals(1 , objectsPool.size())
84+ val rootMap = objectsPool.get(ROOT_OBJECT_ID ) as DefaultLiveMap
85+ // add some data to the root map
86+ rootMap.data[" initialKey1" ] = LiveMapEntry (data = ObjectData (" testValue1" ))
87+ rootMap.data[" initialKey2" ] = LiveMapEntry (data = ObjectData (" testValue2" ))
88+ assertEquals(2 , rootMap.data.size, " RTO3 - Root map should have initial data" )
89+
90+ // Add some objects
91+ objectsPool.set(" testObjectId" , DefaultLiveCounter (" testObjectId" , mockk(relaxed = true )))
92+ assertEquals(2 , objectsPool.size()) // root + testObject
93+ objectsPool.set(" anotherObjectId" , DefaultLiveCounter (" anotherObjectId" , mockk(relaxed = true )))
94+ assertEquals(3 , objectsPool.size()) // root + testObject + anotherObject
95+ objectsPool.set(" testMapId" , DefaultLiveMap (" testMapId" , mockk(relaxed = true ), objectsPool))
96+ assertEquals(4 , objectsPool.size()) // root + testObject + anotherObject + testMap
97+
98+ // Reset to initial pool
99+ objectsPool.resetToInitialPool(true )
100+
101+ // RTO4b1 - Should only contain root object
102+ assertEquals(1 , objectsPool.size())
103+ assertEquals(rootMap, objectsPool.get(ROOT_OBJECT_ID ))
104+ // RTO4b2 - RootMap should be empty after reset
105+ assertTrue(rootMap.data.isEmpty(), " RTO3 - Root map should be empty after reset" )
106+ }
107+
108+ @Test
109+ fun `(RTO5c2, RTO5c2a) ObjectsPool should delete extra object IDs` () {
110+ val defaultLiveObjects = DefaultLiveObjects (" dummyChannel" , mockk(relaxed = true ))
111+ val objectsPool = defaultLiveObjects.objectsPool
112+
113+ // Add some objects
114+ objectsPool.set(" object1" , DefaultLiveCounter (" object1" , mockk(relaxed = true )))
115+ objectsPool.set(" object2" , DefaultLiveCounter (" object2" , mockk(relaxed = true )))
116+ objectsPool.set(" object3" , DefaultLiveCounter (" object3" , mockk(relaxed = true )))
117+ assertEquals(4 , objectsPool.size()) // root + 3 objects
118+
119+ // Delete extra object IDs (keep only object1 and object2)
120+ val receivedObjectIds = mutableSetOf (" object1" , " object2" )
121+ objectsPool.deleteExtraObjectIds(receivedObjectIds)
122+
123+ // Should only contain root, object1, and object2
124+ assertEquals(3 , objectsPool.size())
125+ // RTO5c2a - Should keep the root object
126+ assertNotNull(objectsPool.get(ROOT_OBJECT_ID ))
127+ // RTO5c2 - Should delete object3 and keep object1 and object2
128+ assertNotNull(objectsPool.get(" object1" ))
129+ assertNotNull(objectsPool.get(" object2" ))
130+ assertNull(objectsPool.get(" object3" )) // Should be deleted
35131 }
36132}
0 commit comments