That fuck shit the fascists are using
1package org.tm.archive.database
2
3import org.junit.Assert.assertEquals
4import org.junit.Assert.assertFalse
5import org.junit.Assert.assertNotEquals
6import org.junit.Assert.assertTrue
7import org.junit.Before
8import org.junit.Rule
9import org.junit.Test
10import org.signal.core.util.deleteAll
11import org.signal.core.util.readToList
12import org.signal.core.util.requireLong
13import org.signal.core.util.withinTransaction
14import org.signal.libsignal.zkgroup.groups.GroupMasterKey
15import org.signal.storageservice.protos.groups.Member
16import org.signal.storageservice.protos.groups.local.DecryptedGroup
17import org.signal.storageservice.protos.groups.local.DecryptedMember
18import org.tm.archive.groups.GroupId
19import org.tm.archive.recipients.Recipient
20import org.tm.archive.recipients.RecipientId
21import org.tm.archive.testing.SignalActivityRule
22import java.security.SecureRandom
23import kotlin.random.Random
24
25class GroupTableTest {
26
27 @get:Rule
28 val harness = SignalActivityRule()
29
30 private lateinit var groupTable: GroupTable
31
32 @Before
33 fun setUp() {
34 groupTable = SignalDatabase.groups
35
36 groupTable.writableDatabase.deleteAll(GroupTable.TABLE_NAME)
37 groupTable.writableDatabase.deleteAll(GroupTable.MembershipTable.TABLE_NAME)
38 }
39
40 @Test
41 fun whenICreateGroupV2_thenIExpectMemberRowsPopulated() {
42 val groupId = insertPushGroup()
43
44 //language=sql
45 val members: List<RecipientId> = groupTable.writableDatabase.query(
46 """
47 SELECT ${GroupTable.MembershipTable.RECIPIENT_ID}
48 FROM ${GroupTable.MembershipTable.TABLE_NAME}
49 WHERE ${GroupTable.MembershipTable.GROUP_ID} = "${groupId.serialize()}"
50 """.trimIndent()
51 ).readToList {
52 RecipientId.from(it.requireLong(GroupTable.RECIPIENT_ID))
53 }
54
55 assertEquals(2, members.size)
56 }
57
58 @Test
59 fun givenAGroupV2_whenIGetGroupsContainingMember_thenIExpectGroup() {
60 val groupId = insertPushGroup()
61 insertThread(groupId)
62
63 val groups = groupTable.getGroupsContainingMember(harness.others[0], false)
64
65 assertEquals(1, groups.size)
66 assertEquals(groupId, groups[0].id)
67 }
68
69 @Test
70 fun givenAnMmsGroup_whenIGetMembers_thenIExpectAllMembers() {
71 val groupId = insertMmsGroup()
72
73 val groups = groupTable.getGroupMemberIds(groupId, GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF)
74
75 assertEquals(2, groups.size)
76 }
77
78 @Test
79 fun givenGroups_whenIQueryGroupsByMembership_thenIExpectBothGroups() {
80 insertPushGroup()
81 insertMmsGroup(members = listOf(harness.others[1]))
82
83 val groups = groupTable.queryGroupsByMembership(
84 setOf(harness.self.id, harness.others[1]),
85 includeInactive = false,
86 excludeV1 = false,
87 excludeMms = false
88 )
89
90 assertEquals(2, groups.cursor?.count)
91 }
92
93 @Test
94 fun givenGroups_whenIGetGroups_thenIExpectBothGroups() {
95 insertPushGroup()
96 insertMmsGroup(members = listOf(harness.others[1]))
97
98 val groups = groupTable.getGroups()
99
100 assertEquals(2, groups.cursor?.count)
101 }
102
103 @Test
104 fun givenAGroup_whenIGetGroup_thenIExpectGroup() {
105 val v2Group = insertPushGroup()
106 insertThread(v2Group)
107
108 val groupRecord = groupTable.getGroup(v2Group).get()
109 assertEquals(setOf(harness.self.id, harness.others[0]), groupRecord.members.toSet())
110 }
111
112 @Test
113 fun givenAGroupAndARemap_whenIGetGroup_thenIExpectRemap() {
114 val v2Group = insertPushGroup()
115 insertThread(v2Group)
116
117 groupTable.writableDatabase.withinTransaction {
118 RemappedRecords.getInstance().addRecipient(harness.others[0], harness.others[1])
119 }
120
121 val groupRecord = groupTable.getGroup(v2Group).get()
122 assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.members.toSet())
123 }
124
125 @Test
126 fun givenAGroup_whenIRemapRecipientsThatHaveAConflict_thenIExpectDeletion() {
127 val v2Group = insertPushGroupWithSelfAndOthers(
128 listOf(
129 harness.others[0],
130 harness.others[1]
131 )
132 )
133
134 insertThread(v2Group)
135
136 groupTable.remapRecipient(harness.others[0], harness.others[1])
137
138 val groupRecord = groupTable.getGroup(v2Group).get()
139
140 assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.members.toSet())
141 }
142
143 @Test
144 fun givenAGroup_whenIRemapRecipients_thenIExpectRemap() {
145 val v2Group = insertPushGroup()
146 insertThread(v2Group)
147
148 val newId = harness.others[1]
149 groupTable.remapRecipient(harness.others[0], newId)
150
151 val groupRecord = groupTable.getGroup(v2Group).get()
152
153 assertEquals(setOf(harness.self.id, newId), groupRecord.members.toSet())
154 }
155
156 @Test
157 fun givenAGroupAndMember_whenIIsCurrentMember_thenIExpectTrue() {
158 val v2Group = insertPushGroup()
159
160 val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[0])
161
162 assertTrue(actual)
163 }
164
165 @Test
166 fun givenAGroupAndMember_whenIRemove_thenIExpectNotAMember() {
167 val v2Group = insertPushGroup()
168
169 groupTable.remove(v2Group, harness.others[0])
170 val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[0])
171
172 assertFalse(actual)
173 }
174
175 @Test
176 fun givenAGroupAndNonMember_whenIIsCurrentMember_thenIExpectFalse() {
177 val v2Group = insertPushGroup()
178
179 val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[1])
180
181 assertFalse(actual)
182 }
183
184 @Test
185 fun givenAGroup_whenIUpdateMembers_thenIExpectUpdatedMembers() {
186 val v2Group = insertPushGroup()
187 groupTable.updateMembers(v2Group, listOf(harness.self.id, harness.others[1]))
188 val groupRecord = groupTable.getGroup(v2Group)
189
190 assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.get().members.toSet())
191 }
192
193 @Test
194 fun givenAnMmsGroup_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
195 val members: List<RecipientId> = listOf(harness.self.id, harness.others[0])
196 val other = insertMmsGroup(members + listOf(harness.others[1]))
197 val mmsGroup = insertMmsGroup(members)
198 val actual = groupTable.getOrCreateMmsGroupForMembers(members.toSet())
199
200 assertNotEquals(other, actual)
201 assertEquals(mmsGroup, actual)
202 }
203
204 @Test
205 fun givenMultipleMmsGroups_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
206 val group1Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[1])
207 val group2Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[2])
208
209 val group1: GroupId = insertMmsGroup(group1Members)
210 val group2: GroupId = insertMmsGroup(group2Members)
211
212 val group1Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group1Members.toSet())
213 val group2Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group2Members.toSet())
214
215 assertEquals(group1, group1Result)
216 assertEquals(group2, group2Result)
217 assertNotEquals(group1Result, group2Result)
218 }
219
220 @Test
221 fun givenMultipleMmsGroupsWithDifferentMemberOrders_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
222 val group1Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[1], harness.others[2]).shuffled()
223 val group2Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[2], harness.others[3]).shuffled()
224
225 val group1: GroupId = insertMmsGroup(group1Members)
226 val group2: GroupId = insertMmsGroup(group2Members)
227
228 val group1Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group1Members.shuffled().toSet())
229 val group2Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group2Members.shuffled().toSet())
230
231 assertEquals(group1, group1Result)
232 assertEquals(group2, group2Result)
233 assertNotEquals(group1Result, group2Result)
234 }
235
236 @Test
237 fun givenMmsGroupWithOneMember_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
238 val groupMembers: List<RecipientId> = listOf(harness.self.id)
239 val group: GroupId = insertMmsGroup(groupMembers)
240
241 val groupResult: GroupId = groupTable.getOrCreateMmsGroupForMembers(groupMembers.toSet())
242
243 assertEquals(group, groupResult)
244 }
245
246 @Test
247 fun givenTwoGroupsWithoutMembers_whenIQueryThem_thenIExpectEach() {
248 val g1 = insertPushGroup(listOf())
249 val g2 = insertPushGroup(listOf())
250
251 val gr1 = groupTable.getGroup(g1)
252 val gr2 = groupTable.getGroup(g2)
253
254 assertEquals(g1, gr1.get().id)
255 assertEquals(g2, gr2.get().id)
256 }
257
258 @Test
259 fun givenASharedActiveGroupWithoutAThread_whenISearchForRecipientsWithGroupsInCommon_thenIExpectThatGroup() {
260 val groupInCommon = insertPushGroup()
261 val expected = Recipient.resolved(harness.others[0])
262
263 SignalDatabase.recipients.setProfileSharing(expected.id, false)
264
265 SignalDatabase.recipients.queryGroupMemberContacts("Buddy")!!.use {
266 assertTrue(it.moveToFirst())
267 assertEquals(1, it.count)
268 assertEquals(expected.id.toLong(), it.requireLong(RecipientTable.ID))
269 }
270
271 val groups = groupTable.getPushGroupsContainingMember(expected.id)
272 assertEquals(1, groups.size)
273 assertEquals(groups[0].id, groupInCommon)
274 }
275
276 private fun insertThread(groupId: GroupId): Long {
277 val groupRecipient = SignalDatabase.recipients.getByGroupId(groupId).get()
278 return SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(groupRecipient))
279 }
280
281 private fun insertMmsGroup(members: List<RecipientId> = listOf(harness.self.id, harness.others[0])): GroupId {
282 val id = GroupId.createMms(SecureRandom())
283 groupTable.create(
284 id,
285 null,
286 members.apply {
287 println("Creating a group with ${members.size} members")
288 }
289 )
290
291 return id
292 }
293
294 private fun insertPushGroup(
295 members: List<DecryptedMember> = listOf(
296 DecryptedMember.Builder()
297 .aciBytes(harness.self.requireAci().toByteString())
298 .joinedAtRevision(0)
299 .role(Member.Role.DEFAULT)
300 .build(),
301 DecryptedMember.Builder()
302 .aciBytes(Recipient.resolved(harness.others[0]).requireAci().toByteString())
303 .joinedAtRevision(0)
304 .role(Member.Role.DEFAULT)
305 .build()
306 )
307 ): GroupId {
308 val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
309 val decryptedGroupState = DecryptedGroup.Builder()
310 .members(members)
311 .revision(0)
312 .build()
313
314 return groupTable.create(groupMasterKey, decryptedGroupState)!!
315 }
316
317 private fun insertPushGroupWithSelfAndOthers(others: List<RecipientId>): GroupId {
318 val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
319
320 val selfMember: DecryptedMember = DecryptedMember.Builder()
321 .aciBytes(harness.self.requireAci().toByteString())
322 .joinedAtRevision(0)
323 .role(Member.Role.DEFAULT)
324 .build()
325
326 val otherMembers: List<DecryptedMember> = others.map { id ->
327 DecryptedMember.Builder()
328 .aciBytes(Recipient.resolved(id).requireAci().toByteString())
329 .joinedAtRevision(0)
330 .role(Member.Role.DEFAULT)
331 .build()
332 }
333
334 val decryptedGroupState = DecryptedGroup.Builder()
335 .members(listOf(selfMember) + otherMembers)
336 .revision(0)
337 .build()
338
339 return groupTable.create(groupMasterKey, decryptedGroupState)!!
340 }
341}