FBSDKCoreKit/FBSDKCoreKitTests/ProfileTests.swift (1,078 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
@testable import FBSDKCoreKit
import TestTools
import XCTest
final class ProfileTests: XCTestCase {
// swiftlint:disable implicitly_unwrapped_optional
var dataStore: UserDefaultsSpy!
var notificationCenter: TestNotificationCenter!
var settings: TestSettings!
var urlHoster: TestURLHoster!
var profile: Profile!
var testGraphRequest: TestGraphRequest!
var result: [String: Any]!
var imageURL: URL!
// swiftlint:enable implicitly_unwrapped_optional
let stubbedURL = URL(string: "testProfile.com")! // swiftlint:disable:this force_unwrapping
let accessTokenKey = "access_token"
let pictureModeKey = "type"
let widthKey = "width"
let heightKey = "height"
let sdkVersion = "100"
let validClientToken = "Foo"
static let validSquareSize = CGSize(width: 100, height: 100)
static let validNonSquareSize = CGSize(width: 10, height: 20)
override func setUp() {
super.setUp()
dataStore = UserDefaultsSpy()
notificationCenter = TestNotificationCenter()
settings = TestSettings()
urlHoster = TestURLHoster(url: stubbedURL)
profile = SampleUserProfiles.createValid()
testGraphRequest = TestGraphRequest()
result = [
"id": profile.userID,
"first_name": profile.firstName as Any,
"middle_name": profile.middleName as Any,
"last_name": profile.lastName as Any,
"name": profile.name as Any,
"link": profile.linkURL as Any,
"email": profile.email as Any,
]
Settings.shared.graphAPIVersion = sdkVersion
TestAccessTokenWallet.reset()
Profile.resetCurrentProfileCache()
Profile.configure(
dataStore: dataStore,
accessTokenProvider: TestAccessTokenWallet.self,
notificationCenter: notificationCenter,
settings: settings,
urlHoster: urlHoster
)
}
override func tearDown() {
super.tearDown()
Profile.reset()
TestAccessTokenWallet.reset()
Profile.resetCurrentProfileCache()
}
private func makeImageURL(
mode: Profile.PictureMode = .normal,
size: CGSize = ProfileTests.validSquareSize
) throws {
imageURL = try XCTUnwrap(
profile.imageURL(
forMode: mode,
size: size
)
)
}
func testCreatingImageURL() throws {
try makeImageURL()
XCTAssertEqual(urlHoster.capturedHostPrefix, "graph")
XCTAssertEqual(urlHoster.capturedPath, "\(profile.userID)/picture")
XCTAssertNotNil(urlHoster.capturedQueryParameters)
XCTAssertEqual(imageURL, urlHoster.stubbedURL)
}
// MARK: - Creating Image URL
func testCreatingImageURLWithNoAccessTokenNoClientToken() throws {
try makeImageURL()
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"normal",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingImageURLWithClientTokenNoAccessToken() throws {
settings.clientToken = validClientToken
try makeImageURL()
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"normal",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[accessTokenKey],
"Foo",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingImageURLWithAccessTokenNoClientToken() throws {
TestAccessTokenWallet.currentAccessToken = SampleAccessTokens.validToken
try makeImageURL()
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"normal",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[accessTokenKey],
SampleAccessTokens.validToken.tokenString,
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingImageURLWithAccessTokenAndClientToken() throws {
TestAccessTokenWallet.currentAccessToken = SampleAccessTokens.validToken
settings.clientToken = validClientToken
try makeImageURL()
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"normal",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"100",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[accessTokenKey],
SampleAccessTokens.validToken.tokenString,
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingEnumWithSmallMode() throws {
try makeImageURL(mode: .small, size: Self.validNonSquareSize)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"small",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"20",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingEnumWithAlbumMode() throws {
try makeImageURL(mode: .album, size: Self.validNonSquareSize)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"album",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"20",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingEnumWithLargeMode() throws {
try makeImageURL(mode: .large, size: Self.validNonSquareSize)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"large",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"20",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingEnumWithSquareMode() throws {
try makeImageURL(mode: .square, size: Self.validNonSquareSize)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"square",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"20",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
// MARK: - Size Validations
func testCreatingImageURLWithNoSize() throws {
try makeImageURL(mode: .square, size: .zero)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"square",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"0",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"0",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingSquareImageURLWithNonSquareSize() throws {
try makeImageURL(mode: .square, size: Self.validNonSquareSize)
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"square",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"20",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
func testCreatingSquareImageURLWithNegativeSize() throws {
try makeImageURL(mode: .square, size: CGSize(width: -10, height: -10))
let queryItems = try XCTUnwrap(urlHoster.capturedQueryParameters)
XCTAssertEqual(
queryItems[pictureModeKey],
"square",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[widthKey],
"-10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
XCTAssertEqual(
queryItems[heightKey],
"-10",
"Should add the expected query items to a url when creating a url for fetching a profile image"
)
}
// MARK: - Profile Loading
func testGraphPathForProfileLoadWithLinkPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,link",
permissions: ["user_link"]
)
}
func testGraphPathForProfileLoadWithNoPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name",
permissions: []
)
}
func testGraphPathForProfileLoadWithEmailPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,email",
permissions: ["email"]
)
}
func testGraphPathForProfileLoadWithFriendsPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,friends",
permissions: ["user_friends"]
)
}
func testGraphPathForProfileLoadWithBirthdayPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,birthday",
permissions: ["user_birthday"]
)
}
func testGraphPathForProfileLoadWithAgeRangePermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,age_range",
permissions: ["user_age_range"]
)
}
func testGraphPathForProfileLoadWithHometownPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,hometown",
permissions: ["user_hometown"]
)
}
func testGraphPathForProfileLoadWithLocationPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,location",
permissions: ["user_location"]
)
}
func testGraphPathForProfileLoadWithGenderPermission() {
verfiyGraphPath(
expectedPath: "me?fields=id,first_name,middle_name,last_name,name,gender",
permissions: ["user_gender"]
)
}
func testLoadingProfile() throws {
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, sampleGraphResult(), nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
let expectedProfile = SampleUserProfiles.createValid()
XCTAssertEqual(profile.firstName, expectedProfile.firstName)
XCTAssertEqual(profile.middleName, expectedProfile.middleName)
XCTAssertEqual(profile.lastName, expectedProfile.lastName)
XCTAssertEqual(profile.name, expectedProfile.name)
XCTAssertEqual(profile.userID, expectedProfile.userID)
XCTAssertEqual(profile.linkURL, expectedProfile.linkURL)
XCTAssertEqual(profile.email, expectedProfile.email)
XCTAssertEqual(profile.friendIDs, expectedProfile.friendIDs)
XCTAssertEqual(
formatter.string(from: profile.birthday ?? Date()),
"01/01/1990"
)
XCTAssertEqual(profile.ageRange, expectedProfile.ageRange)
XCTAssertEqual(profile.hometown, expectedProfile.hometown)
XCTAssertEqual(profile.gender, expectedProfile.gender)
XCTAssertEqual(profile.location, expectedProfile.location)
}
func testLoadingProfileWithInvalidLink() throws {
result["link"] = " "
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.linkURL)
}
func testProfileNilWithNilAccessToken() {
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: nil,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
XCTAssertEqual(
testGraphRequest.startCallCount,
0,
"Should not fetch a profile if there is no access token"
)
XCTAssertNil(capturedProfile)
XCTAssertNil(capturedError)
}
func testLoadingProfileWithNoFriends() throws {
result["friends"] = ["data": []]
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.friendIDs)
}
func testLoadingProfileWithInvalidFriends() throws {
result["friends"] = " "
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.friendIDs)
}
func testLoadingProfileWithInvalidBirthday() throws {
result["birthday"] = 123
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.birthday)
}
func testLoadingProfileWithInvalidAgeRange() throws {
result["ageRange"] = "ageRange"
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.ageRange)
}
func testLoadingProfileWithInvalidHometown() throws {
result["hometown"] = "hometown"
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.hometown)
}
func testLoadingProfileWithInvalidLocation() throws {
result["location"] = "location"
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.location)
}
func testLoadingProfileWithInvalidGender() throws {
result["gender"] = [:]
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertNil(profile.gender)
}
func testProfileNotRefreshedIfNotStale() throws {
Profile.current = profile
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: nil,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
XCTAssertEqual(
testGraphRequest.startCallCount,
0,
"Should not fetch a profile if it is not stale"
)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
let expectedProfile = SampleUserProfiles.createValid()
XCTAssertEqual(profile.firstName, expectedProfile.firstName)
XCTAssertEqual(profile.middleName, expectedProfile.middleName)
XCTAssertEqual(profile.lastName, expectedProfile.lastName)
XCTAssertEqual(profile.name, expectedProfile.name)
XCTAssertEqual(profile.userID, expectedProfile.userID)
XCTAssertEqual(profile.linkURL, expectedProfile.linkURL)
XCTAssertEqual(profile.email, expectedProfile.email)
}
func testLoadingProfileWithLimitedProfileWithoutToken() throws {
let expected = SampleUserProfiles.validLimited
Profile.current = expected
let request = TestGraphRequest()
var capturedProfile: Profile?
var capturedError: Error?
Profile.load(
token: nil,
request: testGraphRequest
) {
capturedProfile = $0
capturedError = $1
}
XCTAssertEqual(
request.startCallCount,
0,
"Should not fetch a profile if there is no access token"
)
XCTAssertNil(capturedError)
let profile = try XCTUnwrap(capturedProfile, "capturedProfile should not be nil")
XCTAssertEqual(
profile,
expected,
"Should return the current profile synchronously"
)
}
func testLoadingProfileWithLimitedProfileWithToken() {
Profile.current = SampleUserProfiles.validLimited
let request = TestGraphRequest()
Profile.load(
token: SampleAccessTokens.validToken,
request: request
) { _, _ in
XCTFail("Should not invoke the completion")
}
XCTAssertEqual(
request.startCallCount,
1,
"Should fetch a profile if it is limited and there is an access token"
)
}
func testLoadingProfileWithExpiredNonLimitedProfileWithToken() {
let expected = SampleUserProfiles.createValid(isExpired: true)
Profile.current = expected
let request = TestGraphRequest()
Profile.load(
token: SampleAccessTokens.validToken,
request: request
) { _, _ in
XCTFail("Should not invoke the completion")
}
XCTAssertEqual(
request.startCallCount,
1,
"Should fetch a profile if it is expired and there is an access token"
)
}
func testLoadingProfileWithCurrentlyLoadingProfile() {
let expected = SampleUserProfiles.createValid(isExpired: true)
Profile.current = expected
let request = TestGraphRequest()
let connection = TestGraphRequestConnection()
request.stubbedConnection = connection
Profile.load(
token: SampleAccessTokens.validToken,
request: request
) { _, _ in
XCTFail("Should not invoke the completion")
}
Profile.load(
token: SampleAccessTokens.validToken,
request: request
) { _, _ in
XCTFail("Should not invoke the completion")
}
XCTAssertEqual(
connection.cancelCallCount,
1,
"Should cancel an existing connection if a new connection is started before it completes"
)
}
func testProfileParseBlockInvokedOnSuccessfulGraphRequest() throws {
let result: [String: String] = [:]
var capturedProfileRef: AutoreleasingUnsafeMutablePointer<Profile>?
var capturedResult: Any?
Profile.load(
with: SampleAccessTokens.validToken,
graphRequest: testGraphRequest,
completion: { _, _ in },
parseBlock: {
capturedResult = $0
capturedProfileRef = $1
}
)
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNotNil(capturedProfileRef)
XCTAssertNotNil(capturedResult)
}
func testProfileParseBlockShouldHaveNonNullPointer() throws {
let result: [String: String] = [:]
var capturedProfileRef: AutoreleasingUnsafeMutablePointer<Profile>?
var capturedResult: Any?
Profile.load(
with: SampleAccessTokens.validToken,
graphRequest: testGraphRequest,
completion: { _, _ in },
parseBlock: {
capturedResult = $0
capturedProfileRef = $1
}
)
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNotNil(capturedProfileRef)
XCTAssertNotNil(capturedResult)
}
func testProfileParseBlockReturnsNilIfResultIsEmpty() throws {
let result: [String: String] = [:]
Profile.load(token: SampleAccessTokens.validToken, request: testGraphRequest, completion: nil)
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(Profile.current)
}
func testProfileParseBlockReturnsNilIfResultHasNoID() throws {
let result = [
"first_name": "firstname",
"middle_name": "middlename",
"last_name": "lastname",
"name": "name",
]
Profile.load(token: SampleAccessTokens.validToken, request: testGraphRequest, completion: nil)
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(Profile.current)
}
func testProfileParseBlockReturnsNilIfResultHasEmptyID() throws {
result["id"] = ""
Profile.load(token: SampleAccessTokens.validToken, request: testGraphRequest, completion: nil)
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, result, nil)
XCTAssertNil(Profile.current)
}
func testLoadProfileWithRandomData() throws {
for _ in 0 ..< 100 {
let randomizedResult = Fuzzer.randomize(json: sampleGraphResult())
var completed = false
Profile.load(
token: SampleAccessTokens.validToken,
request: testGraphRequest
) { _, _ in
completed = true
}
let completion = try XCTUnwrap(testGraphRequest.capturedCompletionHandler)
completion(nil, randomizedResult, nil)
XCTAssert(completed, "Completion handler should be invoked synchronously")
}
}
// MARK: Update Notifications
func testClearingMissingProfile() {
Profile.setCurrent(nil, shouldPostNotification: true)
XCTAssertTrue(
notificationCenter.capturedPostNames.isEmpty,
"Clearing an empty current profile should not post a notification"
)
}
func testClearingProfile() {
Profile.setCurrent(profile, shouldPostNotification: false)
notificationCenter.capturedPostNames = []
Profile.setCurrent(nil, shouldPostNotification: true)
XCTAssertFalse(
notificationCenter.capturedPostNames.isEmpty,
"Clearing the current profile should post a notification"
)
}
func testSettingProfile() {
Profile.setCurrent(profile, shouldPostNotification: true)
XCTAssertFalse(
notificationCenter.capturedPostNames.isEmpty,
"Setting the current profile to `nil` should post a notification"
)
}
func testSettingSameProfile() {
Profile.setCurrent(profile, shouldPostNotification: true)
notificationCenter.capturedPostNames = []
Profile.setCurrent(profile, shouldPostNotification: true)
XCTAssertTrue(
notificationCenter.capturedPostNames.isEmpty,
"Setting the current profile to the same profile should not post a notification"
)
}
func testUpdatingProfile() {
Profile.setCurrent(profile, shouldPostNotification: true)
notificationCenter.capturedPostNames = []
let newProfile = SampleUserProfiles.createValid(
userID: "different",
name: "different",
imageURL: nil,
isExpired: true,
isLimited: true
)
Profile.setCurrent(newProfile, shouldPostNotification: true)
XCTAssertFalse(
notificationCenter.capturedPostNames.isEmpty,
"Replacing the current profile should post a notification"
)
}
// MARK: Storage
func testEncoding() {
let coder = TestCoder()
let profile = SampleUserProfiles.validLimited
profile.encode(with: coder)
XCTAssertEqual(
coder.encodedObject["userID"] as? String,
profile.userID,
"Should encode the expected user identifier"
)
XCTAssertEqual(
coder.encodedObject["firstName"] as? String,
profile.firstName,
"Should encode the expected first name"
)
XCTAssertEqual(
coder.encodedObject["middleName"] as? String,
profile.middleName,
"Should encode the expected middle name"
)
XCTAssertEqual(
coder.encodedObject["lastName"] as? String,
profile.lastName,
"Should encode the expected last name"
)
XCTAssertEqual(
coder.encodedObject["name"] as? String,
profile.name,
"Should encode the expected name"
)
XCTAssertEqual(
coder.encodedObject["linkURL"] as? URL,
profile.linkURL,
"Should encode the expected link URL"
)
XCTAssertEqual(
coder.encodedObject["refreshDate"] as? Date,
profile.refreshDate,
"Should encode the expected refresh date"
)
XCTAssertEqual(
coder.encodedObject["imageURL"] as? URL,
profile.imageURL,
"Should encode the expected image URL"
)
XCTAssertEqual(
coder.encodedObject["email"] as? String,
profile.email,
"Should encode the expected email address"
)
XCTAssertEqual(
coder.encodedObject["friendIDs"] as? [String],
profile.friendIDs,
"Should encode the expected list of friend identifiers"
)
XCTAssertEqual(
coder.encodedObject["birthday"] as? Date,
profile.birthday,
"Should encode the expected user birthday"
)
XCTAssertEqual(
coder.encodedObject["ageRange"] as? UserAgeRange,
profile.ageRange,
"Should encode the expected user age range"
)
XCTAssertEqual(
coder.encodedObject["hometown"] as? Location,
profile.hometown,
"Should encode the expected user hometown"
)
XCTAssertEqual(
coder.encodedObject["location"] as? Location,
profile.location,
"Should encode the expected user location"
)
XCTAssertEqual(
coder.encodedObject["gender"] as? String,
profile.gender,
"Should encode the expected user gender"
)
XCTAssertEqual(
coder.encodedObject["isLimited"] as? Bool,
true,
"isLimited should be true"
)
}
func testDecodingEntryWithMethodName() {
let coder = TestCoder()
_ = Profile(coder: coder)
decodeObjectCheck(
decodedObject: "userID",
objectType: NSString.self,
failureMessage: "Should decode a string for the userID key"
)
decodeObjectCheck(
decodedObject: "firstName",
objectType: NSString.self,
failureMessage: "Should decode a string for the firstName key"
)
decodeObjectCheck(
decodedObject: "middleName",
objectType: NSString.self,
failureMessage: "Should decode a string for the middleName key"
)
decodeObjectCheck(
decodedObject: "lastName",
objectType: NSString.self,
failureMessage: "Should decode a string for the lastName key"
)
decodeObjectCheck(
decodedObject: "name",
objectType: NSString.self,
failureMessage:
"Should decode a string for the name key"
)
decodeObjectCheck(
decodedObject: "linkURL",
objectType: NSURL.self,
failureMessage: "Should decode a url for the linkURL key"
)
decodeObjectCheck(
decodedObject: "refreshDate",
objectType: NSDate.self,
failureMessage: "Should decode a date for the refreshDate key"
)
decodeObjectCheck(
decodedObject: "imageURL",
objectType: NSURL.self,
failureMessage: "Should decode a url for the imageURL key"
)
decodeObjectCheck(
decodedObject: "email",
objectType: NSString.self,
failureMessage: "Should decode a string for the email key"
)
decodeObjectCheck(
decodedObject: "friendIDs",
objectType: NSArray.self,
failureMessage: "Should decode an array for the friendIDs key"
)
decodeObjectCheck(
decodedObject: "birthday",
objectType: NSDate.self,
failureMessage: "Should decode a date for the birthday key"
)
decodeObjectCheck(
decodedObject: "ageRange",
objectType: UserAgeRange.self,
failureMessage:
"Should decode a UserAgeRange object for the ageRange key"
)
decodeObjectCheck(
decodedObject: "hometown",
objectType: Location.self,
failureMessage: "Should decode a Location object for the hometown key"
)
decodeObjectCheck(
decodedObject: "location",
objectType: Location.self,
failureMessage: "Should decode a Location object for the location key"
)
decodeObjectCheck(
decodedObject: "gender",
objectType: NSString.self,
failureMessage:
"Should decode a string for the gender key"
)
XCTAssertEqual(
coder.decodedObject["isLimited"] as? String,
"decodeBoolForKey",
"Should decode a boolean for the isLimited key"
)
}
func testDefaultDataStore() {
Profile.reset()
XCTAssertNil(
Profile.dataStore,
"Should not have a default data store"
)
}
func testConfiguringWithDataStore() {
XCTAssertTrue(
Profile.dataStore === dataStore,
"Should be able to set a persistent data store"
)
}
func testConfiguringWithNotificationCenter() {
XCTAssertTrue(
Profile.notificationCenter === notificationCenter,
"Should be able to set a Notification Posting"
)
}
func testDefaultAccessTokenProvider() {
Profile.reset()
XCTAssertNil(
Profile.accessTokenProvider,
"Should not have a default access token provider"
)
}
func testConfiguringWithTokenProvider() {
XCTAssertTrue(
Profile.accessTokenProvider is TestAccessTokenWallet.Type,
"Should be able to set a token wallet"
)
}
func testDefaultSettings() {
Profile.reset()
XCTAssertNil(
Profile.settings,
"Should not have default settings"
)
}
func testConfiguringWithSettings() {
XCTAssertTrue(
Profile.settings === settings,
"Should be able to set settings"
)
}
func testHashability() {
let profile = SampleUserProfiles.createValid()
let profile2 = SampleUserProfiles.createValid(userID: name)
XCTAssertEqual(
profile.hash,
profile.hash,
"Hashed profiles should be consistent"
)
XCTAssertNotEqual(
profile.hash,
profile2.hash,
"Hashed profiles should be unique"
)
}
func testFetchingCachedProfile() {
_ = Profile.fetchCachedProfile()
XCTAssertEqual(
dataStore.capturedObjectRetrievalKey,
"com.facebook.sdk.FBSDKProfile.currentProfile",
"Fetching a cached profile should query the data store with the expected retrieval key"
)
}
func sampleGraphResult() -> [String: Any] {
[
"id": profile.userID,
"first_name": profile.firstName as Any,
"middle_name": profile.middleName as Any,
"last_name": profile.lastName as Any,
"name": profile.name as Any,
"link": profile.linkURL as Any,
"email": profile.email as Any,
"friends": [
"data": [
[
"name": "user1",
"id": profile.friendIDs?[0],
],
[
"name": "user2",
"id": profile.friendIDs?[1],
],
],
],
"birthday": "01/01/1990",
"age_range": [
"min": profile.ageRange?.min,
],
"hometown": [
"id": profile.hometown?.id,
"name": profile.hometown?.name,
],
"location": [
"id": profile.location?.id,
"name": profile.location?.name,
],
"gender": profile.gender as Any,
]
}
func verfiyGraphPath(
expectedPath: String,
permissions: [String],
file: StaticString = #file,
line: UInt = #line
) {
let token = SampleAccessTokens.create(withPermissions: permissions)
let graphPath = Profile.graphPath(for: token)
XCTAssertEqual(graphPath, expectedPath, file: file, line: line)
}
func decodeObjectCheck(
decodedObject: String,
objectType: Any,
failureMessage: String,
file: StaticString = #file,
line: UInt = #line
) {
let coder = TestCoder()
_ = Profile(coder: coder)
XCTAssertTrue(
coder.decodedObject[decodedObject].self as? Any.Type == objectType as? Any.Type,
failureMessage,
file: file,
line: line
)
}
}