From f2848d96eaa99a7a4c3c3ac8f6e8f5b310cbf86c Mon Sep 17 00:00:00 2001 From: marcin-cebo <102806110+marcin-cebo@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:29:47 +0100 Subject: [PATCH] Added test for storeUserActivityInterval (#142) --- gradle/libs.versions.toml | 2 +- .../kotlin/com/pubnub/chat/ThreadChannel.kt | 1 - .../pubnub/chat/config/ChatConfiguration.kt | 4 ++-- .../com/pubnub/chat/internal/ChatImpl.kt | 13 ++++++++----- .../pubnub/chat/internal/MembershipImpl.kt | 4 +++- .../chat/internal/channel/BaseChannel.kt | 5 +++-- .../pubnub/integration/ChatIntegrationTest.kt | 19 +++++++++++++------ .../kotlin/com/pubnub/kmp/ChatTest.kt | 4 ++-- .../compubnub/chat/ChatIntegrationTest.kt | 12 ++++++++++++ 9 files changed, 44 insertions(+), 20 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6e51627d..1be46100 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ ktlint = "12.1.0" dokka = "1.9.20" kotlinx_serialization = "1.7.3" kotlinx_coroutines = "1.9.0" -pubnub = "10.2.1" +pubnub = "10.3.0" [libraries] pubnub-kotlin-api = { module = "com.pubnub:pubnub-kotlin-api", version.ref = "pubnub" } diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/ThreadChannel.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/ThreadChannel.kt index fe31d83a..f28bfd86 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/ThreadChannel.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/ThreadChannel.kt @@ -25,7 +25,6 @@ interface ThreadChannel : Channel { * @return [PNFuture] containing [ThreadChannel] */ override fun pinMessage(message: Message): PNFuture - // TODO change parameter to ThreadMessage /** * Unpins the previously pinned thread message from the thread channel. diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt index 0cce4a37..11c6905c 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt @@ -25,13 +25,13 @@ interface ChatConfiguration { * to be set to true. The default value is set to 60 seconds, and that's the minimum possible value. * If you try to set it to a lower value, you'll get the storeUserActivityInterval must be at least 60000ms error. */ - val storeUserActivityInterval: Duration // todo do we have test for this? + val storeUserActivityInterval: Duration /** * Specifies if you want to track the user's global presence in your chat app. The user's activity is tracked * through [com.pubnub.chat.User.lastActiveTimestamp]. */ - val storeUserActivityTimestamps: Boolean // todo do we have test for this? + val storeUserActivityTimestamps: Boolean /** * List of parameters you must set if you want to enable sending/receiving mobile push notifications for phone diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt index cfc4bcc7..04ea75c6 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt @@ -665,10 +665,10 @@ class ChatImpl( val event = EventImpl( chat = this, - timetoken = pnEvent.timetoken!!, // todo can this even be null? + timetoken = pnEvent.timetoken!!, payload = payload, channelId = pnEvent.channel, - userId = pnEvent.publisher!! // todo can this even be null? + userId = pnEvent.publisher!! ) callback(event) } catch (e: Exception) { @@ -804,8 +804,9 @@ class ChatImpl( return@thenAsync emptyList().asFuture() } val channels = memberships.map { membership -> membership.channel.id } - val channelsTimetoken = - memberships.map { membership -> membership.lastReadMessageTimetoken ?: 0 } + + val channelsTimetoken = memberships.map { membership -> membership.lastReadMessageTimetoken ?: 0 } + pubNub.messageCounts(channels = channels, channelsTimetoken = channelsTimetoken) .then { pnMessageCountResult -> val unreadMessageCounts = @@ -853,7 +854,9 @@ class ChatImpl( val customMap: Map = buildMap { membership.custom?.let { putAll(it) } - put(METADATA_LAST_READ_MESSAGE_TIMETOKEN, relevantLastMessageTimeToken) + // toString is required because server for odd numbers larger than 9007199254740991(timetoken has 17 digits) + // returns values that differ by one + put(METADATA_LAST_READ_MESSAGE_TIMETOKEN, relevantLastMessageTimeToken.toString()) } PNChannelMembership.Partial( diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt index f3305698..aa3be150 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt @@ -67,7 +67,9 @@ data class MembershipImpl( override fun setLastReadMessageTimetoken(timetoken: Long): PNFuture { val newCustom = buildMap { custom?.let { putAll(it) } - put(METADATA_LAST_READ_MESSAGE_TIMETOKEN, timetoken) + // toString is required because server for odd numbers larger than 9007199254740991(timetoken has 17 digits) + // returns values that differ by one + put(METADATA_LAST_READ_MESSAGE_TIMETOKEN, timetoken.toString()) } return update(createCustomObject(newCustom)).alsoAsync { val canISendSignal = AccessManager(chat).canI( diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt index fd1d4e73..bc8aa0bd 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt @@ -505,7 +505,7 @@ abstract class BaseChannel( } // there is a discrepancy between KMP and JS. There is no unsubscribe here. This is agreed and will be changed in JS Chat - override fun leave(): PNFuture = chat.pubNub.removeMemberships(channels = listOf(id)).then { Unit } + override fun leave(): PNFuture = chat.pubNub.removeMemberships(channels = listOf(id), includeType = false).then { Unit } override fun getPinnedMessage(): PNFuture { val pinnedMessageTimetoken = this.custom?.get(PINNED_MESSAGE_TIMETOKEN).tryLong() ?: return null.asFuture() @@ -600,7 +600,8 @@ abstract class BaseChannel( log.pnError(READ_RECEIPTS_ARE_NOT_SUPPORTED_IN_PUBLIC_CHATS) } val timetokensPerUser = mutableMapOf() - val future = getMembers().then { members -> // todo what about paging? maybe not needed in non-public chats... + // in group chats it work till 100 members + val future = getMembers().then { members -> members.members.forEach { m -> val lastTimetoken = m.custom?.get(METADATA_LAST_READ_MESSAGE_TIMETOKEN)?.tryLong() if (lastTimetoken != null) { diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt index 4e77dd5d..8530bcea 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt @@ -45,7 +45,6 @@ import delayForHistory import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import tryLong -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -66,6 +65,16 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { assertEquals(403, exception.statusCode) } + @Test + fun test_storeUserActivityInterval_and_storeUserActivityTimestamps() = runTest { + chat = ChatImpl(ChatConfiguration(storeUserActivityInterval = 100.seconds, storeUserActivityTimestamps = true), pubnub) + chat.initialize().await() + + val user: User = chat.getUser(chat.currentUser.id).await()!! + + assertTrue(user.custom?.get("lastActiveTimestamp") != null) + } + @Test fun initializeShouldPassWhenPamEnableAndTokenProvided() = runTest { if (PLATFORM == "iOS") { @@ -187,7 +196,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { val channelId02 = channel02.id val membership01: ChannelMembershipInput = PNChannelMembership.Partial(channelId01, custom) val membership02: ChannelMembershipInput = PNChannelMembership.Partial(channelId02) - chat.pubNub.setMemberships(listOf(membership01, membership02), chat.currentUser.id).await() + chat.pubNub.setMemberships(channels = listOf(membership01, membership02), uuid = chat.currentUser.id, includeType = false).await() // to each channel add two messages(we want to check if last message will be taken by fetchMessages with limit = 1) channel01.sendText("message01In$channelId01").await() @@ -272,7 +281,6 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { } } - @Ignore // fails from time to time @Test fun can_getUnreadMessagesCount_onMembership() = runTest { val channelId01 = channel01.id @@ -288,7 +296,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { // send message channel01.sendText("message02In$channelId01").await() - delayInMillis(150) // history calls have around 130ms of cache time + delayForHistory() val unreadMessageCount02: Long? = membership.getUnreadMessagesCount().await() assertEquals(1L, unreadMessageCount02) @@ -306,7 +314,6 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { chat.pubNub.deleteMessages(listOf(channelId01)).await() } - @Ignore // fails from time to time @Test fun can_getUnreadMessageCounts_global() = runTest { val channelId01 = channel01.id @@ -350,7 +357,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { }?.count ?: 0 assertEquals(0, unreadMessagesCountsForChannel01) - assertEquals(0, unreadMessagesCountsForChannel02) // todo when run in set sometimes fails :/ + assertEquals(0, unreadMessagesCountsForChannel02) // remove messages chat.pubNub.deleteMessages(listOf(channelId01, channelId02)) diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt index 677cd5d3..3e56e13a 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt @@ -1331,7 +1331,7 @@ class ChatTest : BaseTest() { val mute = false val pnMemberArrayResult = PNMemberArrayResult( status = 200, - data = listOf(PNMember(PNUUIDMetadata(id = userId), null, "", "", null)), + data = listOf(PNMember(PNUUIDMetadata(id = userId), null, "", "", null, null)), 1, null, null @@ -1389,7 +1389,7 @@ class ChatTest : BaseTest() { val reason = "He rehabilitated" val pnMemberArrayResult = PNMemberArrayResult( status = 200, - data = listOf(PNMember(PNUUIDMetadata(id = userId), null, "", "", null)), + data = listOf(PNMember(PNUUIDMetadata(id = userId), null, "", "", null, null)), 1, null, null diff --git a/src/jvmTest/kotlin/compubnub/chat/ChatIntegrationTest.kt b/src/jvmTest/kotlin/compubnub/chat/ChatIntegrationTest.kt index 769433d9..cf859fef 100644 --- a/src/jvmTest/kotlin/compubnub/chat/ChatIntegrationTest.kt +++ b/src/jvmTest/kotlin/compubnub/chat/ChatIntegrationTest.kt @@ -22,9 +22,11 @@ import com.pubnub.test.Keys import com.pubnub.test.await import com.pubnub.test.randomString import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertTrue import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith +import kotlin.time.Duration.Companion.seconds class ChatIntegrationTest : BaseIntegrationTest() { @Test @@ -42,6 +44,16 @@ class ChatIntegrationTest : BaseIntegrationTest() { } } + @Test + fun test_storeUserActivityInterval_and_storeUserActivityTimestamps() = runTest { + val chatConfig = ChatConfiguration(storeUserActivityInterval = 100.seconds, storeUserActivityTimestamps = true) + + val chat: Chat = Chat.init(chatConfig, config).await() + val user = chat.getUser(chat.currentUser.id).await() + + assertTrue(user?.custom?.get("lastActiveTimestamp") != null) + } + @Test fun shouldReceiveExceptionWhenInitializingChatWithoutValidTokenWhenPamEnabled() = runTest { val exception: PubNubException = assertFailsWith {