TM-SGNL-iOS/SignalServiceKit/tests/Profiles/ProfileManagerTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

151 lines
11 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import LibSignalClient
import XCTest
@testable import SignalServiceKit
class ProfileManagerTest: XCTestCase {
func testNormalizeRecipientInProfileWhitelist() {
let aci = Aci.constantForTesting("00000000-0000-4000-8000-000000000aaa")
let phoneNumber = E164("+16505550100")!
let pni = Pni.constantForTesting("PNI:00000000-0000-4000-8000-000000000bbb")
let serviceIdStore = KeyValueStore(collection: "serviceId")
let phoneNumberStore = KeyValueStore(collection: "phoneNumber")
let db = InMemoryDB()
func normalizeRecipient(_ recipient: SignalRecipient, tx: DBWriteTransaction) {
OWSProfileManager.swift_normalizeRecipientInProfileWhitelist(
recipient,
serviceIdStore: serviceIdStore,
phoneNumberStore: phoneNumberStore,
tx: tx
)
}
// Don't add any values unless one is already present.
db.write { tx in
normalizeRecipient(SignalRecipient(aci: aci, pni: pni, phoneNumber: phoneNumber), tx: tx)
XCTAssertFalse(serviceIdStore.hasValue(aci.serviceIdUppercaseString, transaction: tx))
XCTAssertFalse(phoneNumberStore.hasValue(phoneNumber.stringValue, transaction: tx))
XCTAssertFalse(serviceIdStore.hasValue(pni.serviceIdUppercaseString, transaction: tx))
}
// Move the PNI identifier to the phone number.
db.write { tx in
serviceIdStore.setBool(true, key: pni.serviceIdUppercaseString, transaction: tx)
normalizeRecipient(SignalRecipient(aci: nil, pni: pni, phoneNumber: phoneNumber), tx: tx)
XCTAssertFalse(serviceIdStore.hasValue(aci.serviceIdUppercaseString, transaction: tx))
XCTAssertTrue(phoneNumberStore.hasValue(phoneNumber.stringValue, transaction: tx))
XCTAssertFalse(serviceIdStore.hasValue(pni.serviceIdUppercaseString, transaction: tx))
}
// Clear lower priority identifiers when multiple are present.
db.write { tx in
serviceIdStore.setBool(true, key: aci.serviceIdUppercaseString, transaction: tx)
normalizeRecipient(SignalRecipient(aci: aci, pni: pni, phoneNumber: phoneNumber), tx: tx)
XCTAssertTrue(serviceIdStore.hasValue(aci.serviceIdUppercaseString, transaction: tx))
XCTAssertFalse(phoneNumberStore.hasValue(phoneNumber.stringValue, transaction: tx))
XCTAssertFalse(serviceIdStore.hasValue(pni.serviceIdUppercaseString, transaction: tx))
}
// Keep the highest priority identifier if it's already present.
db.write { tx in
normalizeRecipient(SignalRecipient(aci: aci, pni: pni, phoneNumber: phoneNumber), tx: tx)
XCTAssertTrue(serviceIdStore.hasValue(aci.serviceIdUppercaseString, transaction: tx))
XCTAssertFalse(phoneNumberStore.hasValue(phoneNumber.stringValue, transaction: tx))
XCTAssertFalse(serviceIdStore.hasValue(pni.serviceIdUppercaseString, transaction: tx))
}
}
func testEncodeDecodeProfileChanges() throws {
let testCases: [(PendingProfileUpdate, String)] = [
(
PendingProfileUpdate(
profileGivenName: .setTo(.init(truncating: "Alice")!),
profileFamilyName: .setTo(.init(truncating: "Johnson")!),
profileBio: .setTo("A short bio."),
profileBioEmoji: .setTo("💙"),
profileAvatarData: .setTo(Data(1...3)),
visibleBadgeIds: .setTo(["BOOST"]),
userProfileWriter: .registration
),
"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGvEA8LDCEiIyQlJicsLTM3ODxVJG51bGzaDQ4PEBESExQVFhcYGRobHB0eHyBfEA9wcm9maWxlQmlvRW1vamlacHJvZmlsZUJpb1YkY2xhc3NSaWRfEBh1bnNhdmVkUm90YXRlZFByb2ZpbGVLZXlfEBF1c2VyUHJvZmlsZVdyaXRlcl8QD3Zpc2libGVCYWRnZUlkc18QEXByb2ZpbGVBdmF0YXJEYXRhXxARcHJvZmlsZUZhbWlseU5hbWVfEBBwcm9maWxlR2l2ZW5OYW1lgAaABYAOgAKACxAEgAiAB4AEgANfECQwMDAwMDAwMC0wMDAwLTQwMDAtQTAwMC0wMDAwMDAwMDAwMDBVQWxpY2VXSm9obnNvblxBIHNob3J0IGJpby5i2D3cmUMBAgPSKA8pK1pOUy5vYmplY3RzoSqACYAKVUJPT1NU0i4vMDFaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0FycmF5ojAyWE5TT2JqZWN00jQPNTZXa2V5RGF0YYAMgA1PECABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fINIuLzk6XE9XU0FFUzI1NktleaI7MlxPV1NBRVMyNTZLZXnSLi89Pl8QJFNpZ25hbE1lc3NhZ2luZy5QZW5kaW5nUHJvZmlsZVVwZGF0ZaI/Ml8QJFNpZ25hbE1lc3NhZ2luZy5QZW5kaW5nUHJvZmlsZVVwZGF0ZQAIABEAGgAkACkAMgA3AEkATABRAFMAZQBrAIAAkgCdAKQApwDCANYA6AD8ARABIwElAScBKQErAS0BLwExATMBNQE3AV4BZAFsAXkBfgGCAYcBkgGUAZYBmAGeAaMBrgG3Ab8BwgHLAdAB2AHaAdwB/wIEAhECFAIhAiYCTQJQAAAAAAAAAgEAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAnc="
),
(
PendingProfileUpdate(
profileGivenName: .setTo(.init(truncating: "Alice")!),
profileFamilyName: .setTo(.init(truncating: "Johnson")!),
profileBio: .setTo("A short bio."),
profileBioEmoji: .setTo("💙"),
profileAvatarData: .setTo(Data(1...3)),
visibleBadgeIds: .setTo(["BOOST"]),
userProfileWriter: .registration
),
"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGsCwwfICEiIyQlKisxVSRudWxs2Q0ODxAREhMUFRYXGBkaGxwdHl8QD3Byb2ZpbGVCaW9FbW9qaVpwcm9maWxlQmlvViRjbGFzc1JpZF8QEXVzZXJQcm9maWxlV3JpdGVyXxAPdmlzaWJsZUJhZGdlSWRzXxARcHJvZmlsZUF2YXRhckRhdGFfEBFwcm9maWxlRmFtaWx5TmFtZV8QEHByb2ZpbGVHaXZlbk5hbWWABoAFgAuAAhAEgAiAB4AEgANfECQwMDAwMDAwMC0wMDAwLTQwMDAtQTAwMC0wMDAwMDAwMDAwMDBVQWxpY2VXSm9obnNvblxBIHNob3J0IGJpby5i2D3cmUMBAgPSJg8nKVpOUy5vYmplY3RzoSiACYAKVUJPT1NU0iwtLi9aJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0FycmF5oi4wWE5TT2JqZWN00iwtMjNfECRTaWduYWxNZXNzYWdpbmcuUGVuZGluZ1Byb2ZpbGVVcGRhdGWiNDBfECRTaWduYWxNZXNzYWdpbmcuUGVuZGluZ1Byb2ZpbGVVcGRhdGUACAARABoAJAApADIANwBJAEwAUQBTAGAAZgB5AIsAlgCdAKAAtADGANoA7gEBAQMBBQEHAQkBCwENAQ8BEQETAToBQAFIAVUBWgFeAWMBbgFwAXIBdAF6AX8BigGTAZsBngGnAawB0wHWAAAAAAAAAgEAAAAAAAAANQAAAAAAAAAAAAAAAAAAAf0="
),
(
PendingProfileUpdate(
profileGivenName: .noChange,
profileFamilyName: .noChange,
profileBio: .noChange,
profileBioEmoji: .noChange,
profileAvatarData: .noChange,
visibleBadgeIds: .noChange,
userProfileWriter: .localUser
),
"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwdHlUkbnVsbNgNDg8QERITFBUWFxcXFxsXViRjbGFzc1JpZF8QFnByb2ZpbGVCaW9FbW9qaUNoYW5nZWRfEBhwcm9maWxlRmFtaWx5TmFtZUNoYW5nZWRfEBFwcm9maWxlQmlvQ2hhbmdlZF8QF3Byb2ZpbGVHaXZlbk5hbWVDaGFuZ2VkXxARdXNlclByb2ZpbGVXcml0ZXJfEBhwcm9maWxlQXZhdGFyRGF0YUNoYW5nZWSAA4ACCAgICBAACF8QJDAwMDAwMDAwLTAwMDAtNDAwMC1BMDAwLTAwMDAwMDAwMDAwMNIfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNfECRTaWduYWxNZXNzYWdpbmcuUGVuZGluZ1Byb2ZpbGVVcGRhdGWiIyRfECRTaWduYWxNZXNzYWdpbmcuUGVuZGluZ1Byb2ZpbGVVcGRhdGVYTlNPYmplY3QACAARABoAJAApADIANwBJAEwAUQBTAFgAXgBvAHYAeQCSAK0AwQDbAO8BCgEMAQ4BDwEQAREBEgEUARUBPAFBAUwBVQF8AX8BpgAAAAAAAAIBAAAAAAAAACUAAAAAAAAAAAAAAAAAAAGv"
),
(
PendingProfileUpdate(
profileGivenName: .noChange,
profileFamilyName: .noChange,
profileBio: .noChange,
profileBioEmoji: .noChange,
profileAvatarData: .noChangeButMustReupload,
visibleBadgeIds: .noChange,
userProfileWriter: .localUser
),
"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwfIFUkbnVsbNkNDg8QERITFBUWFxgYGBgYHR5WJGNsYXNzUmlkXxAYcHJvZmlsZUF2YXRhckRhdGFDaGFuZ2VkXxAYcHJvZmlsZUZhbWlseU5hbWVDaGFuZ2VkXxARcHJvZmlsZUJpb0NoYW5nZWRfEBZwcm9maWxlQmlvRW1vamlDaGFuZ2VkXxAXcHJvZmlsZUdpdmVuTmFtZUNoYW5nZWRfEBF1c2VyUHJvZmlsZVdyaXRlcl8QHXByb2ZpbGVBdmF0YXJEYXRhTXVzdFJldXBsb2FkgAOAAggICAgIEAAJXxAkMDAwMDAwMDAtMDAwMC00MDAwLUEwMDAtMDAwMDAwMDAwMDAw0iEiIyRaJGNsYXNzbmFtZVgkY2xhc3Nlc18QJVNpZ25hbFNlcnZpY2VLaXQuUGVuZGluZ1Byb2ZpbGVVcGRhdGWiJSZfECVTaWduYWxTZXJ2aWNlS2l0LlBlbmRpbmdQcm9maWxlVXBkYXRlWE5TT2JqZWN0AAgAEQAaACQAKQAyADcASQBMAFEAUwBYAF4AcQB4AHsAlgCxAMUA3gD4AQwBLAEuATABMQEyATMBNAE1ATcBOAFfAWQBbwF4AaABowHLAAAAAAAAAgEAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAdQ="
),
]
let expectedId = UUID(uuidString: "00000000-0000-4000-A000-000000000000")!
for (expectedValue, encodedValue) in testCases {
// Test that it can be encoded/decoded.
let decodedEncodedValue = try Self.deserialize(data: Self.serialize(expectedValue))
XCTAssertEqual(decodedEncodedValue.id, expectedValue.id)
XCTAssertEqual(decodedEncodedValue.profileGivenName, expectedValue.profileGivenName)
XCTAssertEqual(decodedEncodedValue.profileFamilyName, expectedValue.profileFamilyName)
XCTAssertEqual(decodedEncodedValue.profileBio, expectedValue.profileBio)
XCTAssertEqual(decodedEncodedValue.profileBioEmoji, expectedValue.profileBioEmoji)
XCTAssertEqual(decodedEncodedValue.profileAvatarData, expectedValue.profileAvatarData)
XCTAssertEqual(decodedEncodedValue.visibleBadgeIds, expectedValue.visibleBadgeIds)
XCTAssertEqual(decodedEncodedValue.userProfileWriter, expectedValue.userProfileWriter)
// Test a stable decoding.
let decodedValue = try Self.deserialize(data: XCTUnwrap(Data(base64Encoded: encodedValue)))
XCTAssertEqual(decodedValue.id, expectedId)
XCTAssertEqual(decodedValue.profileGivenName, expectedValue.profileGivenName)
XCTAssertEqual(decodedValue.profileFamilyName, expectedValue.profileFamilyName)
XCTAssertEqual(decodedValue.profileBio, expectedValue.profileBio)
XCTAssertEqual(decodedValue.profileBioEmoji, expectedValue.profileBioEmoji)
XCTAssertEqual(decodedValue.profileAvatarData, expectedValue.profileAvatarData)
XCTAssertEqual(decodedValue.visibleBadgeIds, expectedValue.visibleBadgeIds)
XCTAssertEqual(decodedValue.userProfileWriter, expectedValue.userProfileWriter)
}
}
private static func serialize(_ pendingProfileUpdate: PendingProfileUpdate) throws -> Data {
try NSKeyedArchiver.archivedData(withRootObject: pendingProfileUpdate, requiringSecureCoding: false)
}
private static func deserialize(data: Data) throws -> PendingProfileUpdate {
NSKeyedUnarchiver.setClass(PendingProfileUpdate.self, forClassName: "SignalMessaging.PendingProfileUpdate")
return try XCTUnwrap(NSKeyedUnarchiver.unarchivedObject(ofClass: PendingProfileUpdate.self, from: data))
}
}