search groups by member name, cleanup tests

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent 6b43199ba1
commit 13c43c2520

@ -6,6 +6,81 @@ import XCTest
@testable import Signal @testable import Signal
@testable import SignalMessaging @testable import SignalMessaging
@objc
class FakeEnvironment: TextSecureKitEnv {
let proxy: TextSecureKitEnv
init(proxy: TextSecureKitEnv) {
self.proxy = proxy
super.init(callMessageHandler: proxy.callMessageHandler, contactsManager: proxy.contactsManager, messageSender: proxy.messageSender, notificationsManager: proxy.notificationsManager, profileManager: proxy.profileManager)
}
var stubbedCallMessageHandler: OWSCallMessageHandler?
override var callMessageHandler: OWSCallMessageHandler {
if let callMessageHandler = stubbedCallMessageHandler {
return callMessageHandler
}
return proxy.callMessageHandler
}
var stubbedContactsManager: ContactsManagerProtocol?
override var contactsManager: ContactsManagerProtocol {
if let contactsManager = stubbedContactsManager {
return contactsManager
}
return proxy.contactsManager
}
var stubbedMessageSender: MessageSender?
override var messageSender: MessageSender {
if let messageSender = stubbedMessageSender {
return messageSender
}
return proxy.messageSender
}
var stubbedNotificationsManager: NotificationsProtocol?
override var notificationsManager: NotificationsProtocol {
if let notificationsManager = stubbedNotificationsManager {
return notificationsManager
}
return proxy.notificationsManager
}
var stubbedProfileManager: ProfileManagerProtocol?
override var profileManager: ProfileManagerProtocol {
if let profileManager = stubbedProfileManager {
return profileManager
}
return proxy.profileManager
}
}
@objc
class FakeContactsManager: NSObject, ContactsManagerProtocol {
func displayName(forPhoneIdentifier phoneNumber: String?) -> String {
if phoneNumber == "+12345678900" {
return "Alice"
} else if phoneNumber == "+49030183000" {
return "Bob Barker"
} else {
return ""
}
}
func signalAccounts() -> [SignalAccount] {
return []
}
func isSystemContact(_ recipientId: String) -> Bool {
return true
}
func isSystemContact(withSignalAccount recipientId: String) -> Bool {
return true
}
}
class ConversationSearcherTest: XCTestCase { class ConversationSearcherTest: XCTestCase {
// MARK: - Dependencies // MARK: - Dependencies
@ -19,6 +94,14 @@ class ConversationSearcherTest: XCTestCase {
// MARK: - Test Life Cycle // MARK: - Test Life Cycle
var originalEnvironment: TextSecureKitEnv?
override func tearDown() {
super.tearDown()
TextSecureKitEnv.setShared(originalEnvironment!)
}
override func setUp() { override func setUp() {
super.setUp() super.setUp()
@ -28,12 +111,18 @@ class ConversationSearcherTest: XCTestCase {
TSGroupThread.removeAllObjectsInCollection() TSGroupThread.removeAllObjectsInCollection()
TSMessage.removeAllObjectsInCollection() TSMessage.removeAllObjectsInCollection()
originalEnvironment = TextSecureKitEnv.shared()
let testEnvironment: FakeEnvironment = FakeEnvironment(proxy: originalEnvironment!)
testEnvironment.stubbedContactsManager = FakeContactsManager()
TextSecureKitEnv.setShared(testEnvironment)
self.dbConnection.readWrite { transaction in self.dbConnection.readWrite { transaction in
let bookModel = TSGroupModel(title: "Book Club", memberIds: [], image: nil, groupId: Randomness.generateRandomBytes(16)) let bookModel = TSGroupModel(title: "Book Club", memberIds: ["+12345678900", "+49030183000"], image: nil, groupId: Randomness.generateRandomBytes(16))
let bookClubGroupThread = TSGroupThread.getOrCreateThread(with: bookModel, transaction: transaction) let bookClubGroupThread = TSGroupThread.getOrCreateThread(with: bookModel, transaction: transaction)
self.bookClubThread = ThreadViewModel(thread: bookClubGroupThread, transaction: transaction) self.bookClubThread = ThreadViewModel(thread: bookClubGroupThread, transaction: transaction)
let snackModel = TSGroupModel(title: "Snack Club", memberIds: [], image: nil, groupId: Randomness.generateRandomBytes(16)) let snackModel = TSGroupModel(title: "Snack Club", memberIds: ["+12345678900"], image: nil, groupId: Randomness.generateRandomBytes(16))
let snackClubGroupThread = TSGroupThread.getOrCreateThread(with: snackModel, transaction: transaction) let snackClubGroupThread = TSGroupThread.getOrCreateThread(with: snackModel, transaction: transaction)
self.snackClubThread = ThreadViewModel(thread: snackClubGroupThread, transaction: transaction) self.snackClubThread = ThreadViewModel(thread: snackClubGroupThread, transaction: transaction)
@ -68,79 +157,69 @@ class ConversationSearcherTest: XCTestCase {
// MARK: Tests // MARK: Tests
func testSearchByGroupName() { func testSearchByGroupName() {
var threads: [ThreadViewModel] = []
var resultSet: SearchResultSet = .empty
// No Match // No Match
resultSet = getResultSet(searchText: "asdasdasd") threads = searchConversations(searchText: "asdasdasd")
XCTAssert(resultSet.conversations.isEmpty) XCTAssert(threads.isEmpty)
// Partial Match // Partial Match
resultSet = getResultSet(searchText: "Book") threads = searchConversations(searchText: "Book")
XCTAssert(resultSet.conversations.count == 1) XCTAssertEqual(1, threads.count)
if let foundThread: ThreadViewModel = resultSet.conversations.first?.thread { XCTAssertEqual([bookClubThread], threads)
XCTAssertEqual(bookClubThread, foundThread)
} else {
XCTFail("no thread found")
}
resultSet = getResultSet(searchText: "Snack") threads = searchConversations(searchText: "Snack")
XCTAssert(resultSet.conversations.count == 1) XCTAssertEqual(1, threads.count)
if let foundThread: ThreadViewModel = resultSet.conversations.first?.thread { XCTAssertEqual([snackClubThread], threads)
XCTAssertEqual(snackClubThread, foundThread)
} else {
XCTFail("no thread found")
}
// Multiple Partial Matches // Multiple Partial Matches
resultSet = getResultSet(searchText: "Club") threads = searchConversations(searchText: "Club")
XCTAssertEqual(2, resultSet.conversations.count) XCTAssertEqual(2, threads.count)
XCTAssert(resultSet.conversations.map { $0.thread }.contains(bookClubThread)) XCTAssertEqual([bookClubThread, snackClubThread], threads)
XCTAssert(resultSet.conversations.map { $0.thread }.contains(snackClubThread))
// Match Name Exactly // Match Name Exactly
resultSet = getResultSet(searchText: "Book Club") threads = searchConversations(searchText: "Book Club")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(1, threads.count)
XCTAssertEqual(bookClubThread, resultSet.conversations.first!.thread) XCTAssertEqual([bookClubThread], threads)
} }
func testSearchContactByNumber() { func testSearchContactByNumber() {
var resultSet: SearchResultSet = .empty var threads: [ThreadViewModel] = []
// No match // No match
resultSet = getResultSet(searchText: "+5551239999") threads = searchConversations(searchText: "+5551239999")
XCTAssertEqual(0, resultSet.conversations.count) XCTAssertEqual(0, threads.count)
// Exact match // Exact match
resultSet = getResultSet(searchText: "+12345678900") threads = searchConversations(searchText: "+12345678900")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
// Partial match // Partial match
resultSet = getResultSet(searchText: "+123456") threads = searchConversations(searchText: "+123456")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
// Prefixes // Prefixes
resultSet = getResultSet(searchText: "12345678900") threads = searchConversations(searchText: "12345678900")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
resultSet = getResultSet(searchText: "49") threads = searchConversations(searchText: "49")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(2, threads.count)
XCTAssertEqual(bobThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, bobThread], threads)
resultSet = getResultSet(searchText: "1-234-56") threads = searchConversations(searchText: "1-234-56")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
resultSet = getResultSet(searchText: "123456") threads = searchConversations(searchText: "123456")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
resultSet = getResultSet(searchText: "1.234.56") threads = searchConversations(searchText: "1.234.56")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
} }
// TODO // TODO
@ -157,20 +236,24 @@ class ConversationSearcherTest: XCTestCase {
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread)
} }
func pending_testSearchConversationByContactByName() { func testSearchConversationByContactByName() {
var resultSet: SearchResultSet = .empty var threads: [ThreadViewModel] = []
resultSet = getResultSet(searchText: "Alice") threads = searchConversations(searchText: "Alice")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
resultSet = getResultSet(searchText: "Bob") threads = searchConversations(searchText: "Bob")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(2, threads.count)
XCTAssertEqual(bobThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, bobThread], threads)
resultSet = getResultSet(searchText: "Barker") threads = searchConversations(searchText: "Barker")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(2, threads.count)
XCTAssertEqual(bobThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, bobThread], threads)
threads = searchConversations(searchText: "Bob B")
XCTAssertEqual(2, threads.count)
XCTAssertEqual([bookClubThread, bobThread], threads)
} }
func testSearchMessageByBodyContent() { func testSearchMessageByBodyContent() {
@ -188,6 +271,11 @@ class ConversationSearcherTest: XCTestCase {
// Mark: Helpers // Mark: Helpers
private func searchConversations(searchText: String) -> [ThreadViewModel] {
let results = getResultSet(searchText: searchText)
return results.conversations.map { $0.thread }
}
private func getResultSet(searchText: String) -> SearchResultSet { private func getResultSet(searchText: String) -> SearchResultSet {
var results: SearchResultSet! var results: SearchResultSet!
self.dbConnection.read { transaction in self.dbConnection.read { transaction in

@ -45,6 +45,10 @@ public class FullTextSearchFinder: NSObject {
// Mark: Index Building // Mark: Index Building
private class var contactsManager: ContactsManagerProtocol {
return TextSecureKitEnv.shared().contactsManager
}
private class func normalize(text: String) -> String { private class func normalize(text: String) -> String {
var normalized: String = text.trimmingCharacters(in: .whitespacesAndNewlines) var normalized: String = text.trimmingCharacters(in: .whitespacesAndNewlines)
@ -59,37 +63,65 @@ public class FullTextSearchFinder: NSObject {
} }
private static let groupThreadIndexer: SearchIndexer<TSGroupThread> = SearchIndexer { (groupThread: TSGroupThread) in private static let groupThreadIndexer: SearchIndexer<TSGroupThread> = SearchIndexer { (groupThread: TSGroupThread) in
let searchableContent = groupThread.groupModel.groupName ?? "" let groupName = groupThread.groupModel.groupName ?? ""
// TODO member names, member numbers let memberStrings = groupThread.groupModel.groupMemberIds.map { recipientId in
recipientIndexer.index(recipientId)
}.joined(separator: " ")
let searchableContent = "\(groupName) \(memberStrings)"
return normalize(text: searchableContent) return normalize(text: searchableContent)
} }
private static let contactThreadIndexer: SearchIndexer<TSContactThread> = SearchIndexer { (contactThread: TSContactThread) in private static let contactThreadIndexer: SearchIndexer<TSContactThread> = SearchIndexer { (contactThread: TSContactThread) in
let searchableContent = contactThread.contactIdentifier() let recipientId = contactThread.contactIdentifier()
let searchableContent = recipientIndexer.index(recipientId)
// TODO contact name
return normalize(text: searchableContent) return normalize(text: searchableContent)
} }
private static let contactIndexer: SearchIndexer<String> = SearchIndexer { (recipientId: String) in private static let recipientIndexer: SearchIndexer<String> = SearchIndexer { (recipientId: String) in
let displayName = contactsManager.displayName(forPhoneIdentifier: recipientId)
let searchableContent = "\(recipientId)" let searchableContent = "\(recipientId) \(displayName)"
// TODO contact name
return normalize(text: searchableContent) return normalize(text: searchableContent)
} }
private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage) in private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage) in
let searchableContent = message.body ?? "" let searchableContent = message.body ?? ""
return normalize(text: searchableContent) return normalize(text: searchableContent)
} }
// private lazy var groupThreadSearcher: Searcher<TSGroupThread> = Searcher { (groupThread: TSGroupThread) in
// let groupName = groupThread.groupModel.groupName
// let memberStrings = groupThread.groupModel.groupMemberIds.map { recipientId in
// self.indexingString(recipientId: recipientId)
// }.joined(separator: " ")
//
// return "\(memberStrings) \(groupName ?? "")"
// }
//
// private lazy var contactThreadSearcher: Searcher<TSContactThread> = Searcher { (contactThread: TSContactThread) in
// let recipientId = contactThread.contactIdentifier()
// return self.indexingString(recipientId: recipientId)
// }
//
// private lazy var signalAccountSearcher: Searcher<SignalAccount> = Searcher { (signalAccount: SignalAccount) in
// let recipientId = signalAccount.recipientId
// return self.indexingString(recipientId: recipientId)
// }
//
//
// private func indexingString(recipientId: String) -> String {
// let contactName = contactsManager.displayName(forPhoneIdentifier: recipientId)
// let profileName = contactsManager.profileName(forRecipientId: recipientId)
//
// return "\(recipientId) \(contactName) \(profileName ?? "")"
// }
private class func indexContent(object: Any) -> String? { private class func indexContent(object: Any) -> String? {
if let groupThread = object as? TSGroupThread { if let groupThread = object as? TSGroupThread {
return self.groupThreadIndexer.index(groupThread) return self.groupThreadIndexer.index(groupThread)

Loading…
Cancel
Save