Signal-iOS/SignalServiceKit/tests/Account/SignalAccountFinderTest.swift
George Nachman 22c1cb8a22 Fetch phone numbers in a batch.
This is the first meaty part of optimizing
fetching display names.

Some contacts fall back to phone numbers as their
display names. This PR fetches them in a single
SQL query via
`OWSContactsManager.phoneNumbers(for:, transaction)`.

To achieve this, this PR introduces
`GRDBSignalAccountFinder.signalAccounts(for:,transaction:)`
to fetch many accounts at once.

In order to fetch many accounts at once, we need
to be able to fetch many values from a
ModelReadCache at once. So this PR introduces
`ModelReadCache.readValues(for:,transaction:)` and
`ModelReadCache.getValues(for:,transaction:,returnNilOnCacheMiss:)`.

Existing methods that operate on a single value
were refactored to use the batch methods.

This PR adds tests for this functionality, which
necessitated changing the visibility of various
private symbols and also improving the fake
profile manager to make it more configurable.

There's also a tiny optimization for Refinery to
avoid calling a closure that has no work to do.
This helps elide do-nothing SQL queries that would
otherwise have been introduced.
2022-03-17 12:46:33 -07:00

102 lines
4.1 KiB
Swift

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import XCTest
@testable import SignalServiceKit
class SignalAccountFinderTest: SSKBaseTestSwift {
private lazy var localAddress = CommonGenerator.address()
override func setUp() {
super.setUp()
// Create local account.
tsAccountManager.registerForTests(withLocalNumber: localAddress.phoneNumber!,
uuid: localAddress.uuid!)
}
private func createRecipientsAndAccounts(_ addresses: [SignalServiceAddress]) -> [SignalAccount] {
let accounts = addresses.map { SignalAccount(address: $0) }
// Create recipients and accounts.
write { transaction in
for address in addresses {
SignalRecipient.mark(asRegisteredAndGet: address, trustLevel: .high, transaction: transaction)
}
for account in accounts {
account.anyInsert(transaction: transaction)
}
}
return accounts
}
func testReadManyValues() {
let addresses = [SignalServiceAddress(phoneNumber: "+17035559901"),
SignalServiceAddress(phoneNumber: "+17035559902"),
SignalServiceAddress(uuid: UUID()),
SignalServiceAddress(uuid: UUID())]
let accounts = createRecipientsAndAccounts(addresses)
read { transaction in
let accountFinder = AnySignalAccountFinder()
let actual = accountFinder.signalAccounts(for: addresses, transaction: transaction)
XCTAssertEqual(actual.map { $0?.recipientAddress },
accounts.map { $0.recipientAddress })
}
}
func testReadPhoneNumbersAndBogus() {
let addresses = [SignalServiceAddress(phoneNumber: "+17035559901"),
SignalServiceAddress(phoneNumber: "+17035550000")]
let accounts = createRecipientsAndAccounts(addresses)
read { transaction in
let accountFinder = AnySignalAccountFinder()
let bogus = [SignalServiceAddress(uuid: UUID())]
let actual = accountFinder.signalAccounts(for: addresses + bogus, transaction: transaction)
XCTAssertEqual(actual.map { $0?.recipientAddress },
accounts.map { $0.recipientAddress } + [ nil ])
}
}
func testMixOfRealAndBogusAddresses() {
let addresses = [SignalServiceAddress(phoneNumber: "+17035559901"),
SignalServiceAddress(phoneNumber: "+17035550000")] // no account for this one
let accounts = createRecipientsAndAccounts([addresses[0]])
read { transaction in
let accountFinder = AnySignalAccountFinder()
let actual = accountFinder.signalAccounts(for: addresses, transaction: transaction)
XCTAssertEqual(actual.map { $0?.recipientAddress },
accounts.map { $0.recipientAddress } + [ nil ])
}
}
func testTwoAccountsWithSamePhoneNumber() {
let addresses = [SignalServiceAddress(phoneNumber: "+17035559901"),
SignalServiceAddress(phoneNumber: "+17035559901")]
let accounts = createRecipientsAndAccounts(addresses)
read { transaction in
let accountFinder = AnySignalAccountFinder()
let actual = accountFinder.signalAccounts(for: addresses, transaction: transaction)
XCTAssertEqual(actual.map { $0?.recipientAddress },
accounts.map { $0.recipientAddress })
}
}
func testTwoAccountsWithSameUUID() {
let uuid = UUID()
let addresses = [SignalServiceAddress(uuid: uuid),
SignalServiceAddress(uuid: uuid)]
let accounts = createRecipientsAndAccounts(addresses)
read { transaction in
let accountFinder = AnySignalAccountFinder()
let actual = accountFinder.signalAccounts(for: addresses, transaction: transaction)
XCTAssertEqual(actual.map { $0?.recipientAddress },
accounts.map { $0.recipientAddress })
}
}
}