// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import LibSignalClient import XCTest @testable import SignalServiceKit final class OutgoingCallEventSyncMessageSerializationTest: SSKBaseTest { /// ``OutgoingCallEventSyncMessage`` used to be defined in ObjC. This test /// contains a hardcoded base64url-encoded representation of an instance of /// that class that was archived: /// ```swift /// NSKeyedArchiver.archivedData( /// withRootObject: callEventSyncMessage, /// requiringSecureCoding: false /// ) /// ``` /// before the class was migrated to Swift, using hardcoded parameters. /// /// If this test fails, then it's possible that a persisted instance of the /// legacy model will fail to deserialize. /// /// - Note /// At the time of writing, I believe the only place this could be persisted /// is on a ``MessageSenderJobRecord``, as an "invisible message", whose /// deserialization errors are handled gracefully. /// /// This test remains just in case I'm wrong. func testObjcSerializedInstanceDecodes() throws { let archivedObjCCallEventSyncMessage: Data = try! .data(fromBase64Url: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGvEBkLDEdISUpLTFBWXWNnam51dnt_jI2Oj5OUVSRudWxs3xAdDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKystListMTIzKy0rLS0tOisrLSs_LSpCKy0rRl8QE3JlY2VpdmVkQXRUaW1lc3RhbXBfEBJpc1ZpZXdPbmNlQ29tcGxldGVfEBxzdG9yZWRTaG91bGRTdGFydEV4cGlyZVRpbWVyXxAPZXhwaXJlU3RhcnRlZEF0ViRjbGFzc18QEWlzVmlld09uY2VNZXNzYWdlXxAPTVRMTW9kZWxWZXJzaW9uXxAWcmVjaXBpZW50QWRkcmVzc1N0YXRlc18QHG91dGdvaW5nTWVzc2FnZVNjaGVtYVZlcnNpb25edW5pcXVlVGhyZWFkSWRfEBVoYXNMZWdhY3lNZXNzYWdlU3RhdGVWc29ydElkXxASaXNGcm9tTGlua2VkRGV2aWNlXxASbGVnYWN5TWVzc2FnZVN0YXRlXxAQZ3JvdXBNZXRhTWVzc2FnZV8QEGV4cGlyZXNJblNlY29uZHNVZXZlbnRfEBJsZWdhY3lXYXNEZWxpdmVyZWReaXNWb2ljZU1lc3NhZ2VZZXhwaXJlc0F0XxARaXNHcm91cFN0b3J5UmVwbHldc2NoZW1hVmVyc2lvblllZGl0U3RhdGVZdGltZXN0YW1wWHVuaXF1ZUlkXxASd2FzUmVtb3RlbHlEZWxldGVkXxASc3RvcmVkTWVzc2FnZVN0YXRlXxATaGFzU3luY2VkVHJhbnNjcmlwdF1hdHRhY2htZW50SWRzgAWAA4ADgAKAGIADgAKACYAPgASAA4ACgAOAAoACgAKAEoADgAOAAoADgBeAAoAFgAaAA4ACgAOABxAACF8QJEE3QUY2MDE4LTQ1QjgtNDE1OS1CN0ZBLTJFQzU4NDcxOTQxQhMAAAGJ5pgwTl8QJDQyNkE3NjA5LTU2NzUtNDc4Ni1BOUFFLUVEM0Q5NEQ4Mzc3MdJNEU5PWk5TLm9iamVjdHOggAjSUVJTVFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiU1VYTlNPYmplY3TTV00RWFpcV05TLmtleXOhWYAKoVuADoAR0xFeX2BhYltiYWNraW5nVXVpZF8QEmJhY2tpbmdQaG9uZU51bWJlcoANgAuAANJkEWVmXE5TLnV1aWRieXRlc08QEPUX-TuWCUg-n4inEYLbHKuADNJRUmhpVk5TVVVJRKJoVdJRUmtsXxAlU2lnbmFsU2VydmljZUtpdC5TaWduYWxTZXJ2aWNlQWRkcmVzc6JtVV8QJVNpZ25hbFNlcnZpY2VLaXQuU2lnbmFsU2VydmljZUFkZHJlc3PUEW8TcHEyLStVc3RhdGVbd2FzU2VudEJ5VUSAEIAPgAKAAxAB0lFSd3hfEB9UU091dGdvaW5nTWVzc2FnZVJlY2lwaWVudFN0YXRlo3l6VV8QH1RTT3V0Z29pbmdNZXNzYWdlUmVjaXBpZW50U3RhdGVYTVRMTW9kZWzSUVJ8fVxOU0RpY3Rpb25hcnmiflVcTlNEaWN0aW9uYXJ52IARgRMdgiSDhIWGLS0tii1WY2FsbElkWHBlZXJVdWlkVHR5cGVZZGlyZWN0aW9ugBOAFoAVgAKAAoACgBSAAhEwORIAAYHNTxAQ-aLPZIRWRHittTOA3trmItJRUpCRXxART3V0Z29pbmdDYWxsRXZlbnSjknpVXxART3V0Z29pbmdDYWxsRXZlbnQQBNJRUpWWXxAcT3V0Z29pbmdDYWxsRXZlbnRTeW5jTWVzc2FnZamXmJmam5ydelVfEBxPdXRnb2luZ0NhbGxFdmVudFN5bmNNZXNzYWdlXxAWT1dTT3V0Z29pbmdTeW5jTWVzc2FnZV8QEVRTT3V0Z29pbmdNZXNzYWdlWVRTTWVzc2FnZV1UU0ludGVyYWN0aW9uWUJhc2VNb2RlbF8QE1RTWWFwRGF0YWJhc2VPYmplY3QACAARABoAJAApADIANwBJAEwAUQBTAG8AdQCyAMgA3QD8AQ4BFQEpATsBVAFzAYIBmgGhAbYBywHeAfEB9wIMAhsCJQI5AkcCUQJbAmQCeQKOAqQCsgK0ArYCuAK6ArwCvgLAAsICxALGAsgCygLMAs4C0ALSAtQC1gLYAtoC3ALeAuAC4gLkAuYC6ALqAuwC7gLvAxYDHwNGA0sDVgNXA1kDXgNpA3IDegN9A4YDjQOVA5cDmQObA50DnwOmA7IDxwPJA8sDzQPSA98D8gP0A_kEAAQDBAgEMAQzBFsEZARqBHYEeAR6BHwEfgSABIUEpwSrBM0E1gTbBOgE6wT4BQkFEAUZBR4FKAUqBSwFLgUwBTIFNAU2BTgFOwVABVMFWAVsBXAFhAWGBYsFqgW0BdMF7AYABgoGGAYiAAAAAAAAAgEAAAAAAAAAngAAAAAAAAAAAAAAAAAABjg") guard let syncMessage = try NSKeyedUnarchiver.unarchivedObject( ofClass: OutgoingCallEventSyncMessage.self, from: archivedObjCCallEventSyncMessage, requiringSecureCoding: false ) else { XCTFail("Failed to decode sync message!") return } XCTAssertEqual(syncMessage.callEvent.callId, 12345) XCTAssertEqual(syncMessage.callEvent.callType, .audio) XCTAssertEqual(syncMessage.callEvent.eventDirection, .incoming) XCTAssertEqual(syncMessage.callEvent.eventType, .accepted) XCTAssertEqual(syncMessage.callEvent.timestamp, 98765) XCTAssertEqual( syncMessage.callEvent.conversationId.asData, Data(Aci.parseFrom(aciString: "F9A2CF64-8456-4478-ADB5-3380DEDAE622")!.serviceIdBinary) ) } /// This test simply confirms that an instance of /// ``OutgoingCallEventSyncMessage`` will continue to serialize/deserialize /// correctly. This should be trivial, but Mantle makes me nervous. func testCallEventRoundTrip() throws { databaseStorage.write { tx in (DependenciesBridge.shared.registrationStateChangeManager as! RegistrationStateChangeManagerImpl).registerForTests( localIdentifiers: .init( aci: .init(fromUUID: UUID()), pni: .init(fromUUID: UUID()), e164: .init("+17735550199")! ), tx: tx.asV2Write ) } let syncMessage: OutgoingCallEventSyncMessage = write { tx in return OutgoingCallEventSyncMessage( thread: ContactThreadFactory().create(transaction: tx), event: OutgoingCallEvent( timestamp: 98765, conversationId: .individual(contactServiceId: Aci.randomForTesting()), callId: 12345, callType: .video, eventDirection: .outgoing, eventType: .notAccepted ), tx: tx ) } let archivedData = try NSKeyedArchiver.archivedData( withRootObject: syncMessage, requiringSecureCoding: false ) guard let deserializedSyncMessage = try NSKeyedUnarchiver.unarchivedObject( ofClass: OutgoingCallEventSyncMessage.self, from: archivedData, requiringSecureCoding: false ) else { XCTFail("Got nil when unarchiving!") return } XCTAssertEqual( syncMessage.callEvent.callId, deserializedSyncMessage.callEvent.callId ) XCTAssertEqual( syncMessage.callEvent.callType, deserializedSyncMessage.callEvent.callType ) XCTAssertEqual( syncMessage.callEvent.eventDirection, deserializedSyncMessage.callEvent.eventDirection ) XCTAssertEqual( syncMessage.callEvent.eventType, deserializedSyncMessage.callEvent.eventType ) XCTAssertEqual( syncMessage.callEvent.timestamp, deserializedSyncMessage.callEvent.timestamp ) XCTAssertEqual( syncMessage.callEvent.conversationId.asData, deserializedSyncMessage.callEvent.conversationId.asData ) } }