TM-SGNL-iOS/Signal/test/Provisioning/ProvisioningCoordinatorTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

219 lines
8.2 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public import XCTest
import LibSignalClient
@testable import Signal
@testable import SignalServiceKit
public class ProvisioningCoordinatorTest: XCTestCase {
typealias Mocks = ProvisioningCoordinatorImpl.Mocks
private var provisioningCoordinator: ProvisioningCoordinatorImpl!
private var chatConnectionManagerMock: ChatConnectionManagerMock!
private var identityManagerMock: MockIdentityManager!
private var messageFactoryMock: Mocks.MessageFactory!
private var prekeyManagerMock: MockPreKeyManager!
private var profileManagerMock: Mocks.ProfileManager!
private var pushRegistrationManagerMock: Mocks.PushRegistrationManager!
private var receiptManagerMock: Mocks.ReceiptManager!
private var registrationStateChangeManagerMock: MockRegistrationStateChangeManager!
private var signalServiceMock: OWSSignalServiceMock!
private var storageServiceManagerMock: FakeStorageServiceManager!
private var svrMock: SecureValueRecoveryMock!
private var svrKeyDeriverMock: SVRKeyDeriverMock!
private var syncManagerMock: Mocks.SyncManager!
private var threadStoreMock: MockThreadStore!
private var tsAccountManagerMock: MockTSAccountManager!
private var udManagerMock: Mocks.UDManager!
public override func setUp() async throws {
let mockDb = InMemoryDB()
let recipientDbTable = MockRecipientDatabaseTable()
let recipientFetcher = RecipientFetcherImpl(recipientDatabaseTable: recipientDbTable)
let recipientIdFinder = RecipientIdFinder(
recipientDatabaseTable: recipientDbTable,
recipientFetcher: recipientFetcher
)
self.identityManagerMock = .init(recipientIdFinder: recipientIdFinder)
self.chatConnectionManagerMock = .init()
self.messageFactoryMock = .init()
self.prekeyManagerMock = .init()
self.profileManagerMock = .init()
self.pushRegistrationManagerMock = .init()
self.receiptManagerMock = .init()
self.registrationStateChangeManagerMock = .init()
self.signalServiceMock = .init()
self.storageServiceManagerMock = .init()
self.svrMock = .init()
self.svrKeyDeriverMock = .init()
self.syncManagerMock = .init()
self.threadStoreMock = .init()
self.tsAccountManagerMock = .init()
self.udManagerMock = .init()
self.provisioningCoordinator = ProvisioningCoordinatorImpl(
chatConnectionManager: chatConnectionManagerMock,
db: mockDb,
identityManager: identityManagerMock,
linkAndSyncManager: MockLinkAndSyncManager(),
messageFactory: messageFactoryMock,
mrbkStore: MediaRootBackupKeyStore(),
preKeyManager: prekeyManagerMock,
profileManager: profileManagerMock,
pushRegistrationManager: pushRegistrationManagerMock,
receiptManager: receiptManagerMock,
registrationStateChangeManager: registrationStateChangeManagerMock,
signalService: signalServiceMock,
storageServiceManager: storageServiceManagerMock,
svr: svrMock,
svrKeyDeriver: svrKeyDeriverMock,
syncManager: syncManagerMock,
threadStore: threadStoreMock,
tsAccountManager: tsAccountManagerMock,
udManager: udManagerMock
)
tsAccountManagerMock.registrationStateMock = { .unregistered }
}
public func testProvisioning() async throws {
let provisioningMessage = ProvisionMessage(
aci: .randomForTesting(),
phoneNumber: "+17875550100",
pni: .randomForTesting(),
aciIdentityKeyPair: try keyPairForTesting(),
pniIdentityKeyPair: try keyPairForTesting(),
profileKey: .generateRandom(),
masterKey: Randomness.generateRandomBytes(SVR.masterKeyLengthBytes),
mrbk: Randomness.generateRandomBytes(MediaRootBackupKeyStore.mediaRootBackupKeyLength),
ephemeralBackupKey: nil,
areReadReceiptsEnabled: true,
primaryUserAgent: nil,
provisioningCode: "1234",
provisioningVersion: 1
)
let deviceName = "test device"
let deviceId = UInt32.random(in: 1...5)
let mockSession = UrlSessionMock()
let verificationResponse = ProvisioningServiceResponses.VerifySecondaryDeviceResponse(
pni: provisioningMessage.pni!,
deviceId: deviceId
)
mockSession.responder = { request in
if request.url!.absoluteString.hasSuffix("v1/devices/link") {
return try! JSONEncoder().encode(verificationResponse)
} else if request.url!.absoluteString.hasSuffix("v1/devices/capabilities") {
return Data()
} else {
XCTFail("Unexpected request!")
return Data()
}
}
signalServiceMock.mockUrlSessionBuilder = { (signalServiceInfo, _, _) in
XCTAssertEqual(
signalServiceInfo.baseUrl,
SignalServiceType.mainSignalServiceIdentified.signalServiceInfo().baseUrl
)
return mockSession
}
pushRegistrationManagerMock.mockRegistrationId = .init(apnsToken: "apn")
var didSetLocalIdentifiers = false
registrationStateChangeManagerMock.didProvisionSecondaryMock = { e164, aci, pni, _, storedDeviceId in
XCTAssertEqual(e164.stringValue, provisioningMessage.phoneNumber)
XCTAssertEqual(aci, provisioningMessage.aci)
XCTAssertEqual(pni, provisioningMessage.pni)
XCTAssertEqual(storedDeviceId, deviceId)
didSetLocalIdentifiers = true
}
let provisioningResult = await provisioningCoordinator.completeProvisioning(
provisionMessage: provisioningMessage,
deviceName: deviceName,
progressViewModel: LinkAndSyncProgressViewModel(),
shouldRetry: { _ in false }
)
XCTAssert(didSetLocalIdentifiers)
XCTAssert(prekeyManagerMock.didFinalizeRegistrationPrekeys)
XCTAssertEqual(profileManagerMock.localProfileKeyMock, provisioningMessage.profileKey)
XCTAssertEqual(identityManagerMock.identityKeyPairs[.aci], provisioningMessage.aciIdentityKeyPair)
XCTAssertEqual(identityManagerMock.identityKeyPairs[.pni], provisioningMessage.pniIdentityKeyPair)
XCTAssertEqual(svrMock.syncedMasterKey, provisioningMessage.masterKey)
switch provisioningResult {
case .success:
break
default:
XCTFail("Got failure result!")
}
}
private func keyPairForTesting() throws -> ECKeyPair {
let privateKey = try PrivateKey(Array(repeating: 0, count: 31) + [.random(in: 0..<0x48)])
return ECKeyPair(IdentityKeyPair(publicKey: privateKey.publicKey, privateKey: privateKey))
}
}
extension ProvisioningCoordinatorTest {
class UrlSessionMock: BaseOWSURLSessionMock {
var responder: ((TSRequest) -> Data)?
override func performRequest(_ rawRequest: TSRequest) async throws -> any HTTPResponse {
let responseBody = responder!(rawRequest)
return HTTPResponseImpl(
requestUrl: rawRequest.url!,
status: 200,
headers: OWSHttpHeaders(),
bodyData: responseBody
)
}
}
}
private class MockLinkAndSyncManager: LinkAndSyncManager {
func isLinkAndSyncEnabledOnPrimary(tx: DBReadTransaction) -> Bool {
true
}
func setIsLinkAndSyncEnabledOnPrimary(_ isEnabled: Bool, tx: DBWriteTransaction) {}
func generateEphemeralBackupKey() -> BackupKey {
return .forTesting()
}
func waitForLinkingAndUploadBackup(
ephemeralBackupKey: BackupKey,
tokenId: DeviceProvisioningTokenId,
progress: OWSProgressSink
) async throws(PrimaryLinkNSyncError) {
return
}
func waitForBackupAndRestore(
localIdentifiers: LocalIdentifiers,
auth: ChatServiceAuth,
ephemeralBackupKey: BackupKey,
progress: OWSProgressSink
) async throws(SecondaryLinkNSyncError) {
return
}
}