Scaffold for Backup integration tests

This commit is contained in:
Sasha Weiss 2024-05-17 13:59:49 -07:00 committed by GitHub
parent 3526c2b650
commit 3ffcc47a66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 895 additions and 187 deletions

View File

@ -1786,6 +1786,9 @@
D9AE0ADB29188A170063488B /* LegacyMessageDecryptJobRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AE0ADA29188A170063488B /* LegacyMessageDecryptJobRecord.swift */; };
D9AE0ADD2918B2960063488B /* JobRecord+Columns.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AE0ADC2918B2960063488B /* JobRecord+Columns.swift */; };
D9B0AC7429EF42960070F31C /* TSInfoMessage+DisplayableGroupUpdateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9B0AC7329EF42960070F31C /* TSInfoMessage+DisplayableGroupUpdateItem.swift */; };
D9B6FD752BF2B94A00BB7DF1 /* account-data.jsonproto in Resources */ = {isa = PBXBuildFile; fileRef = D9B6FD742BF2B94A00BB7DF1 /* account-data.jsonproto */; };
D9B6FD7A2BF40A7200BB7DF1 /* unregistered-contact.jsonproto in Resources */ = {isa = PBXBuildFile; fileRef = D9B6FD782BF40A7200BB7DF1 /* unregistered-contact.jsonproto */; };
D9B6FD852BF53FCF00BB7DF1 /* registered-blocked-contact.jsonproto in Resources */ = {isa = PBXBuildFile; fileRef = D9B6FD7C2BF53C0100BB7DF1 /* registered-blocked-contact.jsonproto */; };
D9B8541229137C150058F97B /* JobRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9B8541129137C150058F97B /* JobRecord.swift */; };
D9B95A9629E6830B00D7CB95 /* JobRecordTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9B95A9429E682E900D7CB95 /* JobRecordTest.swift */; };
D9B95A9829E8906200D7CB95 /* OWSDeviceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9B95A9729E8906200D7CB95 /* OWSDeviceTest.swift */; };
@ -1814,6 +1817,8 @@
D9C964092BE44D700058F143 /* XCTest+Thenable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C964072BE44D510058F143 /* XCTest+Thenable.swift */; };
D9C964102BE451CE0058F143 /* TSMessageStorageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C9640F2BE451CE0058F143 /* TSMessageStorageTest.swift */; };
D9C964142BE45A030058F143 /* SignedPreKeyDeletionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C964132BE45A030058F143 /* SignedPreKeyDeletionTests.swift */; };
D9C964172BE56DFB0058F143 /* MessageBackupIntegrationTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C964162BE56DFB0058F143 /* MessageBackupIntegrationTestCase.swift */; };
D9C964192BE59E270058F143 /* MessageBackupIntegrationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C964182BE59E270058F143 /* MessageBackupIntegrationTest.swift */; };
D9CA5BF729B3F61E00D9AAD1 /* LegacyChangePhoneNumber+ChangeTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9CA5BF629B3F61E00D9AAD1 /* LegacyChangePhoneNumber+ChangeTokens.swift */; };
D9CA8AB02B698DFF00787167 /* DeletedCallRecordCleanupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9CA8AAF2B698DFF00787167 /* DeletedCallRecordCleanupManager.swift */; };
D9CA8AB32B6ACC0600787167 /* DeletedCallRecordCleanupManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9CA8AB22B6ACC0600787167 /* DeletedCallRecordCleanupManagerTest.swift */; };
@ -4691,6 +4696,9 @@
D9AE0ADA29188A170063488B /* LegacyMessageDecryptJobRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyMessageDecryptJobRecord.swift; sourceTree = "<group>"; };
D9AE0ADC2918B2960063488B /* JobRecord+Columns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JobRecord+Columns.swift"; sourceTree = "<group>"; };
D9B0AC7329EF42960070F31C /* TSInfoMessage+DisplayableGroupUpdateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSInfoMessage+DisplayableGroupUpdateItem.swift"; sourceTree = "<group>"; };
D9B6FD742BF2B94A00BB7DF1 /* account-data.jsonproto */ = {isa = PBXFileReference; lastKnownFileType = text; path = "account-data.jsonproto"; sourceTree = "<group>"; };
D9B6FD782BF40A7200BB7DF1 /* unregistered-contact.jsonproto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "unregistered-contact.jsonproto"; sourceTree = "<group>"; };
D9B6FD7C2BF53C0100BB7DF1 /* registered-blocked-contact.jsonproto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "registered-blocked-contact.jsonproto"; sourceTree = "<group>"; };
D9B8541129137C150058F97B /* JobRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRecord.swift; sourceTree = "<group>"; };
D9B91D8D2B17E2A600BCB11A /* GroupCallRecordRingUpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupCallRecordRingUpdateDelegate.swift; sourceTree = "<group>"; };
D9B95A9429E682E900D7CB95 /* JobRecordTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRecordTest.swift; sourceTree = "<group>"; };
@ -4721,6 +4729,8 @@
D9C964072BE44D510058F143 /* XCTest+Thenable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTest+Thenable.swift"; sourceTree = "<group>"; };
D9C9640F2BE451CE0058F143 /* TSMessageStorageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSMessageStorageTest.swift; sourceTree = "<group>"; };
D9C964132BE45A030058F143 /* SignedPreKeyDeletionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignedPreKeyDeletionTests.swift; sourceTree = "<group>"; };
D9C964162BE56DFB0058F143 /* MessageBackupIntegrationTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBackupIntegrationTestCase.swift; sourceTree = "<group>"; };
D9C964182BE59E270058F143 /* MessageBackupIntegrationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBackupIntegrationTest.swift; sourceTree = "<group>"; };
D9CA5BF629B3F61E00D9AAD1 /* LegacyChangePhoneNumber+ChangeTokens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LegacyChangePhoneNumber+ChangeTokens.swift"; sourceTree = "<group>"; };
D9CA8AAF2B698DFF00787167 /* DeletedCallRecordCleanupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedCallRecordCleanupManager.swift; sourceTree = "<group>"; };
D9CA8AB22B6ACC0600787167 /* DeletedCallRecordCleanupManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedCallRecordCleanupManagerTest.swift; sourceTree = "<group>"; };
@ -9575,6 +9585,16 @@
path = Individual;
sourceTree = "<group>";
};
D9B6FD732BF2B92000BB7DF1 /* TestCases */ = {
isa = PBXGroup;
children = (
D9B6FD742BF2B94A00BB7DF1 /* account-data.jsonproto */,
D9B6FD7C2BF53C0100BB7DF1 /* registered-blocked-contact.jsonproto */,
D9B6FD782BF40A7200BB7DF1 /* unregistered-contact.jsonproto */,
);
path = TestCases;
sourceTree = "<group>";
};
D9B95A9329E682CA00D7CB95 /* JobRecords */ = {
isa = PBXGroup;
children = (
@ -9604,6 +9624,16 @@
path = ChangePhoneNumber;
sourceTree = "<group>";
};
D9C964152BE56DE20058F143 /* MessageBackup */ = {
isa = PBXGroup;
children = (
D9B6FD732BF2B92000BB7DF1 /* TestCases */,
D9C964182BE59E270058F143 /* MessageBackupIntegrationTest.swift */,
D9C964162BE56DFB0058F143 /* MessageBackupIntegrationTestCase.swift */,
);
path = MessageBackup;
sourceTree = "<group>";
};
D9CA8AAD2B698B2400787167 /* DeletedCallRecord */ = {
isa = PBXGroup;
children = (
@ -10202,6 +10232,7 @@
5075C21529CA1ED500A260D2 /* GroupMembers */,
F94261E2289B1B5400460798 /* Groups */,
5000CA2F2B1F97DC00BB8EFF /* Jobs */,
D9C964152BE56DE20058F143 /* MessageBackup */,
F942621C289B1B5500460798 /* Messages */,
D9F399B72A9EA1EB001599EC /* Mocks */,
F94261CF289B1B5400460798 /* Network */,
@ -11988,10 +12019,13 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D9B6FD752BF2B94A00BB7DF1 /* account-data.jsonproto in Resources */,
D9B6FD852BF53FCF00BB7DF1 /* registered-blocked-contact.jsonproto in Resources */,
F942628B289B1B5600460798 /* sample-sticker.encrypted in Resources */,
F942628C289B1B5600460798 /* sample-sticker.webp in Resources */,
F908AA7D28CE629700472E68 /* test-apng.png in Resources */,
F927478828CFE9B10056EAFE /* test-png.png in Resources */,
D9B6FD7A2BF40A7200BB7DF1 /* unregistered-contact.jsonproto in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -14594,6 +14628,8 @@
D938307C2A704338006CDCDE /* LocalUsernameManagerTests.swift in Sources */,
F942625F289B1B5500460798 /* LRUCacheTest.swift in Sources */,
F9426269289B1B5500460798 /* MathOWSTests.swift in Sources */,
D9C964192BE59E270058F143 /* MessageBackupIntegrationTest.swift in Sources */,
D9C964172BE56DFB0058F143 /* MessageBackupIntegrationTestCase.swift in Sources */,
66FC637229DF7A1500F00DAC /* MessageBodyRangesTests.swift in Sources */,
668444822A3292AB00DBED7C /* MessageBodyStyleTests.swift in Sources */,
66883A3A29D7630A00E898CF /* MessageBodyTests.swift in Sources */,

View File

@ -446,7 +446,7 @@ class DebugUIMisc: NSObject, DebugUIPage, Dependencies {
updateStorageService: false, /* storage service updated below */
transaction: transaction
)
StoryManager.setHasSetMyStoriesPrivacy(false, transaction: transaction, shouldUpdateStorageService: true)
StoryManager.setHasSetMyStoriesPrivacy(false, shouldUpdateStorageService: true, transaction: transaction)
}
}

View File

@ -12,7 +12,6 @@ public class PerformanceBaseTest: XCTestCase {
// MARK: Hooks
func setUpIteration() {
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
}

View File

@ -11,8 +11,6 @@ open class SignalBaseTest: XCTestCase {
public override func setUp() {
super.setUp()
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
}

View File

@ -185,7 +185,11 @@ class GRDBFinderTest: SignalBaseTest {
var expectedAddresses = Set<OWSUserProfile.Address>()
self.write { tx in
let buildUserProfile = { () -> OWSUserProfile in
return OWSUserProfile.getOrBuildUserProfile(for: .otherUser(Aci.randomForTesting()), tx: tx)
return OWSUserProfile.getOrBuildUserProfile(
for: .otherUser(Aci.randomForTesting()),
userProfileWriter: .tests,
tx: tx
)
}
func updateUserProfile(

View File

@ -138,8 +138,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:lastVisibleSortIdOnScreenPercentageO
{
if ([self isMyStory]) {
[StoryManager setHasSetMyStoriesPrivacy:YES
transaction:transaction
shouldUpdateStorageService:updateStorageService];
shouldUpdateStorageService:updateStorageService
transaction:transaction];
}
[self anyUpdatePrivateStoryThreadWithTransaction:transaction
block:^(TSPrivateStoryThread *thread) {

View File

@ -10,28 +10,85 @@ import SignalCoreKit
public class AppSetup {
public init() {}
/// Injectable mocks for global singletons accessed by tests for components
/// that cannot be isolated in tests.
///
/// For example, many legacy tests rely on the globally-available singletons
/// from ``Dependencies`` and its ``NSObject`` extension, and use this type
/// to inject mock versions of those singletons to the global state.
///
/// Additionally, the integration tests for message backup access these
/// globals transitively through message backup's dependency on managers
/// that use the global state, and similarly use this type to inject a
/// limited set of mock singletons.
public struct TestDependencies {
let accountServiceClient: AccountServiceClient
let contactManager: any ContactManager
let groupV2Updates: any GroupV2Updates
let groupsV2: any GroupsV2
let keyValueStoreFactory: any KeyValueStoreFactory
let messageSender: MessageSender
let modelReadCaches: ModelReadCaches
let networkManager: NetworkManager
let paymentsCurrencies: any PaymentsCurrenciesSwift
let paymentsHelper: any PaymentsHelperSwift
let pendingReceiptRecorder: any PendingReceiptRecorder
let profileManager: any ProfileManager
let reachabilityManager: any SSKReachabilityManager
let remoteConfigManager: any RemoteConfigManager
let signalService: any OWSSignalServiceProtocol
let storageServiceManager: any StorageServiceManager
let subscriptionManager: any SubscriptionManager
let syncManager: any SyncManagerProtocol
let systemStoryManager: any SystemStoryManagerProtocol
let versionedProfiles: any VersionedProfilesSwift
let webSocketFactory: any WebSocketFactory
let accountServiceClient: AccountServiceClient?
let contactManager: (any ContactManager)?
let groupV2Updates: (any GroupV2Updates)?
let groupsV2: (any GroupsV2)?
let keyValueStoreFactory: (any KeyValueStoreFactory)?
let messageSender: MessageSender?
let modelReadCaches: ModelReadCaches?
let networkManager: NetworkManager?
let paymentsCurrencies: (any PaymentsCurrenciesSwift)?
let paymentsHelper: (any PaymentsHelperSwift)?
let pendingReceiptRecorder: (any PendingReceiptRecorder)?
let profileManager: (any ProfileManager)?
let reachabilityManager: (any SSKReachabilityManager)?
let remoteConfigManager: (any RemoteConfigManager)?
let signalService: (any OWSSignalServiceProtocol)?
let storageServiceManager: (any StorageServiceManager)?
let subscriptionManager: (any SubscriptionManager)?
let syncManager: (any SyncManagerProtocol)?
let systemStoryManager: (any SystemStoryManagerProtocol)?
let versionedProfiles: (any VersionedProfilesSwift)?
let webSocketFactory: (any WebSocketFactory)?
public init(
accountServiceClient: AccountServiceClient? = nil,
contactManager: (any ContactManager)? = nil,
groupV2Updates: (any GroupV2Updates)? = nil,
groupsV2: (any GroupsV2)? = nil,
keyValueStoreFactory: (any KeyValueStoreFactory)? = nil,
messageSender: MessageSender? = nil,
modelReadCaches: ModelReadCaches? = nil,
networkManager: NetworkManager? = nil,
paymentsCurrencies: (any PaymentsCurrenciesSwift)? = nil,
paymentsHelper: (any PaymentsHelperSwift)? = nil,
pendingReceiptRecorder: (any PendingReceiptRecorder)? = nil,
profileManager: (any ProfileManager)? = nil,
reachabilityManager: (any SSKReachabilityManager)? = nil,
remoteConfigManager: (any RemoteConfigManager)? = nil,
signalService: (any OWSSignalServiceProtocol)? = nil,
storageServiceManager: (any StorageServiceManager)? = nil,
subscriptionManager: (any SubscriptionManager)? = nil,
syncManager: (any SyncManagerProtocol)? = nil,
systemStoryManager: (any SystemStoryManagerProtocol)? = nil,
versionedProfiles: (any VersionedProfilesSwift)? = nil,
webSocketFactory: (any WebSocketFactory)? = nil
) {
self.accountServiceClient = accountServiceClient
self.contactManager = contactManager
self.groupV2Updates = groupV2Updates
self.groupsV2 = groupsV2
self.keyValueStoreFactory = keyValueStoreFactory
self.messageSender = messageSender
self.modelReadCaches = modelReadCaches
self.networkManager = networkManager
self.paymentsCurrencies = paymentsCurrencies
self.paymentsHelper = paymentsHelper
self.pendingReceiptRecorder = pendingReceiptRecorder
self.profileManager = profileManager
self.reachabilityManager = reachabilityManager
self.remoteConfigManager = remoteConfigManager
self.signalService = signalService
self.storageServiceManager = storageServiceManager
self.subscriptionManager = subscriptionManager
self.syncManager = syncManager
self.systemStoryManager = systemStoryManager
self.versionedProfiles = versionedProfiles
self.webSocketFactory = webSocketFactory
}
}
public func start(
@ -42,7 +99,7 @@ public class AppSetup {
callMessageHandler: CallMessageHandler,
currentCallThreadProvider: any CurrentCallThreadProvider,
notificationPresenter: any NotificationPresenter,
testDependencies: TestDependencies? = nil
testDependencies: TestDependencies = TestDependencies()
) -> AppSetup.DatabaseContinuation {
configureUnsatisfiableConstraintLogging()
@ -58,7 +115,7 @@ public class AppSetup {
OWSBackgroundTaskManager.shared().observeNotifications()
let appVersion = AppVersionImpl.shared
let webSocketFactory = testDependencies?.webSocketFactory ?? WebSocketFactoryNative()
let webSocketFactory = testDependencies.webSocketFactory ?? WebSocketFactoryNative()
// AFNetworking (via CFNetworking) spools its attachments in
// NSTemporaryDirectory(). If you receive a media message while the device
@ -69,13 +126,13 @@ public class AppSetup {
owsAssert(OWSFileSystem.protectFileOrFolder(atPath: temporaryDirectory, fileProtectionType: .completeUntilFirstUserAuthentication))
let tsConstants = TSConstants.shared
let keyValueStoreFactory = testDependencies?.keyValueStoreFactory ?? SDSKeyValueStoreFactory()
let keyValueStoreFactory = testDependencies.keyValueStoreFactory ?? SDSKeyValueStoreFactory()
let recipientDatabaseTable = RecipientDatabaseTableImpl()
let recipientFetcher = RecipientFetcherImpl(recipientDatabaseTable: recipientDatabaseTable)
let recipientIdFinder = RecipientIdFinder(recipientDatabaseTable: recipientDatabaseTable, recipientFetcher: recipientFetcher)
let accountServiceClient = testDependencies?.accountServiceClient ?? AccountServiceClient()
let accountServiceClient = testDependencies.accountServiceClient ?? AccountServiceClient()
let aciSignalProtocolStore = SignalProtocolStoreImpl(
for: .aci,
keyValueStoreFactory: keyValueStoreFactory,
@ -85,34 +142,34 @@ public class AppSetup {
let dateProvider = Date.provider
let earlyMessageManager = EarlyMessageManager()
let messageProcessor = MessageProcessor()
let messageSender = testDependencies?.messageSender ?? MessageSender()
let messageSender = testDependencies.messageSender ?? MessageSender()
let messageSenderJobQueue = MessageSenderJobQueue()
let modelReadCaches = testDependencies?.modelReadCaches ?? ModelReadCaches(factory: ModelReadCacheFactory())
let networkManager = testDependencies?.networkManager ?? NetworkManager()
let modelReadCaches = testDependencies.modelReadCaches ?? ModelReadCaches(factory: ModelReadCacheFactory())
let networkManager = testDependencies.networkManager ?? NetworkManager()
let ows2FAManager = OWS2FAManager()
let paymentsHelper = testDependencies?.paymentsHelper ?? PaymentsHelperImpl()
let paymentsHelper = testDependencies.paymentsHelper ?? PaymentsHelperImpl()
let pniSignalProtocolStore = SignalProtocolStoreImpl(
for: .pni,
keyValueStoreFactory: keyValueStoreFactory,
recipientIdFinder: recipientIdFinder
)
let profileManager = testDependencies?.profileManager ?? OWSProfileManager(
let profileManager = testDependencies.profileManager ?? OWSProfileManager(
databaseStorage: databaseStorage,
swiftValues: OWSProfileManagerSwiftValues()
)
let reachabilityManager = testDependencies?.reachabilityManager ?? SSKReachabilityManagerImpl()
let reachabilityManager = testDependencies.reachabilityManager ?? SSKReachabilityManagerImpl()
let receiptManager = OWSReceiptManager()
let senderKeyStore = SenderKeyStore()
let signalProtocolStoreManager = SignalProtocolStoreManagerImpl(
aciProtocolStore: aciSignalProtocolStore,
pniProtocolStore: pniSignalProtocolStore
)
let signalService = testDependencies?.signalService ?? OWSSignalService()
let signalService = testDependencies.signalService ?? OWSSignalService()
let signalServiceAddressCache = SignalServiceAddressCache()
let storageServiceManager = testDependencies?.storageServiceManager ?? StorageServiceManagerImpl.shared
let syncManager = testDependencies?.syncManager ?? OWSSyncManager(default: ())
let storageServiceManager = testDependencies.storageServiceManager ?? StorageServiceManagerImpl.shared
let syncManager = testDependencies.syncManager ?? OWSSyncManager(default: ())
let udManager = OWSUDManagerImpl()
let versionedProfiles = testDependencies?.versionedProfiles ?? VersionedProfilesImpl()
let versionedProfiles = testDependencies.versionedProfiles ?? VersionedProfilesImpl()
let signalAccountStore = SignalAccountStoreImpl()
let threadStore = ThreadStoreImpl()
@ -141,7 +198,7 @@ public class AppSetup {
storageServiceManager: storageServiceManager,
schedulers: schedulers
)
let contactManager = testDependencies?.contactManager ?? OWSContactsManager(swiftValues: OWSContactsManagerSwiftValues(
let contactManager = testDependencies.contactManager ?? OWSContactsManager(swiftValues: OWSContactsManagerSwiftValues(
usernameLookupManager: usernameLookupManager,
recipientDatabaseTable: recipientDatabaseTable,
nicknameManager: nicknameManager
@ -158,7 +215,7 @@ public class AppSetup {
db: db
)
let groupsV2 = testDependencies?.groupsV2 ?? GroupsV2Impl(
let groupsV2 = testDependencies.groupsV2 ?? GroupsV2Impl(
authCredentialStore: authCredentialStore,
authCredentialManager: authCredentialManager
)
@ -664,8 +721,8 @@ public class AppSetup {
let messageBackupKeyMaterial = MessageBackupKeyMaterialImpl(svr: svr)
let preferences = Preferences()
let storyStore = StoryStoreImpl()
let subscriptionManager = testDependencies?.subscriptionManager ?? SubscriptionManagerImpl()
let systemStoryManager = testDependencies?.systemStoryManager ?? SystemStoryManager()
let subscriptionManager = testDependencies.subscriptionManager ?? SubscriptionManagerImpl()
let systemStoryManager = testDependencies.systemStoryManager ?? SystemStoryManager()
let typingIndicators = TypingIndicatorsImpl()
let attachmentUploadManager = AttachmentUploadManagerImpl(
@ -687,6 +744,7 @@ public class AppSetup {
localUsernameManager: localUsernameManager,
phoneNumberDiscoverabilityManager: phoneNumberDiscoverabilityManager,
preferences: MessageBackup.AccountData.Wrappers.Preferences(preferences: preferences),
profileManager: MessageBackup.Wrappers.ProfileManager(profileManager),
receiptManager: MessageBackup.AccountData.Wrappers.ReceiptManager(receiptManager: receiptManager),
reactionManager: MessageBackup.AccountData.Wrappers.ReactionManager(),
sskPreferences: MessageBackup.AccountData.Wrappers.SSKPreferences(),
@ -695,8 +753,7 @@ public class AppSetup {
systemStoryManager: MessageBackup.AccountData.Wrappers.SystemStoryManager(systemStoryManager: systemStoryManager),
typingIndicators: MessageBackup.AccountData.Wrappers.TypingIndicators(typingIndicators: typingIndicators),
udManager: MessageBackup.AccountData.Wrappers.UDManager(udManager: udManager),
usernameEducationManager: usernameEducationManager,
userProfile: MessageBackup.AccountData.Wrappers.UserProfile()
usernameEducationManager: usernameEducationManager
),
attachmentDownloadManager: attachmentDownloadManager,
attachmentUploadManager: attachmentUploadManager,
@ -919,9 +976,9 @@ public class AppSetup {
reachabilityManager: reachabilityManager
)
let pendingReceiptRecorder = testDependencies?.pendingReceiptRecorder ?? MessageRequestPendingReceipts()
let pendingReceiptRecorder = testDependencies.pendingReceiptRecorder ?? MessageRequestPendingReceipts()
let messageReceiver = MessageReceiver(callMessageHandler: callMessageHandler)
let remoteConfigManager = testDependencies?.remoteConfigManager ?? RemoteConfigManagerImpl(
let remoteConfigManager = testDependencies.remoteConfigManager ?? RemoteConfigManagerImpl(
appExpiry: appExpiry,
db: db,
keyValueStoreFactory: keyValueStoreFactory,
@ -937,7 +994,7 @@ public class AppSetup {
)
let stickerManager = StickerManager()
let sskPreferences = SSKPreferences()
let groupV2Updates = testDependencies?.groupV2Updates ?? GroupV2UpdatesImpl()
let groupV2Updates = testDependencies.groupV2Updates ?? GroupV2UpdatesImpl()
let messageFetcherJob = MessageFetcherJob()
let profileFetcher = ProfileFetcherImpl(
db: db,
@ -953,7 +1010,7 @@ public class AppSetup {
versionedProfiles: versionedProfiles
)
let messagePipelineSupervisor = MessagePipelineSupervisor()
let paymentsCurrencies = testDependencies?.paymentsCurrencies ?? PaymentsCurrenciesImpl()
let paymentsCurrencies = testDependencies.paymentsCurrencies ?? PaymentsCurrenciesImpl()
let spamChallengeResolver = SpamChallengeResolver()
let phoneNumberUtil = PhoneNumberUtil(swiftValues: PhoneNumberUtilSwiftValues())
let legacyChangePhoneNumber = LegacyChangePhoneNumber()

View File

@ -3,6 +3,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalCoreKit
extension MessageBackup {
/// An identifier for the ``BackupProto.AccountData`` backup frame.
///
@ -48,6 +50,7 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
private let localUsernameManager: LocalUsernameManager
private let phoneNumberDiscoverabilityManager: PhoneNumberDiscoverabilityManager
private let preferences: MessageBackup.AccountData.Shims.Preferences
private let profileManager: MessageBackup.Shims.ProfileManager
private let receiptManager: MessageBackup.AccountData.Shims.ReceiptManager
private let reactionManager: MessageBackup.AccountData.Shims.ReactionManager
private let sskPreferences: MessageBackup.AccountData.Shims.SSKPreferences
@ -57,13 +60,13 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
private let typingIndicators: MessageBackup.AccountData.Shims.TypingIndicators
private let udManager: MessageBackup.AccountData.Shims.UDManager
private let usernameEducationManager: UsernameEducationManager
private let userProfile: MessageBackup.AccountData.Shims.UserProfile
public init(
disappearingMessageConfigurationStore: DisappearingMessagesConfigurationStore,
localUsernameManager: LocalUsernameManager,
phoneNumberDiscoverabilityManager: PhoneNumberDiscoverabilityManager,
preferences: MessageBackup.AccountData.Shims.Preferences,
profileManager: MessageBackup.Shims.ProfileManager,
receiptManager: MessageBackup.AccountData.Shims.ReceiptManager,
reactionManager: MessageBackup.AccountData.Shims.ReactionManager,
sskPreferences: MessageBackup.AccountData.Shims.SSKPreferences,
@ -72,8 +75,7 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
systemStoryManager: MessageBackup.AccountData.Shims.SystemStoryManager,
typingIndicators: MessageBackup.AccountData.Shims.TypingIndicators,
udManager: MessageBackup.AccountData.Shims.UDManager,
usernameEducationManager: UsernameEducationManager,
userProfile: MessageBackup.AccountData.Shims.UserProfile
usernameEducationManager: UsernameEducationManager
) {
self.disappearingMessageConfigurationStore = disappearingMessageConfigurationStore
self.localUsernameManager = localUsernameManager
@ -88,7 +90,7 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
self.typingIndicators = typingIndicators
self.udManager = udManager
self.usernameEducationManager = usernameEducationManager
self.userProfile = userProfile
self.profileManager = profileManager
}
public func archiveAccountData(
@ -96,7 +98,7 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
tx: DBReadTransaction
) -> MessageBackup.ArchiveAccountDataResult {
guard let localProfile = userProfile.getLocalProfile(tx: tx) else {
guard let localProfile = profileManager.getUserProfileForLocalUser(tx: tx) else {
return .failure(.archiveFrameError(.missingLocalProfile, .localUser))
}
guard let profileKeyData = localProfile.profileKey?.keyData else {
@ -224,7 +226,6 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
_ accountData: BackupProto.AccountData,
tx: DBWriteTransaction
) -> RestoreFrameResult {
guard let profileKey = OWSAES256Key(data: accountData.profileKey) else {
return .failure([.restoreFrameError(
.invalidProtoData(.invalidLocalProfileKey),
@ -232,6 +233,16 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
)])
}
// Given name and profile key are required for the local profile. The
// rest are optional.
profileManager.insertLocalUserProfile(
givenName: accountData.givenName,
familyName: accountData.familyName.nilIfEmpty,
avatarUrlPath: accountData.avatarUrlPath.nilIfEmpty,
profileKey: profileKey,
tx: tx
)
// Restore Subscription data, if nod a default value
if accountData.subscriberId.count > 0, accountData.subscriberCurrencyCode.count > 0 {
subscriptionManager.setSubscriberID(subscriberID: accountData.subscriberId, tx: tx)
@ -288,16 +299,6 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
udManager.setPhoneNumberSharingMode(phoneNumberSharingMode, tx: tx)
}
// Restore Local Profile
// For familyName & avatarUrlPath, pass in `nil` if the value is empty.
userProfile.insertLocalProfile(
givenName: accountData.givenName,
familyName: accountData.familyName.nilIfEmpty,
avatarUrlPath: accountData.avatarUrlPath.nilIfEmpty,
profileKey: profileKey,
tx: tx
)
// Restore username details (username, link, QR color)
if let username = accountData.username, let usernameLink = accountData.usernameLink {
if

View File

@ -51,7 +51,7 @@ public class MessageBackupContactRecipientArchiver: MessageBackupRecipientDestin
context: MessageBackup.RecipientArchivingContext,
tx: DBReadTransaction
) -> ArchiveMultiFrameResult {
let whitelistedAddresses = Set(profileManager.allWhitelistedRegisteredAddresses(tx: tx))
let whitelistedAddresses = Set(profileManager.allWhitelistedAddresses(tx: tx))
let blockedAddresses = blockingManager.blockedAddresses(tx: tx)
var errors = [ArchiveMultiFrameResult.ArchiveFrameError]()
@ -102,7 +102,10 @@ public class MessageBackupContactRecipientArchiver: MessageBackupRecipientDestin
contact.registered = recipient.isRegistered ? .REGISTERED : .NOT_REGISTERED
contact.aci = recipient.aci.map(\.rawUUID.data)
contact.pni = recipient.pni.map(\.rawUUID.data)
contact.e164 = recipient.address.e164.map(\.uint64Value)
contact.e164 = { () -> UInt64? in
guard let phoneNumberString = recipient.phoneNumber?.stringValue else { return nil }
return E164(phoneNumberString)?.uint64Value
}()
if let aci = recipient.aci {
contact.username = usernameLookupManager.fetchUsername(
@ -272,7 +275,7 @@ public class MessageBackupContactRecipientArchiver: MessageBackupRecipientDestin
// We only need to active hide, since unhidden is the default.
if contactProto.hideStory, let aci = address.aci {
let storyContext = storyStore.getOrCreateStoryContextAssociatedData(for: aci, tx: tx)
storyStore.updateStoryContext(storyContext, isHidden: true, tx: tx)
storyStore.updateStoryContext(storyContext, updateStorageService: false, isHidden: true, tx: tx)
}
profileManager.insertOtherUserProfile(

View File

@ -43,7 +43,7 @@ public class _MessageBackup_BlockingManagerWrapper: _MessageBackup_BlockingManag
}
public func addBlockedAddress(_ address: SignalServiceAddress, tx: DBWriteTransaction) {
blockingManager.addBlockedAddress(address, blockMode: .localShouldNotLeaveGroups, transaction: SDSDB.shimOnlyBridge(tx))
blockingManager.addBlockedAddress(address, blockMode: .restoreFromBackup, transaction: SDSDB.shimOnlyBridge(tx))
}
}
@ -53,11 +53,11 @@ public protocol _MessageBackup_ProfileManagerShim {
func getUserProfile(for address: SignalServiceAddress, tx: DBReadTransaction) -> OWSUserProfile?
func getLocalUsersProfile(tx: DBReadTransaction) -> OWSUserProfile?
func getUserProfileForLocalUser(tx: DBReadTransaction) -> OWSUserProfile?
func getProfileKeyData(for address: SignalServiceAddress, tx: DBReadTransaction) -> Data?
func allWhitelistedRegisteredAddresses(tx: DBReadTransaction) -> [SignalServiceAddress]
func allWhitelistedAddresses(tx: DBReadTransaction) -> [SignalServiceAddress]
func isThread(inProfileWhitelist thread: TSThread, tx: DBReadTransaction) -> Bool
@ -72,6 +72,14 @@ public protocol _MessageBackup_ProfileManagerShim {
tx: DBWriteTransaction
)
func insertLocalUserProfile(
givenName: String,
familyName: String?,
avatarUrlPath: String?,
profileKey: OWSAES256Key,
tx: DBWriteTransaction
)
func insertOtherUserProfile(
givenName: String?,
familyName: String?,
@ -82,11 +90,6 @@ public protocol _MessageBackup_ProfileManagerShim {
}
public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManagerShim {
private var userProfileWriter: UserProfileWriter {
// [Backups] TODO: add a dedicated profile writer case
return .storageService
}
private let profileManager: ProfileManager
public init(_ profileManager: ProfileManager) {
@ -97,7 +100,7 @@ public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManager
profileManager.getUserProfile(for: address, transaction: SDSDB.shimOnlyBridge(tx))
}
public func getLocalUsersProfile(tx: DBReadTransaction) -> OWSUserProfile? {
public func getUserProfileForLocalUser(tx: any DBReadTransaction) -> OWSUserProfile? {
return OWSUserProfile.getUserProfileForLocalUser(tx: SDSDB.shimOnlyBridge(tx))
}
@ -105,8 +108,8 @@ public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManager
profileManager.profileKeyData(for: address, transaction: SDSDB.shimOnlyBridge(tx))
}
public func allWhitelistedRegisteredAddresses(tx: DBReadTransaction) -> [SignalServiceAddress] {
profileManager.allWhitelistedRegisteredAddresses(tx: SDSDB.shimOnlyBridge(tx))
public func allWhitelistedAddresses(tx: any DBReadTransaction) -> [SignalServiceAddress] {
profileManager.allWhitelistedAddresses(tx: SDSDB.shimOnlyBridge(tx))
}
public func isThread(inProfileWhitelist thread: TSThread, tx: DBReadTransaction) -> Bool {
@ -116,7 +119,7 @@ public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManager
public func addToWhitelist(_ address: SignalServiceAddress, tx: DBWriteTransaction) {
profileManager.addUser(
toProfileWhitelist: address,
userProfileWriter: userProfileWriter,
userProfileWriter: .messageBackupRestore,
transaction: SDSDB.shimOnlyBridge(tx)
)
}
@ -136,13 +139,38 @@ public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManager
for: aci,
onlyFillInIfMissing: false,
shouldFetchProfile: false,
userProfileWriter: userProfileWriter,
userProfileWriter: .messageBackupRestore,
localIdentifiers: localIdentifiers,
authedAccount: .implicit(),
tx: tx
)
}
public func insertLocalUserProfile(
givenName: String,
familyName: String?,
avatarUrlPath: String?,
profileKey: OWSAES256Key,
tx: DBWriteTransaction
) {
let sdsTx = SDSDB.shimOnlyBridge(tx)
let localUserProfile = OWSUserProfile.getOrBuildUserProfileForLocalUser(
userProfileWriter: .messageBackupRestore,
tx: sdsTx
)
localUserProfile.update(
givenName: .setTo(givenName),
familyName: .setTo(familyName),
avatarUrlPath: .setTo(avatarUrlPath),
profileKey: .setTo(profileKey),
userProfileWriter: .messageBackupRestore,
transaction: sdsTx,
completion: nil
)
}
public func insertOtherUserProfile(
givenName: String?,
familyName: String?,
@ -162,6 +190,8 @@ public class _MessageBackup_ProfileManagerWrapper: _MessageBackup_ProfileManager
}
}
// MARK: - AccountData
extension MessageBackup {
public enum AccountData {}
}
@ -177,7 +207,6 @@ extension MessageBackup.AccountData {
public typealias SystemStoryManager = _MessageBackup_AccountData_SystemStoryManagerShim
public typealias ReactionManager = _MessageBackup_AccountData_ReactionManagerShim
public typealias UDManager = _MessageBackup_AccountData_UDManagerShim
public typealias UserProfile = _MessageBackup_AccountData_UserProfileShim
}
public enum Wrappers {
@ -190,7 +219,6 @@ extension MessageBackup.AccountData {
public typealias SystemStoryManager = _MessageBackup_AccountData_SystemStoryManagerWrapper
public typealias ReactionManager = _MessageBackup_AccountData_ReactionManagerWrapper
public typealias UDManager = _MessageBackup_AccountData_UDManagerWrapper
public typealias UserProfile = _MessageBackup_AccountData_UserProfileWrapper
}
}
@ -257,7 +285,7 @@ public class _MessageBackup_AccountData_PreferencesWrapper: _MessageBackup_Accou
}
}
// MARK: - SSKPreferences
// MARK: SSKPreferences
public protocol _MessageBackup_AccountData_SSKPreferencesShim {
func areLinkPreviewsEnabled(tx: DBReadTransaction) -> Bool
@ -268,8 +296,8 @@ public protocol _MessageBackup_AccountData_SSKPreferencesShim {
func shouldKeepMutedChatsArchived(tx: DBReadTransaction) -> Bool
func setShouldKeepMutedChatsArchived(value: Bool, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_SSKPreferencesWrapper: _MessageBackup_AccountData_SSKPreferencesShim {
public func areLinkPreviewsEnabled(tx: DBReadTransaction) -> Bool {
SSKPreferences.areLinkPreviewsEnabled(transaction: SDSDB.shimOnlyBridge(tx))
@ -293,7 +321,7 @@ public class _MessageBackup_AccountData_SSKPreferencesWrapper: _MessageBackup_Ac
}
}
// MARK: - SubscriptionManager
// MARK: SubscriptionManager
public protocol _MessageBackup_AccountData_SubscriptionManagerShim {
func displayBadgesOnProfile(tx: DBReadTransaction) -> Bool
@ -305,6 +333,7 @@ public protocol _MessageBackup_AccountData_SubscriptionManagerShim {
func userManuallyCancelledSubscription(tx: DBReadTransaction) -> Bool
func setUserManuallyCancelledSubscription(value: Bool, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_SubscriptionManagerWrapper: _MessageBackup_AccountData_SubscriptionManagerShim {
let subscriptionManager: SubscriptionManager
init(subscriptionManager: SubscriptionManager) {
@ -342,7 +371,7 @@ public class _MessageBackup_AccountData_SubscriptionManagerWrapper: _MessageBack
}
}
// MARK: - StoryManager
// MARK: StoryManager
public protocol _MessageBackup_AccountData_StoryManagerShim {
func hasSetMyStoriesPrivacy(tx: DBReadTransaction) -> Bool
@ -352,28 +381,29 @@ public protocol _MessageBackup_AccountData_StoryManagerShim {
func areViewReceiptsEnabled(tx: DBReadTransaction) -> Bool
func setAreViewReceiptsEnabled(value: Bool, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_StoryManagerWrapper: _MessageBackup_AccountData_StoryManagerShim {
public func hasSetMyStoriesPrivacy(tx: DBReadTransaction) -> Bool {
StoryManager.hasSetMyStoriesPrivacy(transaction: SDSDB.shimOnlyBridge(tx))
}
public func setHasSetMyStoriesPrivacy(value: Bool, tx: DBWriteTransaction) {
StoryManager.setHasSetMyStoriesPrivacy(value, transaction: SDSDB.shimOnlyBridge(tx), shouldUpdateStorageService: false)
StoryManager.setHasSetMyStoriesPrivacy(value, shouldUpdateStorageService: false, transaction: SDSDB.shimOnlyBridge(tx))
}
public func areStoriesEnabled(tx: DBReadTransaction) -> Bool {
StoryManager.areStoriesEnabled(transaction: SDSDB.shimOnlyBridge(tx))
}
public func setAreStoriesEnabled(value: Bool, tx: DBWriteTransaction) {
StoryManager.setAreStoriesEnabled(value, transaction: SDSDB.shimOnlyBridge(tx))
StoryManager.setAreStoriesEnabled(value, shouldUpdateStorageService: false, transaction: SDSDB.shimOnlyBridge(tx))
}
public func areViewReceiptsEnabled(tx: DBReadTransaction) -> Bool {
StoryManager.areViewReceiptsEnabled(transaction: SDSDB.shimOnlyBridge(tx))
}
public func setAreViewReceiptsEnabled(value: Bool, tx: DBWriteTransaction) {
StoryManager.setAreViewReceiptsEnabled(value, transaction: SDSDB.shimOnlyBridge(tx))
StoryManager.setAreViewReceiptsEnabled(value, shouldUpdateStorageService: false, transaction: SDSDB.shimOnlyBridge(tx))
}
}
// MARK: - SystemStoryManager
// MARK: SystemStoryManager
public protocol _MessageBackup_AccountData_SystemStoryManagerShim {
func isOnboardingStoryViewed(tx: DBReadTransaction) -> Bool
@ -381,6 +411,7 @@ public protocol _MessageBackup_AccountData_SystemStoryManagerShim {
func hasSeenGroupStoryEducationSheet(tx: DBReadTransaction) -> Bool
func setHasSeenGroupStoryEducationSheet(value: Bool, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_SystemStoryManagerWrapper: _MessageBackup_AccountData_SystemStoryManagerShim {
let systemStoryManager: SystemStoryManagerProtocol
init(systemStoryManager: SystemStoryManagerProtocol) {
@ -404,12 +435,13 @@ public class _MessageBackup_AccountData_SystemStoryManagerWrapper: _MessageBacku
}
}
// MARK: - ReactionManager
// MARK: ReactionManager
public protocol _MessageBackup_AccountData_ReactionManagerShim {
func customEmojiSet(tx: DBReadTransaction) -> [String]?
func setCustomEmojiSet(emojis: [String]?, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_ReactionManagerWrapper: _MessageBackup_AccountData_ReactionManagerShim {
public func customEmojiSet(tx: DBReadTransaction) -> [String]? {
ReactionManager.customEmojiSet(transaction: SDSDB.shimOnlyBridge(tx))
@ -419,12 +451,13 @@ public class _MessageBackup_AccountData_ReactionManagerWrapper: _MessageBackup_A
}
}
// MARK: - UDManager
// MARK: UDManager
public protocol _MessageBackup_AccountData_UDManagerShim {
func phoneNumberSharingMode(tx: DBReadTransaction) -> PhoneNumberSharingMode?
func setPhoneNumberSharingMode(_ mode: PhoneNumberSharingMode, tx: DBWriteTransaction)
}
public class _MessageBackup_AccountData_UDManagerWrapper: _MessageBackup_AccountData_UDManagerShim {
let udManager: OWSUDManager
init(udManager: OWSUDManager) {
@ -437,37 +470,3 @@ public class _MessageBackup_AccountData_UDManagerWrapper: _MessageBackup_Account
udManager.setPhoneNumberSharingMode(mode, updateStorageServiceAndProfile: false, tx: SDSDB.shimOnlyBridge(tx))
}
}
// MARK: - UserProfile
public protocol _MessageBackup_AccountData_UserProfileShim {
func getLocalProfile(tx: DBReadTransaction) -> OWSUserProfile?
func insertLocalProfile(
givenName: String,
familyName: String?,
avatarUrlPath: String?,
profileKey: OWSAES256Key,
tx: DBWriteTransaction
)
}
public class _MessageBackup_AccountData_UserProfileWrapper: _MessageBackup_AccountData_UserProfileShim {
public func getLocalProfile(tx: DBReadTransaction) -> OWSUserProfile? {
return OWSUserProfile.getUserProfileForLocalUser(tx: SDSDB.shimOnlyBridge(tx))
}
public func insertLocalProfile(
givenName: String,
familyName: String?,
avatarUrlPath: String?,
profileKey: OWSAES256Key,
tx: DBWriteTransaction
) {
OWSUserProfile(
address: .localUser,
givenName: givenName,
familyName: familyName,
profileKey: profileKey,
avatarUrlPath: avatarUrlPath
).anyInsert(transaction: SDSDB.shimOnlyBridge(tx))
}
}

View File

@ -9,16 +9,15 @@ import SignalCoreKit
public enum BlockMode: UInt {
case remote
case restoreFromBackup
case localShouldLeaveGroups
case localShouldNotLeaveGroups
var locallyInitiated: Bool {
switch self {
case .remote:
case .remote, .restoreFromBackup:
return false
case .localShouldLeaveGroups:
return true
case .localShouldNotLeaveGroups:
case .localShouldLeaveGroups, .localShouldNotLeaveGroups:
return true
}
}

View File

@ -213,9 +213,9 @@ public class StoryManager: NSObject {
@objc
public class func setHasSetMyStoriesPrivacy(
_ hasSet: Bool = true,
transaction: SDSAnyWriteTransaction,
shouldUpdateStorageService: Bool = true
_ hasSet: Bool,
shouldUpdateStorageService: Bool,
transaction: SDSAnyWriteTransaction
) {
guard hasSet != hasSetMyStoriesPrivacy(transaction: transaction) else {
// Don't trigger account record updates unneccesarily!

View File

@ -162,16 +162,16 @@ public class SystemStoryManager: NSObject, Dependencies, SystemStoryManagerProto
public func isOnboardingOverlayViewed(transaction: SDSAnyReadTransaction) -> Bool {
if overlayKvStore.getBool(Constants.kvStoreOnboardingOverlayViewedKey, defaultValue: false, transaction: transaction) {
return false
return true
}
if isOnboardingStoryViewed(transaction: transaction) {
// We don't sync view state for the onboarding overlay. But we can use
// viewing of the onboarding story as an imperfect proxy; if they viewed it
// that means they also definitely saw the viewer overlay.
return false
return true
}
return true
return false
}
public func setOnboardingOverlayViewed(value: Bool, transaction: SDSAnyWriteTransaction) {

View File

@ -177,7 +177,9 @@ NSString *const kNSNotificationKey_UserProfileWriter = @"kNSNotificationKey_User
}
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
localUserProfile = [OWSUserProfile getOrBuildUserProfileForLocalUserWithTx:transaction];
localUserProfile =
[OWSUserProfile getOrBuildUserProfileForLocalUserWithUserProfileWriter:UserProfileWriter_LocalUser
tx:transaction];
OWSAssertDebug(localUserProfile.profileKey);
});
@ -416,8 +418,9 @@ NSString *const kNSNotificationKey_UserProfileWriter = @"kNSNotificationKey_User
// by the time this method is called. If it's not, we've changed our caching
// logic and should re-evaluate this method.
OWSFailDebug(@"Missing local profile when setting key.");
localUserProfile = [OWSUserProfile getOrBuildUserProfileForLocalUserWithTx:transaction];
localUserProfile =
[OWSUserProfile getOrBuildUserProfileForLocalUserWithUserProfileWriter:UserProfileWriter_LocalUser
tx:transaction];
_localUserProfile = localUserProfile;
} else {
@ -931,11 +934,6 @@ NSString *const kNSNotificationKey_UserProfileWriter = @"kNSNotificationKey_User
return userProfile.avatarUrlPath;
}
- (NSArray<SignalServiceAddress *> *)allWhitelistedRegisteredAddressesWithTx:(SDSAnyReadTransaction *)tx
{
return [self objc_allWhitelistedRegisteredAddressesWithTx:tx];
}
- (nullable NSString *)profileBioForDisplayForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{

View File

@ -10,8 +10,7 @@ import SignalCoreKit
public class OWSProfileManagerSwiftValues {
fileprivate let pendingUpdateRequests = AtomicValue<[OWSProfileManager.ProfileUpdateRequest]>([], lock: .init())
public init() {
}
public init() {}
}
extension OWSProfileManager: ProfileManager, Dependencies {
@ -38,7 +37,11 @@ extension OWSProfileManager: ProfileManager, Dependencies {
) {
AssertNotOnMainThread()
let userProfile = OWSUserProfile.getOrBuildUserProfile(for: address, tx: tx)
let userProfile = OWSUserProfile.getOrBuildUserProfile(
for: address,
userProfileWriter: userProfileWriter,
tx: tx
)
var givenNameChange: OptionalChange<String> = .noChange
var familyNameChange: OptionalChange<String?> = .noChange
@ -164,9 +167,9 @@ extension OWSProfileManager: ProfileManager, Dependencies {
)
}
@objc
@available(swift, obsoleted: 1.0)
func objc_allWhitelistedRegisteredAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress] {
// MARK: -
public func allWhitelistedAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress] {
var addresses = Set<SignalServiceAddress>()
for serviceIdString in whitelistedServiceIdsStore.allKeys(transaction: tx) {
addresses.insert(SignalServiceAddress(serviceIdString: serviceIdString))
@ -175,12 +178,20 @@ extension OWSProfileManager: ProfileManager, Dependencies {
addresses.insert(SignalServiceAddress.legacyAddress(serviceId: nil, phoneNumber: phoneNumber))
}
return Array(
SignalRecipientFinder().signalRecipients(for: Array(addresses), tx: tx)
.lazy.filter { $0.isRegistered }.map { $0.address }
)
return Array(addresses)
}
public func allWhitelistedRegisteredAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress] {
return SignalRecipientFinder().signalRecipients(
for: allWhitelistedAddresses(tx: tx),
tx: tx
)
.lazy
.filter { $0.isRegistered }.map { $0.address }
}
// MARK: -
@objc
internal func rotateLocalProfileKeyIfNecessary() {
DispatchQueue.global().async {
@ -618,7 +629,11 @@ extension OWSProfileManager: ProfileManager, Dependencies {
return
}
let userProfile = OWSUserProfile.getOrBuildUserProfile(for: address, tx: SDSDB.shimOnlyBridge(tx))
let userProfile = OWSUserProfile.getOrBuildUserProfile(
for: address,
userProfileWriter: userProfileWriter,
tx: SDSDB.shimOnlyBridge(tx)
)
if onlyFillInIfMissing, userProfile.profileKey != nil {
return
@ -982,7 +997,11 @@ extension OWSProfileManager: ProfileManager, Dependencies {
await databaseStorage.awaitableWrite { tx in
self.tryToDequeueProfileChanges(profileChanges, tx: tx)
// Apply the changes to our local profile.
let userProfile = OWSUserProfile.getOrBuildUserProfile(for: .localUser, tx: tx)
let userProfile = OWSUserProfile.getOrBuildUserProfile(
for: .localUser,
userProfileWriter: .localUser,
tx: tx
)
userProfile.update(
givenName: .setTo(newGivenName?.stringValue.rawValue),
familyName: .setTo(newFamilyName?.stringValue.rawValue),
@ -1214,6 +1233,8 @@ extension OWSProfileManager: ProfileManager, Dependencies {
localIdentifiers: LocalIdentifiers,
tx: DBWriteTransaction
) {
let userProfileWriter: UserProfileWriter = .metadataUpdate
let address = OWSUserProfile.insertableAddress(for: serviceId, localIdentifiers: localIdentifiers)
switch address {
case .localUser:
@ -1221,7 +1242,11 @@ extension OWSProfileManager: ProfileManager, Dependencies {
case .otherUser:
break
}
let userProfile = OWSUserProfile.getOrBuildUserProfile(for: address, tx: SDSDB.shimOnlyBridge(tx))
let userProfile = OWSUserProfile.getOrBuildUserProfile(
for: address,
userProfileWriter: userProfileWriter,
tx: SDSDB.shimOnlyBridge(tx)
)
// lastMessagingDate is coarse; we don't need to track every single message
// sent or received. It is sufficient to update it only when the value
@ -1232,7 +1257,7 @@ extension OWSProfileManager: ProfileManager, Dependencies {
userProfile.update(
lastMessagingDate: .setTo(Date()),
userProfileWriter: .metadataUpdate,
userProfileWriter: userProfileWriter,
transaction: SDSDB.shimOnlyBridge(tx),
completion: nil
)

View File

@ -79,11 +79,23 @@ public enum OptionalAvatarChange<Wrapped: Equatable>: Equatable {
}
public protocol ProfileManager: ProfileManagerProtocol {
// MARK: -
func fetchLocalUsersProfile(authedAccount: AuthedAccount) -> Promise<FetchedProfile>
func fetchUserProfiles(for addresses: [SignalServiceAddress], tx: SDSAnyReadTransaction) -> [OWSUserProfile?]
func reuploadLocalProfile(
unsavedRotatedProfileKey: OWSAES256Key?,
mustReuploadAvatar: Bool,
authedAccount: AuthedAccount,
tx: DBWriteTransaction
) -> Promise<Void>
func downloadAndDecryptLocalUserAvatarIfNeeded(authedAccount: AuthedAccount) async throws
// MARK: -
/// Downloads & decrypts the avatar at a particular URL.
///
/// While this method de-dupes in-flight requests, it won't de-dupe requests
@ -119,13 +131,6 @@ public protocol ProfileManager: ProfileManagerProtocol {
tx: SDSAnyWriteTransaction
) -> Promise<Void>
func reuploadLocalProfile(
unsavedRotatedProfileKey: OWSAES256Key?,
mustReuploadAvatar: Bool,
authedAccount: AuthedAccount,
tx: DBWriteTransaction
) -> Promise<Void>
func didSendOrReceiveMessage(
serviceId: ServiceId,
localIdentifiers: LocalIdentifiers,
@ -150,4 +155,9 @@ public protocol ProfileManager: ProfileManagerProtocol {
localIdentifiers: LocalIdentifiers,
tx: DBWriteTransaction
)
// MARK: -
func allWhitelistedAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress]
func allWhitelistedRegisteredAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress]
}

View File

@ -115,9 +115,6 @@ typedef NS_ENUM(NSUInteger, UserProfileWriter) {
- (nullable ModelReadCacheSizeLease *)leaseCacheSize:(NSInteger)size;
- (NSArray<SignalServiceAddress *> *)allWhitelistedRegisteredAddressesWithTx:(SDSAnyReadTransaction *)tx
NS_SWIFT_NAME(allWhitelistedRegisteredAddresses(tx:));
/**
* Rotates the local profile key. Intended specifically for the
* use case of recipient hiding.

View File

@ -525,6 +525,7 @@ class StorageServiceContactRecordUpdater: StorageServiceRecordUpdater {
)
let localUserProfile = OWSUserProfile.getOrBuildUserProfile(
for: profileAddress,
userProfileWriter: .storageService,
tx: SDSDB.shimOnlyBridge(tx)
)
localUserProfile.update(
@ -1351,7 +1352,10 @@ class StorageServiceAccountRecordUpdater: StorageServiceRecordUpdater {
|| localUserProfile?.familyName != normalizedRemoteFamilyName
|| localAvatarUrl != record.avatarURL
) {
let localUserProfile = OWSUserProfile.getOrBuildUserProfileForLocalUser(tx: transaction)
let localUserProfile = OWSUserProfile.getOrBuildUserProfileForLocalUser(
userProfileWriter: .storageService,
tx: transaction
)
localUserProfile.update(
givenName: .setTo(normalizedRemoteGivenName),
familyName: .setTo(normalizedRemoteFamilyName),
@ -1555,7 +1559,7 @@ class StorageServiceAccountRecordUpdater: StorageServiceRecordUpdater {
let localHasSetMyStoriesPrivacy = StoryManager.hasSetMyStoriesPrivacy(transaction: transaction)
if !localHasSetMyStoriesPrivacy && record.myStoryPrivacyHasBeenSet {
StoryManager.setHasSetMyStoriesPrivacy(transaction: transaction, shouldUpdateStorageService: false)
StoryManager.setHasSetMyStoriesPrivacy(true, shouldUpdateStorageService: false, transaction: transaction)
}
let localHasReadOnboardingStory = systemStoryManager.isOnboardingStoryRead(transaction: transaction)

View File

@ -14,8 +14,11 @@ public class MockSSKEnvironment: NSObject {
/// Set up a mock SSK environment as well as ``DependenciesBridge``.
@objc
public static func activate() {
let testAppContext = TestAppContext()
SetCurrentAppContext(testAppContext)
let finalContinuation = AppSetup().start(
appContext: TestAppContext(),
appContext: testAppContext,
databaseStorage: try! SDSDatabaseStorage(
databaseFileUrl: SDSDatabaseStorage.grdbDatabaseFileUrl,
keychainStorage: MockKeychainStorage()

View File

@ -245,11 +245,6 @@ NS_ASSUME_NONNULL_BEGIN
// Do nothing.
}
- (NSArray<SignalServiceAddress *> *)allWhitelistedRegisteredAddressesWithTx:(SDSAnyReadTransaction *)tx
{
return @[];
}
- (void)rotateProfileKeyUponRecipientHideWithTx:(SDSAnyWriteTransaction *)tx
{
// Do nothing.

View File

@ -90,6 +90,9 @@ extension OWSFakeProfileManager: ProfileManager {
tx: DBWriteTransaction
) {
}
public func allWhitelistedAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress] { [] }
public func allWhitelistedRegisteredAddresses(tx: SDSAnyReadTransaction) -> [SignalServiceAddress] { [] }
}
#endif

View File

@ -789,12 +789,20 @@ public final class OWSUserProfile: NSObject, NSCopying, SDSCodableModel, Decodab
}
@objc
public class func getOrBuildUserProfileForLocalUser(tx: SDSAnyWriteTransaction) -> OWSUserProfile {
return getOrBuildUserProfile(for: .localUser, tx: tx)
public class func getOrBuildUserProfileForLocalUser(
userProfileWriter: UserProfileWriter,
tx: SDSAnyWriteTransaction
) -> OWSUserProfile {
return getOrBuildUserProfile(
for: .localUser,
userProfileWriter: userProfileWriter,
tx: tx
)
}
public class func getOrBuildUserProfile(
for insertableAddress: InsertableAddress,
userProfileWriter: UserProfileWriter,
tx: SDSAnyWriteTransaction
) -> OWSUserProfile {
// If we already have a profile for this address, return it.
@ -815,7 +823,7 @@ public final class OWSUserProfile: NSObject, NSCopying, SDSCodableModel, Decodab
if case .localUser = address {
userProfile.update(
profileKey: .setTo(OWSAES256Key.generateRandom()),
userProfileWriter: .localUser,
userProfileWriter: userProfileWriter,
transaction: tx,
completion: nil
)

View File

@ -92,6 +92,7 @@ class SignalRecipientTest: SSKBaseTest {
write { transaction in
let aciProfile = OWSUserProfile.getOrBuildUserProfile(
for: .otherUser(aci),
userProfileWriter: .tests,
tx: transaction
)
aciProfile.anyInsert(transaction: transaction)
@ -154,7 +155,11 @@ class SignalRecipientTest: SSKBaseTest {
let oldMessage = messageBuilder.build()
oldMessage.anyInsert(transaction: transaction)
let oldPhoneNumberProfile = OWSUserProfile.getOrBuildUserProfile(for: .otherUser(aci), tx: transaction)
let oldPhoneNumberProfile = OWSUserProfile.getOrBuildUserProfile(
for: .otherUser(aci),
userProfileWriter: .tests,
tx: transaction
)
oldPhoneNumberProfile.anyInsert(transaction: transaction)
oldPhoneNumberProfile.update(
isPhoneNumberShared: .setTo(true),
@ -183,7 +188,11 @@ class SignalRecipientTest: SSKBaseTest {
uniqueId: oldMessage.uniqueId,
transaction: transaction
)!
let newProfile = OWSUserProfile.getOrBuildUserProfile(for: .otherUser(aci), tx: transaction)
let newProfile = OWSUserProfile.getOrBuildUserProfile(
for: .otherUser(aci),
userProfileWriter: .tests,
tx: transaction
)
let newAccount = SignalAccount.anyFetch(
uniqueId: oldAccount.uniqueId,
transaction: transaction
@ -259,7 +268,11 @@ class SignalRecipientTest: SSKBaseTest {
uniqueId: oldMessage.uniqueId,
transaction: transaction
)!
let newProfile = OWSUserProfile.getOrBuildUserProfile(for: .otherUser(newAci), tx: transaction)
let newProfile = OWSUserProfile.getOrBuildUserProfile(
for: .otherUser(newAci),
userProfileWriter: .tests,
tx: transaction
)
let newAccount = SignalAccount.anyFetch(
uniqueId: oldAccount.uniqueId,
transaction: transaction

View File

@ -0,0 +1,177 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import GRDB
import LibSignalClient
import XCTest
@testable import SignalServiceKit
private extension MessageBackupIntegrationTestCase {
var depBridge: DependenciesBridge { .shared }
func skipTestForNow() throws {
// [Backup] TODO: unwind this once we've codified shared integration test cases.
throw XCTSkip("Skipped while we codify shared integration test cases.")
}
}
final class MessageBackupAccountDataTest: MessageBackupIntegrationTestCase {
func testAccountData() async throws {
try skipTestForNow()
try await runTest(backupName: "account-data") { sdsTx, tx in
XCTAssertNotNil(profileManager.localProfileKey())
switch depBridge.localUsernameManager.usernameState(tx: tx) {
case .available(let username, let usernameLink):
XCTAssertEqual(username, "boba_fett.66")
XCTAssertEqual(usernameLink.handle, UUID("61C101A2-00D5-4217-89C2-0518D8497AF0"))
XCTAssertEqual(depBridge.localUsernameManager.usernameLinkQRCodeColor(tx: tx), .olive)
case .unset, .linkCorrupted, .usernameAndLinkCorrupted:
XCTFail("Unexpected username state!")
}
XCTAssertEqual(profileManager.localGivenName(), "Boba")
XCTAssertEqual(profileManager.localFamilyName(), "Fett")
XCTAssertNil(profileManager.localProfileAvatarData())
XCTAssertNotNil(subscriptionManager.getSubscriberID(transaction: sdsTx))
XCTAssertEqual(subscriptionManager.getSubscriberCurrencyCode(transaction: sdsTx), "USD")
XCTAssertTrue(subscriptionManager.userManuallyCancelledSubscription(transaction: sdsTx))
XCTAssertTrue(receiptManager.areReadReceiptsEnabled(transaction: sdsTx))
XCTAssertTrue(preferences.shouldShowUnidentifiedDeliveryIndicators(transaction: sdsTx))
XCTAssertTrue(typingIndicatorsImpl.areTypingIndicatorsEnabled())
XCTAssertFalse(SSKPreferences.areLinkPreviewsEnabled(transaction: sdsTx))
XCTAssertEqual(depBridge.phoneNumberDiscoverabilityManager.phoneNumberDiscoverability(tx: tx), .nobody)
XCTAssertTrue(SSKPreferences.preferContactAvatars(transaction: sdsTx))
let universalExpireConfig = depBridge.disappearingMessagesConfigurationStore.fetch(for: .universal, tx: tx)
XCTAssertEqual(universalExpireConfig?.isEnabled, true)
XCTAssertEqual(universalExpireConfig?.durationSeconds, 3600)
XCTAssertEqual(ReactionManager.customEmojiSet(transaction: sdsTx), ["🏎️"])
XCTAssertTrue(subscriptionManager.displayBadgesOnProfile(transaction: sdsTx))
XCTAssertTrue(SSKPreferences.shouldKeepMutedChatsArchived(transaction: sdsTx))
XCTAssertTrue(StoryManager.hasSetMyStoriesPrivacy(transaction: sdsTx))
XCTAssertTrue(systemStoryManager.isOnboardingStoryRead(transaction: sdsTx))
XCTAssertFalse(StoryManager.areStoriesEnabled(transaction: sdsTx))
XCTAssertTrue(StoryManager.areViewReceiptsEnabled(transaction: sdsTx))
XCTAssertTrue(systemStoryManager.isOnboardingOverlayViewed(transaction: sdsTx))
XCTAssertFalse(depBridge.usernameEducationManager.shouldShowUsernameEducation(tx: tx))
XCTAssertEqual(udManager.phoneNumberSharingMode(tx: tx), .nobody)
}
}
}
final class MessageBackupContactTest: MessageBackupIntegrationTestCase {
private func assert(
recipient: SignalRecipient,
aci: Aci = .fixture,
pni: Pni = .fixture,
username: String = "han_solo.44",
phoneNumber: String = "+17735550199",
isBlocked: Bool,
isHidden: Bool,
isRegistered: Bool,
unregisteredAtTimestamp: UInt64?,
isWhitelisted: Bool,
givenName: String = "Han",
familyName: String = "Solo",
isStoryHidden: Bool = true,
sdsTx: SDSAnyReadTransaction,
tx: DBReadTransaction
) {
XCTAssertEqual(recipient.aci, aci)
XCTAssertEqual(recipient.pni, pni)
XCTAssertEqual(depBridge.usernameLookupManager.fetchUsername(forAci: recipient.aci!, transaction: tx), username)
XCTAssertEqual(recipient.phoneNumber?.stringValue, phoneNumber)
XCTAssertEqual(blockingManager.isAddressBlocked(recipient.address, transaction: sdsTx), isBlocked)
XCTAssertEqual(depBridge.recipientHidingManager.isHiddenRecipient(recipient, tx: tx), isHidden)
XCTAssertEqual(recipient.isRegistered, isRegistered)
XCTAssertEqual(recipient.unregisteredAtTimestamp, unregisteredAtTimestamp)
let recipientProfile = profileManager.getUserProfile(for: recipient.address, transaction: sdsTx)
XCTAssertNotNil(recipientProfile?.profileKey)
XCTAssertEqual(profileManager.isUser(inProfileWhitelist: recipient.address, transaction: sdsTx), isWhitelisted)
XCTAssertEqual(recipientProfile?.givenName, givenName)
XCTAssertEqual(recipientProfile?.familyName, familyName)
XCTAssertEqual(StoryStoreImpl().getOrCreateStoryContextAssociatedData(for: recipient.aci!, tx: tx).isHidden, isStoryHidden)
}
func testRegisteredBlockedContact() async throws {
try skipTestForNow()
try await runTest(backupName: "registered-blocked-contact") { sdsTx, tx in
let allRecipients = depBridge.recipientDatabaseTable.allRecipients(tx: tx)
XCTAssertEqual(allRecipients.count, 1)
assert(
recipient: allRecipients.first!,
isBlocked: true,
isHidden: true,
isRegistered: true,
unregisteredAtTimestamp: nil,
isWhitelisted: false,
sdsTx: sdsTx,
tx: tx
)
}
}
func testUnregisteredContact() async throws {
try skipTestForNow()
try await runTest(backupName: "unregistered-contact") { sdsTx, tx in
let allRecipients = depBridge.recipientDatabaseTable.allRecipients(tx: tx)
XCTAssertEqual(allRecipients.count, 1)
assert(
recipient: allRecipients.first!,
isBlocked: false,
isHidden: false,
isRegistered: false,
unregisteredAtTimestamp: 1713157772000,
isWhitelisted: true,
sdsTx: sdsTx,
tx: tx
)
}
}
}
// MARK: -
private extension RecipientDatabaseTable {
func allRecipients(tx: any DBReadTransaction) -> [SignalRecipient] {
var result = [SignalRecipient]()
enumerateAll(tx: tx) { result.append($0) }
return result
}
}
// MARK: _
private extension UUID {
init(_ string: String) {
self.init(uuidString: string)!
}
}
private extension Aci {
/// Corresponds to base64 data `QHaZXgUxQEKp5B5np33zWA==`.
static let fixture: Aci = Aci("4076995E-0531-4042-A9E4-1E67A77DF358")
convenience init(_ string: String) {
self.init(fromUUID: UUID(string))
}
}
private extension Pni {
/// Corresponds to base64 data `JvwCorpYSn2wgZ2iOXFXCg==`.
static let fixture: Pni = Pni("26FC02A2-BA58-4A7D-B081-9DA23971570A")
convenience init(_ string: String) {
self.init(fromUUID: UUID(string))
}
}

View File

@ -0,0 +1,175 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import LibSignalClient
import XCTest
@testable import SignalServiceKit
class MessageBackupIntegrationTestCase: XCTestCase {
override func setUp() {
DDLog.add(DDTTYLogger.sharedInstance!)
}
// MARK: -
private var messageBackupManager: MessageBackupManager {
DependenciesBridge.shared.messageBackupManager
}
private var localIdentifiers: LocalIdentifiers {
/// A backup doesn't contain our own local identifiers. Rather, those
/// are determined as part of registration for a backup import, and are
/// already-known for a backup export.
///
/// Consequently, we can use any local identifiers for our test
/// purposes without worrying about the contents of each test case's
/// backup file.
return .forUnitTests
}
func runTest(
backupName: String,
assertionsBlock: (SDSAnyReadTransaction, DBReadTransaction) throws -> Void
) async throws {
try await importAndAssert(
localIdentifiers: localIdentifiers,
backupUrl: backupFileUrl(named: backupName),
assertionsBlock: assertionsBlock
)
let exportedBackupUrl = try await messageBackupManager
.exportPlaintextBackup(localIdentifiers: localIdentifiers)
try await importAndAssert(
localIdentifiers: localIdentifiers,
backupUrl: exportedBackupUrl,
assertionsBlock: assertionsBlock
)
}
private func backupFileUrl(named backupName: String) -> URL {
let testBundle = Bundle(for: type(of: self))
return testBundle.url(forResource: backupName, withExtension: "binproto")!
}
private func importAndAssert(
localIdentifiers: LocalIdentifiers,
backupUrl: URL,
assertionsBlock: (SDSAnyReadTransaction, DBReadTransaction) throws -> Void
) async throws {
await initializeApp()
try await messageBackupManager.importPlaintextBackup(
fileUrl: backupUrl,
localIdentifiers: localIdentifiers
)
try NSObject.databaseStorage.read { tx in
try assertionsBlock(tx, tx.asV2Read)
}
}
// MARK: -
@MainActor
final func initializeApp() async {
let testAppContext = TestAppContext()
SetCurrentAppContext(testAppContext)
/// Note that ``SDSDatabaseStorage/grdbDatabaseFileUrl``, through a few
/// layers of abstraction, uses the "current app context" to decide
/// where to put the database,
///
/// For a ``TestAppContext`` as configured above, this will be a
/// subdirectory of our temp directory unique to the instantiation of
/// the app context.
let databaseStorage = try! SDSDatabaseStorage(
databaseFileUrl: SDSDatabaseStorage.grdbDatabaseFileUrl,
keychainStorage: MockKeychainStorage()
)
/// We use crashy versions of dependencies that should never be called
/// during backups, and no-op implementations of payments because those
/// are bound to the SignalUI target.
_ = await AppSetup().start(
appContext: testAppContext,
databaseStorage: databaseStorage,
paymentsEvents: PaymentsEventsNoop(),
mobileCoinHelper: MobileCoinHelperMock(),
callMessageHandler: CrashyMocks.MockCallMessageHandler(),
currentCallThreadProvider: CrashyMocks.MockCurrentCallThreadProvider(),
notificationPresenter: CrashyMocks.MockNotificationPresenter(),
testDependencies: AppSetup.TestDependencies(
networkManager: CrashyMocks.MockNetworkManager(),
webSocketFactory: CrashyMocks.MockWebSocketFactory()
)
).prepareDatabase().awaitable()
}
}
// MARK: -
private func failTest<T>(
_ type: T.Type,
_ function: StaticString = #function
) -> Never {
let message = "Unexpectedly called \(type)#\(function)!"
XCTFail(message)
owsFail(message)
}
/// As a rule, integration tests for message backup should not mock out their
/// dependencies as their goal is to validate how the real, production app will
/// behave with respect to Backups.
///
/// These mocks are the exceptions to that rule, and encompass managers that
/// should never be invoked during Backup import or export.
private enum CrashyMocks {
final class MockNetworkManager: NetworkManager {
override func makePromise(request: TSRequest, canUseWebSocket: Bool = false) -> Promise<any HTTPResponse> { failTest(Self.self) }
}
final class MockWebSocketFactory: WebSocketFactory {
var canBuildWebSocket: Bool { failTest(Self.self) }
func buildSocket(request: WebSocketRequest, callbackScheduler: any Scheduler) -> (any SSKWebSocket)? { failTest(Self.self) }
}
final class MockCallMessageHandler: CallMessageHandler {
func action(for envelope: SSKProtoEnvelope, callMessage: SSKProtoCallMessage, serverDeliveryTimestamp: UInt64) -> CallMessageAction { failTest(Self.self) }
func receivedOffer(_ offer: SSKProtoCallMessageOffer, from caller: (aci: Aci, deviceId: UInt32), sentAtTimestamp: UInt64, serverReceivedTimestamp: UInt64, serverDeliveryTimestamp: UInt64, tx: SDSAnyWriteTransaction) { failTest(Self.self) }
func receivedAnswer(_ answer: SSKProtoCallMessageAnswer, from caller: (aci: Aci, deviceId: UInt32)) { failTest(Self.self) }
func receivedIceUpdate(_ iceUpdate: [SSKProtoCallMessageIceUpdate], from caller: (aci: Aci, deviceId: UInt32)) { failTest(Self.self) }
func receivedHangup(_ hangup: SSKProtoCallMessageHangup, from caller: (aci: Aci, deviceId: UInt32)) { failTest(Self.self) }
func receivedBusy(_ busy: SSKProtoCallMessageBusy, from caller: (aci: Aci, deviceId: UInt32)) { failTest(Self.self) }
func receivedOpaque(_ opaque: SSKProtoCallMessageOpaque, from caller: (aci: Aci, deviceId: UInt32), serverReceivedTimestamp: UInt64, serverDeliveryTimestamp: UInt64, tx: SDSAnyReadTransaction) { failTest(Self.self) }
func receivedGroupCallUpdateMessage(_ updateMessage: SSKProtoDataMessageGroupCallUpdate, for thread: TSGroupThread, serverReceivedTimestamp: UInt64) async { failTest(Self.self) }
func externallyHandleCallMessage(envelope: SSKProtoEnvelope, plaintextData: Data, wasReceivedByUD: Bool, serverDeliveryTimestamp: UInt64, tx: SDSAnyWriteTransaction) { failTest(Self.self) }
}
final class MockCurrentCallThreadProvider: CurrentCallThreadProvider {
var currentCallThread: TSThread? { failTest(Self.self) }
}
final class MockNotificationPresenter: NotificationPresenter {
func notifyUser(forIncomingMessage: TSIncomingMessage, thread: TSThread, transaction: SDSAnyReadTransaction) { failTest(Self.self) }
func notifyUser(forIncomingMessage: TSIncomingMessage, editTarget: TSIncomingMessage, thread: TSThread, transaction: SDSAnyReadTransaction) { failTest(Self.self) }
func notifyUser(forReaction: OWSReaction, onOutgoingMessage: TSOutgoingMessage, thread: TSThread, transaction: SDSAnyReadTransaction) { failTest(Self.self) }
func notifyUser(forErrorMessage: TSErrorMessage, thread: TSThread, transaction: SDSAnyWriteTransaction) { failTest(Self.self) }
func notifyUser(forTSMessage: TSMessage, thread: TSThread, wantsSound: Bool, transaction: SDSAnyWriteTransaction) { failTest(Self.self) }
func notifyUser(forPreviewableInteraction: any TSInteraction & OWSPreviewText, thread: TSThread, wantsSound: Bool, transaction: SDSAnyWriteTransaction) { failTest(Self.self) }
func notifyTestPopulation(ofErrorMessage errorString: String) { failTest(Self.self) }
func notifyUser(forFailedStorySend: StoryMessage, to: TSThread, transaction: SDSAnyWriteTransaction) { failTest(Self.self) }
func notifyUserToRelaunchAfterTransfer(completion: (() -> Void)?) { failTest(Self.self) }
func notifyUserOfDeregistration(transaction: SDSAnyWriteTransaction) { failTest(Self.self) }
func clearAllNotifications() { failTest(Self.self) }
func cancelNotifications(threadId: String) { failTest(Self.self) }
func cancelNotifications(messageIds: [String]) { failTest(Self.self) }
func cancelNotifications(reactionId: String) { failTest(Self.self) }
func cancelNotificationsForMissedCalls(threadUniqueId: String) { failTest(Self.self) }
func cancelNotifications(for storyMessage: StoryMessage) { failTest(Self.self) }
func notifyUserOfDeregistration(tx: any DBWriteTransaction) { failTest(Self.self) }
}
}

View File

@ -0,0 +1,9 @@
#!/usr/bin/env zsh
if [ "$#" -ne 1 ]; then
length=32
else
length="$1"
fi
openssl rand -base64 "$length"

View File

@ -0,0 +1,6 @@
#!/usr/bin/env zsh
uuid=$(uuidgen)
echo "UUID: $uuid"
echo "UUID Base64: $(echo $uuid | xxd -r -p | base64)"

View File

@ -0,0 +1,42 @@
[
{
"version": "1",
"backupTimeMs": "1715636551000"
},
{
"account": {
"profileKey": "YQKRq+3DQklInaOaMcmlzZnN0m/1hzLiaONX7gB12dg=",
"username": "boba_fett.66",
"usernameLink": {
"entropy": "ZWdcc9AOsBAF47t8SkfylstlVPeJgSOIFekV2CT9LpM=",
"serverId": "YcEBogDVQheJwgUY2El68A==",
"color": "OLIVE"
},
"givenName": "Boba",
"familyName": "Fett",
"avatarUrlPath": "",
"subscriberId": "7LtoxzQzGi6jM82nR8mMRVNlImFYK0/OWuDeqE3OZRk=",
"subscriberCurrencyCode": "USD",
"subscriptionManuallyCancelled": true,
"accountSettings": {
"readReceipts": true,
"sealedSenderIndicators": true,
"typingIndicators": true,
"linkPreviews": false,
"notDiscoverableByPhoneNumber": true,
"preferContactAvatars": true,
"universalExpireTimer": 3600,
"preferredReactionEmoji": ["🏎️"],
"displayBadgesOnProfile": true,
"keepMutedChatsArchived": true,
"hasSetMyStoriesPrivacy": true,
"hasViewedOnboardingStory": true,
"storiesDisabled": true,
"storyViewReceiptsEnabled": true,
"hasSeenGroupStoryEducationSheet": true,
"hasCompletedUsernameOnboarding": true,
"phoneNumberSharingMode": "NOBODY"
}
}
}
]

View File

@ -0,0 +1,74 @@
[
{
"version": "1",
"backupTimeMs": "1715636551000"
},
{
"account": {
"profileKey": "YQKRq+3DQklInaOaMcmlzZnN0m/1hzLiaONX7gB12dg=",
"givenName": "Boba",
"familyName": "Fett",
"avatarUrlPath": "",
"subscriberId": "",
"subscriberCurrencyCode": "",
"subscriptionManuallyCancelled": false,
"accountSettings": {
"readReceipts": false,
"sealedSenderIndicators": true,
"typingIndicators": false,
"linkPreviews": false,
"notDiscoverableByPhoneNumber": false,
"preferContactAvatars": false,
"universalExpireTimer": 0,
"preferredReactionEmoji": [],
"displayBadgesOnProfile": false,
"keepMutedChatsArchived": false,
"hasSetMyStoriesPrivacy": false,
"hasViewedOnboardingStory": false,
"storiesDisabled": false,
"storyViewReceiptsEnabled": false,
"hasSeenGroupStoryEducationSheet": false,
"hasCompletedUsernameOnboarding": false,
"phoneNumberSharingMode": "NOBODY"
}
}
},
{
"recipient": {
"id": "1",
"self": {}
}
},
{
"recipient": {
"id": "2",
"contact": {
"aci": "QHaZXgUxQEKp5B5np33zWA==",
"pni": "JvwCorpYSn2wgZ2iOXFXCg==",
"username": "han_solo.44",
"e164": "17735550199",
"blocked": true,
"hidden": true,
"registered": "REGISTERED",
"unregisteredTimestamp": 0,
"profileKey": "nH0NX5+LqtIe85lAy958oyRNH9INMHFn2eb1VF6i4/o=",
"profileSharing": false,
"profileGivenName": "Han",
"profileFamilyName": "Solo",
"hideStory": true
}
}
},
{
"chat": {
"id": "1",
"recipientId": "1"
}
},
{
"chat": {
"id": "2",
"recipientId": "2"
}
}
]

View File

@ -0,0 +1,74 @@
[
{
"version": "1",
"backupTimeMs": "1715636551000"
},
{
"account": {
"profileKey": "YQKRq+3DQklInaOaMcmlzZnN0m/1hzLiaONX7gB12dg=",
"givenName": "Boba",
"familyName": "Fett",
"avatarUrlPath": "",
"subscriberId": "",
"subscriberCurrencyCode": "",
"subscriptionManuallyCancelled": false,
"accountSettings": {
"readReceipts": false,
"sealedSenderIndicators": true,
"typingIndicators": false,
"linkPreviews": false,
"notDiscoverableByPhoneNumber": false,
"preferContactAvatars": false,
"universalExpireTimer": 0,
"preferredReactionEmoji": [],
"displayBadgesOnProfile": false,
"keepMutedChatsArchived": false,
"hasSetMyStoriesPrivacy": false,
"hasViewedOnboardingStory": false,
"storiesDisabled": false,
"storyViewReceiptsEnabled": false,
"hasSeenGroupStoryEducationSheet": false,
"hasCompletedUsernameOnboarding": false,
"phoneNumberSharingMode": "NOBODY"
}
}
},
{
"recipient": {
"id": "1",
"self": {}
}
},
{
"recipient": {
"id": "2",
"contact": {
"aci": "QHaZXgUxQEKp5B5np33zWA==",
"pni": "JvwCorpYSn2wgZ2iOXFXCg==",
"username": "han_solo.44",
"e164": "17735550199",
"blocked": false,
"hidden": false,
"registered": "NOT_REGISTERED",
"unregisteredTimestamp": 1713157772000,
"profileKey": "nH0NX5+LqtIe85lAy958oyRNH9INMHFn2eb1VF6i4/o=",
"profileSharing": true,
"profileGivenName": "Han",
"profileFamilyName": "Solo",
"hideStory": true
}
}
},
{
"chat": {
"id": "1",
"recipientId": "1"
}
},
{
"chat": {
"id": "2",
"recipientId": "2"
}
}
]

View File

@ -10,7 +10,6 @@ import CocoaLumberjack
public class SSKBaseTest: XCTestCase {
public override func setUp() {
DDLog.add(DDTTYLogger.sharedInstance!)
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
}