From a268a1d96e971d679eaeb39b00488742523147ef Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Tue, 24 Feb 2026 12:55:43 +0100 Subject: [PATCH 1/2] Fix delete message not working when offline (SYNC_NEEDED) Co-Authored-By: Claude --- .../client/utils/message/MessageUtils.kt | 3 +-- .../client/utils/message/MessageUtilsTest.kt | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/utils/message/MessageUtils.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/utils/message/MessageUtils.kt index 03be42f9c58..c5c75c54c09 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/utils/message/MessageUtils.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/utils/message/MessageUtils.kt @@ -230,10 +230,9 @@ public fun Message.shouldDeleteRemote(currentUserId: String?): Result { return Result.Failure(error) } // 2. type = 'error'/'ephemeral' - not persisted on server, delete only locally - // 3. syncStatus = 'IN_PROGRESS'/`SYNC_NEEDED`/`FAILED_PERMANENTLY` - not persisted on server, delete only locally + // 3. syncStatus = 'IN_PROGRESS'/`FAILED_PERMANENTLY` - not persisted on server, delete only locally if (isError() || isEphemeral() || syncStatus == SyncStatus.IN_PROGRESS || - syncStatus == SyncStatus.SYNC_NEEDED || syncStatus == SyncStatus.FAILED_PERMANENTLY ) { val error = Error.GenericError("Message is local-only, don't call DeleteMessage API") diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/utils/message/MessageUtilsTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/utils/message/MessageUtilsTest.kt index 9e52d4d795a..3fb8c3af7ee 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/utils/message/MessageUtilsTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/utils/message/MessageUtilsTest.kt @@ -629,40 +629,40 @@ internal class MessageUtilsTest { } @Test - fun `shouldDeleteRemote should return Failure for SYNC_NEEDED sync status`() { + fun `shouldDeleteRemote should return Failure for FAILED_PERMANENTLY sync status`() { val message = randomMessage( type = MessageType.REGULAR, - syncStatus = SyncStatus.SYNC_NEEDED, + syncStatus = SyncStatus.FAILED_PERMANENTLY, ) val result = message.shouldDeleteRemote(randomString()) assertTrue(result is Result.Failure) } @Test - fun `shouldDeleteRemote should return Failure for FAILED_PERMANENTLY sync status`() { + fun `shouldDeleteRemote should return Success for COMPLETED regular message`() { val message = randomMessage( type = MessageType.REGULAR, - syncStatus = SyncStatus.FAILED_PERMANENTLY, + syncStatus = SyncStatus.COMPLETED, ) val result = message.shouldDeleteRemote(randomString()) - assertTrue(result is Result.Failure) + assertTrue(result is Result.Success) } @Test - fun `shouldDeleteRemote should return Success for COMPLETED regular message`() { + fun `shouldDeleteRemote should return Success for AWAITING_ATTACHMENTS regular message`() { val message = randomMessage( type = MessageType.REGULAR, - syncStatus = SyncStatus.COMPLETED, + syncStatus = SyncStatus.AWAITING_ATTACHMENTS, ) val result = message.shouldDeleteRemote(randomString()) assertTrue(result is Result.Success) } @Test - fun `shouldDeleteRemote should return Success for AWAITING_ATTACHMENTS regular message`() { + fun `shouldDeleteRemote should return Success for SYNC_NEEDED sync status`() { val message = randomMessage( type = MessageType.REGULAR, - syncStatus = SyncStatus.AWAITING_ATTACHMENTS, + syncStatus = SyncStatus.SYNC_NEEDED, ) val result = message.shouldDeleteRemote(randomString()) assertTrue(result is Result.Success) From 103ac4c8108b98db2b94477a53721247bcc0437a Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Tue, 24 Feb 2026 13:14:22 +0100 Subject: [PATCH 2/2] Update DeleteMessageListener tests for SYNC_NEEDED remote delete Co-Authored-By: Claude --- .../internal/DeleteMessageListenerDatabaseTest.kt | 5 ++--- .../listener/internal/DeleteMessageListenerStateTest.kt | 8 ++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/plugin/listener/internal/DeleteMessageListenerDatabaseTest.kt b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/plugin/listener/internal/DeleteMessageListenerDatabaseTest.kt index 0da7cd80b34..804dfeb4b4f 100644 --- a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/plugin/listener/internal/DeleteMessageListenerDatabaseTest.kt +++ b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/plugin/listener/internal/DeleteMessageListenerDatabaseTest.kt @@ -209,7 +209,7 @@ internal class DeleteMessageListenerDatabaseTest { } @Test - fun `onMessageDeletePrecondition when message has SYNC_NEEDED should return Failure and delete from repo`() = + fun `onMessageDeletePrecondition when message has SYNC_NEEDED should return Success`() = runTest { val currentUser = randomUser() val testMessage = randomMessage( @@ -224,8 +224,7 @@ internal class DeleteMessageListenerDatabaseTest { val result = deleteMessageListenerState.onMessageDeletePrecondition(testMessage.id) - assertTrue(result is Result.Failure) - verify(messageRepository).deleteChannelMessage(argThat { id == testMessage.id }) + assertTrue(result is Result.Success) } @Test diff --git a/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt b/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt index 0440575acdd..e2ee17bdaaa 100644 --- a/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt +++ b/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt @@ -252,7 +252,7 @@ internal class DeleteMessageListenerStateTest { } @Test - fun `onMessageDeletePrecondition when message has SYNC_NEEDED should return Failure and delete locally`() = + fun `onMessageDeletePrecondition when message has SYNC_NEEDED should return Success`() = runTest { val testMessage = randomMessage( id = "msg-1", @@ -262,15 +262,11 @@ internal class DeleteMessageListenerStateTest { ) whenever(logicRegistry.channelFromMessageId(any())) doReturn channelLogic - whenever(logicRegistry.channelFromMessage(any())) doReturn channelLogic - whenever(logicRegistry.getActiveQueryThreadsLogic()) doReturn activeThreadsLogic - whenever(logicRegistry.threadFromMessage(any())) doReturn null whenever(channelLogic.getMessage(any())) doReturn testMessage val result = deleteMessageListenerState.onMessageDeletePrecondition(testMessage.id) - assertTrue(result is Result.Failure) - verify(channelLogic).deleteMessage(argThat { id == testMessage.id }) + assertTrue(result is Result.Success) } @Test