From b6598a0248dc55ea677fa511882ef4ab16898ebd Mon Sep 17 00:00:00 2001 From: Harry <109690906+harry-signal@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:23:29 -0700 Subject: [PATCH] Add OWSAttachmentInfoSerializationTest --- Signal.xcodeproj/project.pbxproj | 12 ++ .../Interactions/Quotes/TSQuotedMessage.h | 2 +- .../Interactions/Quotes/TSQuotedMessage.m | 2 +- .../OWSAttachmentInfoSerializationTest.swift | 113 ++++++++++++++++++ 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 SignalServiceKit/tests/Messages/Quotes/OWSAttachmentInfoSerializationTest.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 9d7d24065e..ec51022a63 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -853,6 +853,7 @@ 667AF9DE2B4C5824008AEE5D /* PersistableGroupUpdateItem+CVComponentSystemMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667AF9DD2B4C5824008AEE5D /* PersistableGroupUpdateItem+CVComponentSystemMessageAction.swift */; }; 667AF9E02B4C6377008AEE5D /* TSInfoMessage+LegacyPersistablegroupUpdateItemTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667AF9DF2B4C6377008AEE5D /* TSInfoMessage+LegacyPersistablegroupUpdateItemTest.swift */; }; 667AF9E22B4DC5EE008AEE5D /* GroupUpdateSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667AF9E12B4DC5EE008AEE5D /* GroupUpdateSource.swift */; }; + 667BBAD92BAA5F84006AB9DE /* OWSAttachmentInfoSerializationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667BBAD72BAA5F70006AB9DE /* OWSAttachmentInfoSerializationTest.swift */; }; 667E90D028E799D1005FE603 /* ConnectionsEducationSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667E90CF28E799D1005FE603 /* ConnectionsEducationSheetViewController.swift */; }; 667EDE6428F8D6B7001FB487 /* YYAnimatedImage+Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667EDE6328F8D6B7001FB487 /* YYAnimatedImage+Duration.swift */; }; 667EDE6628FA0372001FB487 /* StoryBadgeCountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667EDE6528FA0372001FB487 /* StoryBadgeCountManager.swift */; }; @@ -3662,6 +3663,7 @@ 667AF9DD2B4C5824008AEE5D /* PersistableGroupUpdateItem+CVComponentSystemMessageAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PersistableGroupUpdateItem+CVComponentSystemMessageAction.swift"; sourceTree = ""; }; 667AF9DF2B4C6377008AEE5D /* TSInfoMessage+LegacyPersistablegroupUpdateItemTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSInfoMessage+LegacyPersistablegroupUpdateItemTest.swift"; sourceTree = ""; }; 667AF9E12B4DC5EE008AEE5D /* GroupUpdateSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupUpdateSource.swift; sourceTree = ""; }; + 667BBAD72BAA5F70006AB9DE /* OWSAttachmentInfoSerializationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSAttachmentInfoSerializationTest.swift; sourceTree = ""; }; 667E90CF28E799D1005FE603 /* ConnectionsEducationSheetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionsEducationSheetViewController.swift; sourceTree = ""; }; 667EDE6328F8D6B7001FB487 /* YYAnimatedImage+Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "YYAnimatedImage+Duration.swift"; sourceTree = ""; }; 667EDE6528FA0372001FB487 /* StoryBadgeCountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryBadgeCountManager.swift; sourceTree = ""; }; @@ -7300,6 +7302,14 @@ path = V2; sourceTree = ""; }; + 667BBAD62BAA5F5F006AB9DE /* Quotes */ = { + isa = PBXGroup; + children = ( + 667BBAD72BAA5F70006AB9DE /* OWSAttachmentInfoSerializationTest.swift */, + ); + path = Quotes; + sourceTree = ""; + }; 6691E7EA2996E61D0032A68A /* KeyBackupService */ = { isa = PBXGroup; children = ( @@ -9520,6 +9530,7 @@ C182C4BC29E45D64007F7A7C /* Edit */, F942621F289B1B5500460798 /* Interactions */, 669FAE192B7AC8E5009EE2FE /* LinkPreview */, + 667BBAD62BAA5F5F006AB9DE /* Quotes */, F988DC11289DC8DE003B4B82 /* Reactions */, F9426222289B1B5500460798 /* Stickers */, F9426233289B1B5500460798 /* DeliveryReceiptContextTests.swift */, @@ -14146,6 +14157,7 @@ D925C7BB2B7BEC0F00AC73B0 /* OutgoingCallLogEventSyncMessageTest.swift in Sources */, D9D3216A2A8AC9B0004FC110 /* OutgoingGroupCallUpdateMessageTest.swift in Sources */, F96BB60729A528BD001C18DF /* OWS2FAManagerTest.swift in Sources */, + 667BBAD92BAA5F84006AB9DE /* OWSAttachmentInfoSerializationTest.swift in Sources */, D9708B5C29E4CCCB004306FA /* OWSDeviceManagerTest.swift in Sources */, F942623B289B1B5500460798 /* OWSDeviceProvisionerTest.swift in Sources */, D9B95A9829E8906200D7CB95 /* OWSDeviceTest.swift in Sources */, diff --git a/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.h b/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.h index b125dd1627..feaf9ff889 100644 --- a/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.h @@ -68,7 +68,7 @@ typedef NS_ENUM(NSUInteger, OWSAttachmentInfoReference) { - (instancetype)initWithAttachmentId:(NSString *_Nullable)attachmentId ofType:(OWSAttachmentInfoReference)attachmentType - contentType:(NSString *)contentType + contentType:(NSString *_Nullable)contentType sourceFilename:(NSString *_Nullable)sourceFilename; @end diff --git a/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.m b/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.m index 5108aa23a6..2f69af8930 100644 --- a/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/Quotes/TSQuotedMessage.m @@ -55,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithAttachmentId:(NSString *_Nullable)attachmentId ofType:(OWSAttachmentInfoReference)attachmentType - contentType:(NSString *)contentType + contentType:(NSString *_Nullable)contentType sourceFilename:(NSString *_Nullable)sourceFilename { self = [super init]; diff --git a/SignalServiceKit/tests/Messages/Quotes/OWSAttachmentInfoSerializationTest.swift b/SignalServiceKit/tests/Messages/Quotes/OWSAttachmentInfoSerializationTest.swift new file mode 100644 index 0000000000..8370de5f54 --- /dev/null +++ b/SignalServiceKit/tests/Messages/Quotes/OWSAttachmentInfoSerializationTest.swift @@ -0,0 +1,113 @@ +// +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +@testable import SignalServiceKit +import XCTest + +class OWSAttachmentInfoSerializationTest: XCTestCase { + + // MARK: - Hardcoded constant data + + enum HardcodedDataTestMode { + case runTest + case printStrings + + /// Toggle this to use ``testHardcodedJsonDataDecodes()`` to print + /// hardcoded strings, for example when adding new constants. + static let mode: Self = .runTest + } + + func testHardcodedArchiverDataDecodes() { + switch HardcodedDataTestMode.mode { + case .printStrings: + for (idx, (constant, _)) in OWSAttachmentInfo.constants.enumerated() { + let serializedArchiver = try! NSKeyedArchiver.archivedData( + withRootObject: constant, + requiringSecureCoding: false + ) + print("\(Self.self) constant \(idx) keyed archiver: \(serializedArchiver.base64EncodedString())") + } + + case .runTest: + for (idx, (constant, archiverData)) in OWSAttachmentInfo.constants.enumerated() { + do { + let deserialized = try NSKeyedUnarchiver.unarchivedObject( + ofClass: OWSAttachmentInfo.self, + from: archiverData, + requiringSecureCoding: false + )! + try deserialized.validate(against: constant) + } catch ValidatableModelError.failedToValidate { + XCTFail("Failed to validate NSKeyedArchiver-decoded model for constant \(idx)") + } catch { + XCTFail("Unexpected error for constant \(idx)") + } + } + } + } +} + +extension OWSAttachmentInfo { + static let constants: [(OWSAttachmentInfo, base64NSArchiverData: Data)] = [ + ( + OWSAttachmentInfo( + attachmentId: "1234", + ofType: .original, + contentType: "jpeg", + sourceFilename: "somefile.jpg" + ), + Data(base64Encoded: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGpCwwbHB0eHyAhVSRudWxs1w0ODxAREhMUFRYXGBkaViRjbGFzc11zY2hlbWFWZXJzaW9uXxAPcmF3QXR0YWNobWVudElkXnNvdXJjZUZpbGVuYW1lXxAPTVRMTW9kZWxWZXJzaW9uW2NvbnRlbnRUeXBlXmF0dGFjaG1lbnRUeXBlgAiABYAEgAOAAoAGgAcQAFxzb21lZmlsZS5qcGdUMTIzNBABVGpwZWcQAtIiIyQlWiRjbGFzc25hbWVYJGNsYXNzZXNfEBFPV1NBdHRhY2htZW50SW5mb6MmJyhfEBFPV1NBdHRhY2htZW50SW5mb1hNVExNb2RlbFhOU09iamVjdAAIABEAGgAkACkAMgA3AEkATABRAFMAXQBjAHIAeQCHAJkAqAC6AMYA1QDXANkA2wDdAN8A4QDjAOUA8gD3APkA/gEAAQUBEAEZAS0BMQFFAU4AAAAAAAACAQAAAAAAAAApAAAAAAAAAAAAAAAAAAABVw==")! + ), + ( + OWSAttachmentInfo( + attachmentId: "1234", + ofType: .originalForSend, + contentType: nil, + sourceFilename: nil + ), + Data(base64Encoded: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXGBkaVSRudWxs1Q0ODxAREhMUEhZdc2NoZW1hVmVyc2lvbl8QD3Jhd0F0dGFjaG1lbnRJZF8QD01UTE1vZGVsVmVyc2lvbl5hdHRhY2htZW50VHlwZVYkY2xhc3OABIADgAKABIAFEABUMTIzNBAB0hscHR5aJGNsYXNzbmFtZVgkY2xhc3Nlc18QEU9XU0F0dGFjaG1lbnRJbmZvox8gIV8QEU9XU0F0dGFjaG1lbnRJbmZvWE1UTE1vZGVsWE5TT2JqZWN0AAgAEQAaACQAKQAyADcASQBMAFEAUwBaAGAAawB5AIsAnQCsALMAtQC3ALkAuwC9AL8AxADGAMsA1gDfAPMA9wELARQAAAAAAAACAQAAAAAAAAAiAAAAAAAAAAAAAAAAAAABHQ==")! + ), + ( + OWSAttachmentInfo( + attachmentId: nil, + ofType: .V2, + contentType: "mp4", + sourceFilename: "file.mp4" + ), + Data(base64Encoded: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGoCwwZGhscHR5VJG51bGzWDQ4PEBESExQVFhcYXnNvdXJjZUZpbGVuYW1lViRjbGFzc11zY2hlbWFWZXJzaW9uXxAPTVRMTW9kZWxWZXJzaW9uW2NvbnRlbnRUeXBlXmF0dGFjaG1lbnRUeXBlgAOAB4AEgAKABYAGEABYZmlsZS5tcDQQAVNtcDQQBdIfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNfEBFPV1NBdHRhY2htZW50SW5mb6MjJCVfEBFPV1NBdHRhY2htZW50SW5mb1hNVExNb2RlbFhOU09iamVjdAAIABEAGgAkACkAMgA3AEkATABRAFMAXABiAG8AfgCFAJMApQCxAMAAwgDEAMYAyADKAMwAzgDXANkA3QDfAOQA7wD4AQwBEAEkAS0AAAAAAAACAQAAAAAAAAAmAAAAAAAAAAAAAAAAAAABNg==")! + ), + ( + OWSAttachmentInfo( + attachmentId: nil, + ofType: .V2, + contentType: nil, + sourceFilename: nil + ), + Data(base64Encoded: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwVFhcYVSRudWxs1A0ODxAREhMUXXNjaGVtYVZlcnNpb25fEA9NVExNb2RlbFZlcnNpb25eYXR0YWNobWVudFR5cGVWJGNsYXNzgAOAAoAEgAUQABABEAXSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzXxART1dTQXR0YWNobWVudEluZm+jHR4fXxART1dTQXR0YWNobWVudEluZm9YTVRMTW9kZWxYTlNPYmplY3QACAARABoAJAApADIANwBJAEwAUQBTAFoAYABpAHcAiQCYAJ8AoQCjAKUApwCpAKsArQCyAL0AxgDaAN4A8gD7AAAAAAAAAgEAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAQQ=")! + ), + ( + OWSAttachmentInfo( + attachmentId: nil, + ofType: .unset, + contentType: "png", + sourceFilename: "image.png" + ), + Data(base64Encoded: "YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwZGhscHVUkbnVsbNYNDg8QERITFBUWFxZec291cmNlRmlsZW5hbWVWJGNsYXNzXXNjaGVtYVZlcnNpb25fEA9NVExNb2RlbFZlcnNpb25bY29udGVudFR5cGVeYXR0YWNobWVudFR5cGWAA4AGgASAAoAFgAIQAFlpbWFnZS5wbmcQAVNwbmfSHh8gIVokY2xhc3NuYW1lWCRjbGFzc2VzXxART1dTQXR0YWNobWVudEluZm+jIiMkXxART1dTQXR0YWNobWVudEluZm9YTVRMTW9kZWxYTlNPYmplY3QACAARABoAJAApADIANwBJAEwAUQBTAFsAYQBuAH0AhACSAKQAsAC/AMEAwwDFAMcAyQDLAM0A1wDZAN0A4gDtAPYBCgEOASIBKwAAAAAAAAIBAAAAAAAAACUAAAAAAAAAAAAAAAAAAAE0")! + ) + ] + + func validate(against: OWSAttachmentInfo) throws { + guard + // Note that "rawAttachmentId" deserialized by mantle converts + // nil to empty string; use the derived "attachmentId" instead. + attachmentId == against.attachmentId, + attachmentType == against.attachmentType, + contentType == against.contentType, + sourceFilename == against.sourceFilename + else { + throw ValidatableModelError.failedToValidate + } + } +}