kopia lustrzana https://github.com/ryukoposting/Signal-Android
560 wiersze
24 KiB
Kotlin
560 wiersze
24 KiB
Kotlin
package org.thoughtcrime.securesms.database
|
||
|
||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||
import org.junit.Assert.assertEquals
|
||
import org.junit.Assert.assertFalse
|
||
import org.junit.Assert.assertNotEquals
|
||
import org.junit.Assert.assertTrue
|
||
import org.junit.Before
|
||
import org.junit.Test
|
||
import org.junit.runner.RunWith
|
||
import org.signal.core.util.ThreadUtil
|
||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||
import org.thoughtcrime.securesms.jobs.RecipientChangedNumberJob
|
||
import org.thoughtcrime.securesms.keyvalue.AccountValues
|
||
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet
|
||
import org.thoughtcrime.securesms.keyvalue.KeyValueStore
|
||
import org.thoughtcrime.securesms.keyvalue.MockKeyValuePersistentStorage
|
||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||
import org.thoughtcrime.securesms.recipients.Recipient
|
||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||
import org.whispersystems.signalservice.api.push.ACI
|
||
import org.whispersystems.signalservice.api.push.PNI
|
||
import java.util.Optional
|
||
import java.util.UUID
|
||
|
||
@RunWith(AndroidJUnit4::class)
|
||
class RecipientDatabaseTest {
|
||
|
||
private lateinit var recipientDatabase: RecipientDatabase
|
||
|
||
private val localAci = ACI.from(UUID.randomUUID())
|
||
private val localPni = PNI.from(UUID.randomUUID())
|
||
|
||
@Before
|
||
fun setup() {
|
||
recipientDatabase = SignalDatabase.recipients
|
||
ensureDbEmpty()
|
||
|
||
SignalStore.account().setAci(localAci)
|
||
SignalStore.account().setPni(localPni)
|
||
}
|
||
|
||
// ==============================================================
|
||
// If both the ACI and E164 map to no one
|
||
// ==============================================================
|
||
|
||
/** If all you have is an ACI, you can just store that, regardless of trust level. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciOnly_highTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, true)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(ACI_A, recipient.requireServiceId())
|
||
assertFalse(recipient.hasE164())
|
||
}
|
||
|
||
/** If all you have is an ACI, you can just store that, regardless of trust level. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciOnly_lowTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, false)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(ACI_A, recipient.requireServiceId())
|
||
assertFalse(recipient.hasE164())
|
||
}
|
||
|
||
/** If all you have is an E164, you can just store that, regardless of trust level. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_e164Only_highTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(E164_A, recipient.requireE164())
|
||
assertFalse(recipient.hasServiceId())
|
||
}
|
||
|
||
/** If all you have is an E164, you can just store that, regardless of trust level. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_e164Only_lowTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, false)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(E164_A, recipient.requireE164())
|
||
assertFalse(recipient.hasServiceId())
|
||
}
|
||
|
||
/** With high trust, you can associate an ACI-e164 pair. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciAndE164_highTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(ACI_A, recipient.requireServiceId())
|
||
assertEquals(E164_A, recipient.requireE164())
|
||
}
|
||
|
||
/** With low trust, you cannot associate an ACI-e164 pair, and therefore can only store the ACI. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciAndE164_lowTrust() {
|
||
val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false)
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertEquals(ACI_A, recipient.requireServiceId())
|
||
assertFalse(recipient.hasE164())
|
||
}
|
||
|
||
// ==============================================================
|
||
// If the ACI maps to an existing user, but the E164 doesn't
|
||
// ==============================================================
|
||
|
||
/** With high trust, you can associate an e164 with an existing ACI. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_highTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getOrInsertFromServiceId(ACI_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** With low trust, you cannot associate an ACI-e164 pair, and therefore cannot store the e164. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_lowTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getOrInsertFromServiceId(ACI_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
}
|
||
|
||
/** Basically the ‘change number’ case. High trust lets you update the existing user. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_2_highTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_B, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** Low trust means you can’t update the underlying data, but you also don’t need to create any new rows. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_2_lowTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, false)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
// ==============================================================
|
||
// If the E164 maps to an existing user, but the ACI doesn't
|
||
// ==============================================================
|
||
|
||
/** With high trust, you can associate an e164 with an existing ACI. */
|
||
@Test
|
||
fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_highTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** With low trust, you cannot associate an ACI-e164 pair, and therefore need to create a new person with just the ACI. */
|
||
@Test
|
||
fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_lowTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false)
|
||
assertNotEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
|
||
val existingRecipient = Recipient.resolved(existingId)
|
||
assertEquals(E164_A, existingRecipient.requireE164())
|
||
assertFalse(existingRecipient.hasServiceId())
|
||
}
|
||
|
||
/** We never change the ACI of an existing row. New ACI = new person, regardless of trust. But high trust lets us take the e164 from the current holder. */
|
||
@Test
|
||
fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_2_highTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
recipientDatabase.setPni(existingId, PNI_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true)
|
||
recipientDatabase.setPni(retrievedId, PNI_A)
|
||
assertNotEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_B, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
|
||
val existingRecipient = Recipient.resolved(existingId)
|
||
assertEquals(ACI_A, existingRecipient.requireServiceId())
|
||
assertFalse(existingRecipient.hasE164())
|
||
}
|
||
|
||
/** We never change the ACI of an existing row. New ACI = new person, regardless of trust. And low trust means we can’t take the e164. */
|
||
@Test
|
||
fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_2_lowTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, false)
|
||
assertNotEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_B, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
|
||
val existingRecipient = Recipient.resolved(existingId)
|
||
assertEquals(ACI_A, existingRecipient.requireServiceId())
|
||
assertEquals(E164_A, existingRecipient.requireE164())
|
||
}
|
||
|
||
/** We never want to remove the e164 of our own contact entry. So basically treat this as a low-trust case, and leave the e164 alone. */
|
||
@Test
|
||
fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_e164BelongsToLocalUser_highTrust() {
|
||
val dataSet = KeyValueDataSet().apply {
|
||
putString(AccountValues.KEY_E164, E164_A)
|
||
putString(AccountValues.KEY_ACI, ACI_A.toString())
|
||
}
|
||
SignalStore.inject(KeyValueStore(MockKeyValuePersistentStorage.withDataSet(dataSet)))
|
||
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true)
|
||
assertNotEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_B, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
|
||
val existingRecipient = Recipient.resolved(existingId)
|
||
assertEquals(ACI_A, existingRecipient.requireServiceId())
|
||
assertEquals(E164_A, existingRecipient.requireE164())
|
||
}
|
||
|
||
// ==============================================================
|
||
// If both the ACI and E164 map to an existing user
|
||
// ==============================================================
|
||
|
||
/** Regardless of trust, if your ACI and e164 match, you’re good. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_highTrust() {
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** High trust lets you merge two different users into one. You should prefer the ACI user. Not shown: merging threads, dropping e164 sessions, etc. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_merge_highTrust() {
|
||
val changeNumberListener = ChangeNumberListener()
|
||
changeNumberListener.enqueue()
|
||
|
||
val existingAciId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, true)
|
||
val existingE164Id: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingAciId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
|
||
val existingE164Recipient = Recipient.resolved(existingE164Id)
|
||
assertEquals(retrievedId, existingE164Recipient.id)
|
||
|
||
changeNumberListener.waitForJobManager()
|
||
assertFalse(changeNumberListener.numberChangeWasEnqueued)
|
||
}
|
||
|
||
/** Same as [getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_merge_highTrust], but with a number change. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_merge_highTrust_changedNumber() {
|
||
val changeNumberListener = ChangeNumberListener()
|
||
changeNumberListener.enqueue()
|
||
|
||
val existingAciId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
val existingE164Id: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingAciId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
|
||
val existingE164Recipient = Recipient.resolved(existingE164Id)
|
||
assertEquals(retrievedId, existingE164Recipient.id)
|
||
|
||
changeNumberListener.waitForJobManager()
|
||
assert(changeNumberListener.numberChangeWasEnqueued)
|
||
}
|
||
|
||
/** Low trust means you can’t merge. If you’re retrieving a user from the table with this data, prefer the ACI one. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_lowTrust() {
|
||
val existingAciId: RecipientId = recipientDatabase.getOrInsertFromServiceId(ACI_A)
|
||
val existingE164Id: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false)
|
||
assertEquals(existingAciId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
|
||
val existingE164Recipient = Recipient.resolved(existingE164Id)
|
||
assertEquals(E164_A, existingE164Recipient.requireE164())
|
||
assertFalse(existingE164Recipient.hasServiceId())
|
||
}
|
||
|
||
/** Another high trust case. No new rules here, just a more complex scenario to show how different rules interact. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_complex_highTrust() {
|
||
val changeNumberListener = ChangeNumberListener()
|
||
changeNumberListener.enqueue()
|
||
|
||
val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId1, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
|
||
val existingRecipient2 = Recipient.resolved(existingId2)
|
||
assertEquals(ACI_B, existingRecipient2.requireServiceId())
|
||
assertFalse(existingRecipient2.hasE164())
|
||
|
||
assert(changeNumberListener.numberChangeWasEnqueued)
|
||
}
|
||
|
||
/** Another low trust case. No new rules here, just a more complex scenario to show how different rules interact. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_complex_lowTrust() {
|
||
val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false)
|
||
assertEquals(existingId1, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_B, retrievedRecipient.requireE164())
|
||
|
||
val existingRecipient2 = Recipient.resolved(existingId2)
|
||
assertEquals(ACI_B, existingRecipient2.requireServiceId())
|
||
assertEquals(E164_A, existingRecipient2.requireE164())
|
||
}
|
||
|
||
/**
|
||
* Another high trust case that results in a merge. Nothing strictly new here, but this case is called out because it’s a merge but *also* an E164 change,
|
||
* which clients may need to know for UX purposes.
|
||
*/
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_mergeAndPhoneNumberChange_highTrust() {
|
||
val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId1, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
|
||
assertFalse(recipientDatabase.getByE164(E164_B).isPresent)
|
||
|
||
val recipientWithId2 = Recipient.resolved(existingId2)
|
||
assertEquals(retrievedId, recipientWithId2.id)
|
||
}
|
||
|
||
/** We never want to remove the e164 of our own contact entry. So basically treat this as a low-trust case, and leave the e164 alone. */
|
||
@Test
|
||
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_e164BelongsToLocalUser() {
|
||
val dataSet = KeyValueDataSet().apply {
|
||
putString(AccountValues.KEY_E164, E164_A)
|
||
putString(AccountValues.KEY_ACI, ACI_B.toString())
|
||
}
|
||
SignalStore.inject(KeyValueStore(MockKeyValuePersistentStorage.withDataSet(dataSet)))
|
||
|
||
val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true)
|
||
val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
assertEquals(existingId2, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertFalse(retrievedRecipient.hasE164())
|
||
|
||
val recipientWithId1 = Recipient.resolved(existingId1)
|
||
assertEquals(ACI_B, recipientWithId1.requireServiceId())
|
||
assertEquals(E164_A, recipientWithId1.requireE164())
|
||
}
|
||
|
||
/** This is a case where normally we'd update the E164 of a user, but here the changeSelf flag is disabled, so we shouldn't. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciBelongsToLocalUser_highTrust_changeSelfFalse() {
|
||
val dataSet = KeyValueDataSet().apply {
|
||
putString(AccountValues.KEY_E164, E164_A)
|
||
putString(AccountValues.KEY_ACI, ACI_A.toString())
|
||
}
|
||
SignalStore.inject(KeyValueStore(MockKeyValuePersistentStorage.withDataSet(dataSet)))
|
||
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, highTrust = true, changeSelf = false)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_A, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** This is a case where we're changing our own number, and it's allowed because changeSelf = true. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciBelongsToLocalUser_highTrust_changeSelfTrue() {
|
||
val dataSet = KeyValueDataSet().apply {
|
||
putString(AccountValues.KEY_E164, E164_A)
|
||
putString(AccountValues.KEY_ACI, ACI_A.toString())
|
||
}
|
||
SignalStore.inject(KeyValueStore(MockKeyValuePersistentStorage.withDataSet(dataSet)))
|
||
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, highTrust = true, changeSelf = true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_B, retrievedRecipient.requireE164())
|
||
}
|
||
|
||
/** Verifying a case where a change number job is expected to be enqueued. */
|
||
@Test
|
||
fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_highTrust_changedNumber() {
|
||
val changeNumberListener = ChangeNumberListener()
|
||
changeNumberListener.enqueue()
|
||
|
||
val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
|
||
|
||
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
|
||
assertEquals(existingId, retrievedId)
|
||
|
||
val retrievedRecipient = Recipient.resolved(retrievedId)
|
||
assertEquals(ACI_A, retrievedRecipient.requireServiceId())
|
||
assertEquals(E164_B, retrievedRecipient.requireE164())
|
||
|
||
changeNumberListener.waitForJobManager()
|
||
assert(changeNumberListener.numberChangeWasEnqueued)
|
||
}
|
||
|
||
// ==============================================================
|
||
// Misc
|
||
// ==============================================================
|
||
|
||
@Test
|
||
fun createByE164SanityCheck() {
|
||
// GIVEN one recipient
|
||
val recipientId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A)
|
||
|
||
// WHEN I retrieve one by E164
|
||
val possible: Optional<RecipientId> = recipientDatabase.getByE164(E164_A)
|
||
|
||
// THEN I get it back, and it has the properties I expect
|
||
assertTrue(possible.isPresent)
|
||
assertEquals(recipientId, possible.get())
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertTrue(recipient.e164.isPresent)
|
||
assertEquals(E164_A, recipient.e164.get())
|
||
}
|
||
|
||
@Test
|
||
fun createByUuidSanityCheck() {
|
||
// GIVEN one recipient
|
||
val recipientId: RecipientId = recipientDatabase.getOrInsertFromServiceId(ACI_A)
|
||
|
||
// WHEN I retrieve one by UUID
|
||
val possible: Optional<RecipientId> = recipientDatabase.getByServiceId(ACI_A)
|
||
|
||
// THEN I get it back, and it has the properties I expect
|
||
assertTrue(possible.isPresent)
|
||
assertEquals(recipientId, possible.get())
|
||
|
||
val recipient = Recipient.resolved(recipientId)
|
||
assertTrue(recipient.serviceId.isPresent)
|
||
assertEquals(ACI_A, recipient.serviceId.get())
|
||
}
|
||
|
||
@Test(expected = IllegalArgumentException::class)
|
||
fun getAndPossiblyMerge_noArgs_invalid() {
|
||
recipientDatabase.getAndPossiblyMerge(null, null, true)
|
||
}
|
||
|
||
private fun ensureDbEmpty() {
|
||
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
|
||
assertTrue(cursor.moveToFirst())
|
||
assertEquals(0, cursor.getLong(0))
|
||
}
|
||
}
|
||
|
||
private class ChangeNumberListener {
|
||
|
||
var numberChangeWasEnqueued = false
|
||
private set
|
||
|
||
fun waitForJobManager() {
|
||
ApplicationDependencies.getJobManager().flush()
|
||
ThreadUtil.sleep(500)
|
||
}
|
||
|
||
fun enqueue() {
|
||
ApplicationDependencies.getJobManager().addListener(
|
||
{ job -> job.factoryKey == RecipientChangedNumberJob.KEY },
|
||
{ _, _ -> numberChangeWasEnqueued = true }
|
||
)
|
||
}
|
||
}
|
||
|
||
companion object {
|
||
val ACI_A = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
|
||
val ACI_B = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
|
||
|
||
val PNI_A = PNI.from(UUID.fromString("154b8d92-c960-4f6c-8385-671ad2ffb999"))
|
||
val PNI_B = PNI.from(UUID.fromString("ba92b1fb-cd55-40bf-adda-c35a85375533"))
|
||
|
||
const val E164_A = "+12221234567"
|
||
const val E164_B = "+13331234567"
|
||
}
|
||
}
|