219 lines
8.2 KiB
Swift
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
|
|
}
|
|
}
|