GroupCreateTaskTest.kt 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /* _____ _
  2. * |_ _| |_ _ _ ___ ___ _ __ __ _
  3. * | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. * |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. *
  6. * Threema for Android
  7. * Copyright (c) 2024-2025 Threema GmbH
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. package ch.threema.app.tasks
  22. import ch.threema.app.DangerousTest
  23. import ch.threema.app.TestMultiDeviceManager
  24. import ch.threema.app.ThreemaApplication
  25. import ch.threema.app.protocol.ExpectedProfilePictureChange
  26. import ch.threema.app.protocol.PredefinedMessageIds
  27. import ch.threema.app.testutils.TestHelpers
  28. import ch.threema.app.testutils.TestHelpers.TestContact
  29. import ch.threema.app.testutils.clearDatabaseAndCaches
  30. import ch.threema.app.utils.OutgoingCspMessageServices
  31. import ch.threema.data.models.ContactModelData
  32. import ch.threema.data.models.GroupIdentity
  33. import ch.threema.data.models.GroupModelData
  34. import ch.threema.data.repositories.GroupCreateException
  35. import ch.threema.domain.helpers.TransactionAckTaskCodec
  36. import ch.threema.domain.models.ContactSyncState
  37. import ch.threema.domain.models.IdentityState
  38. import ch.threema.domain.models.IdentityType
  39. import ch.threema.domain.models.ReadReceiptPolicy
  40. import ch.threema.domain.models.TypingIndicatorPolicy
  41. import ch.threema.domain.models.VerificationLevel
  42. import ch.threema.domain.models.WorkVerificationLevel
  43. import ch.threema.domain.protocol.connection.data.CspMessage
  44. import ch.threema.domain.protocol.connection.data.OutboundD2mMessage
  45. import ch.threema.storage.models.ContactModel
  46. import ch.threema.storage.models.GroupModel
  47. import io.mockk.mockk
  48. import java.util.Date
  49. import kotlin.test.BeforeTest
  50. import kotlin.test.Test
  51. import kotlin.test.assertEquals
  52. import kotlin.test.assertIs
  53. import kotlinx.coroutines.test.runTest
  54. @DangerousTest
  55. class GroupCreateTaskTest {
  56. private val myContact: TestContact = TestHelpers.TEST_CONTACT
  57. private val initialContactModelData = ContactModelData(
  58. identity = "12345678",
  59. publicKey = ByteArray(32),
  60. createdAt = Date(),
  61. firstName = "",
  62. lastName = "",
  63. verificationLevel = VerificationLevel.SERVER_VERIFIED,
  64. workVerificationLevel = WorkVerificationLevel.NONE,
  65. nickname = null,
  66. identityType = IdentityType.NORMAL,
  67. acquaintanceLevel = ContactModel.AcquaintanceLevel.DIRECT,
  68. activityState = IdentityState.ACTIVE,
  69. syncState = ContactSyncState.INITIAL,
  70. featureMask = 255u,
  71. readReceiptPolicy = ReadReceiptPolicy.DEFAULT,
  72. typingIndicatorPolicy = TypingIndicatorPolicy.DEFAULT,
  73. isArchived = false,
  74. profilePictureBlobId = null,
  75. androidContactLookupInfo = null,
  76. localAvatarExpires = null,
  77. isRestored = false,
  78. jobTitle = null,
  79. department = null,
  80. notificationTriggerPolicyOverride = null,
  81. )
  82. private val serviceManager by lazy { ThreemaApplication.requireServiceManager() }
  83. private val testMultiDeviceManagerMdEnabled by lazy {
  84. TestMultiDeviceManager(
  85. isMdDisabledOrSupportsFs = false,
  86. isMultiDeviceActive = true,
  87. )
  88. }
  89. private val testMultiDeviceManagerMdDisabled by lazy {
  90. TestMultiDeviceManager(
  91. isMdDisabledOrSupportsFs = true,
  92. isMultiDeviceActive = false,
  93. )
  94. }
  95. @BeforeTest
  96. fun setup() {
  97. clearDatabaseAndCaches(serviceManager)
  98. assert(myContact.identity == TestHelpers.ensureIdentity(serviceManager))
  99. // Note that we use from sync to prevent any reflection. This is only acceptable in tests.
  100. serviceManager.modelRepositories.contacts.createFromSync(initialContactModelData)
  101. // Note that we use from sync to prevent any reflection. This is only acceptable in tests.
  102. try {
  103. val now = Date()
  104. serviceManager.modelRepositories.groups.createFromSync(
  105. GroupModelData(
  106. groupIdentity = GroupIdentity(myContact.identity, 42),
  107. name = "My Group",
  108. createdAt = now,
  109. synchronizedAt = null,
  110. lastUpdate = now,
  111. isArchived = false,
  112. userState = GroupModel.UserState.MEMBER,
  113. otherMembers = setOf(initialContactModelData.identity),
  114. groupDescription = null,
  115. groupDescriptionChangedAt = null,
  116. notificationTriggerPolicyOverride = null,
  117. ),
  118. )
  119. } catch (_: GroupCreateException) {
  120. // Ignore
  121. }
  122. }
  123. @Test
  124. fun testSimpleGroupMd() = runTest {
  125. val predefinedMessageIds = PredefinedMessageIds.random()
  126. val groupCreateTask = GroupCreateTask(
  127. name = "My Group",
  128. expectedProfilePictureChange = ExpectedProfilePictureChange.Remove,
  129. members = setOf(initialContactModelData.identity),
  130. groupIdentity = GroupIdentity(myContact.identity, 42),
  131. predefinedMessageIds = predefinedMessageIds,
  132. outgoingCspMessageServices = getOutgoingCspMessageServicesMd(),
  133. groupCallManager = serviceManager.groupCallManager,
  134. fileService = serviceManager.fileService,
  135. groupProfilePictureUploader = mockk(),
  136. groupModelRepository = serviceManager.modelRepositories.groups,
  137. )
  138. val handle = TransactionAckTaskCodec()
  139. groupCreateTask.invoke(handle)
  140. assertEquals(1, handle.transactionBeginCount)
  141. assertEquals(1, handle.transactionCommitCount)
  142. handle.outboundMessages.apply {
  143. // The transaction start
  144. assertIs<OutboundD2mMessage.BeginTransaction>(get(0))
  145. // The reflected csp messages
  146. assertIs<OutboundD2mMessage.Reflect>(get(1))
  147. assertIs<OutboundD2mMessage.Reflect>(get(2))
  148. assertIs<OutboundD2mMessage.Reflect>(get(3))
  149. // The sent csp messages
  150. assertIs<CspMessage>(get(4))
  151. assertIs<CspMessage>(get(5))
  152. assertIs<CspMessage>(get(6))
  153. // The transaction end
  154. assertIs<OutboundD2mMessage.CommitTransaction>(get(7))
  155. // Assert that there are no more messages
  156. assertEquals(8, size)
  157. }
  158. }
  159. @Test
  160. fun testSimpleGroupNonMd() = runTest {
  161. val predefinedMessageIds = PredefinedMessageIds.random()
  162. val groupCreateTask = GroupCreateTask(
  163. name = "My Group",
  164. expectedProfilePictureChange = ExpectedProfilePictureChange.Remove,
  165. members = setOf("12345678"),
  166. groupIdentity = GroupIdentity(myContact.identity, 42),
  167. predefinedMessageIds = predefinedMessageIds,
  168. outgoingCspMessageServices = getOutgoingCspMessageServicesNonMd(),
  169. groupCallManager = serviceManager.groupCallManager,
  170. fileService = serviceManager.fileService,
  171. groupProfilePictureUploader = mockk(),
  172. groupModelRepository = serviceManager.modelRepositories.groups,
  173. )
  174. val handle = TransactionAckTaskCodec()
  175. groupCreateTask.invoke(handle)
  176. assertEquals(0, handle.transactionBeginCount)
  177. assertEquals(0, handle.transactionCommitCount)
  178. handle.outboundMessages.apply {
  179. assertEquals(6, size)
  180. // Empty message and group setup message
  181. assertIs<CspMessage>(get(0))
  182. assertIs<CspMessage>(get(1))
  183. // Empty message and group name message
  184. assertIs<CspMessage>(get(2))
  185. assertIs<CspMessage>(get(3))
  186. // Empty message and group delete profile picture message
  187. assertIs<CspMessage>(get(4))
  188. assertIs<CspMessage>(get(5))
  189. }
  190. }
  191. private fun getOutgoingCspMessageServicesMd() = OutgoingCspMessageServices(
  192. forwardSecurityMessageProcessor = serviceManager.forwardSecurityMessageProcessor,
  193. identityStore = serviceManager.identityStore,
  194. userService = serviceManager.userService,
  195. contactStore = serviceManager.contactStore,
  196. contactService = serviceManager.contactService,
  197. contactModelRepository = serviceManager.modelRepositories.contacts,
  198. groupService = serviceManager.groupService,
  199. nonceFactory = serviceManager.nonceFactory,
  200. blockedIdentitiesService = serviceManager.blockedIdentitiesService,
  201. preferenceService = serviceManager.preferenceService,
  202. multiDeviceManager = testMultiDeviceManagerMdEnabled,
  203. ).apply {
  204. forwardSecurityMessageProcessor.setForwardSecurityEnabled(false)
  205. }
  206. private fun getOutgoingCspMessageServicesNonMd() = OutgoingCspMessageServices(
  207. forwardSecurityMessageProcessor = serviceManager.forwardSecurityMessageProcessor,
  208. identityStore = serviceManager.identityStore,
  209. userService = serviceManager.userService,
  210. contactStore = serviceManager.contactStore,
  211. contactService = serviceManager.contactService,
  212. contactModelRepository = serviceManager.modelRepositories.contacts,
  213. groupService = serviceManager.groupService,
  214. nonceFactory = serviceManager.nonceFactory,
  215. blockedIdentitiesService = serviceManager.blockedIdentitiesService,
  216. preferenceService = serviceManager.preferenceService,
  217. multiDeviceManager = testMultiDeviceManagerMdDisabled,
  218. ).apply {
  219. forwardSecurityMessageProcessor.setForwardSecurityEnabled(true)
  220. }
  221. }