// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import Foundation import Combine import GameKit import XCTest import FirebaseAuth class GameCenterAuthProviderTests: XCTestCase { override class func setUp() { FirebaseApp.configureForTests() GKLocalPlayer.mock(with: MockLocalPlayer.self) } override class func tearDown() { FirebaseApp.app()?.delete { success in if success { print("Shut down app successfully.") } else { print("💥 There was a problem when shutting down the app..") } } } fileprivate static let playerID = "PLAYERID" fileprivate static let displayName = "DISPLAYNAME" fileprivate static let publicKeyURL = "PUBLICKEYURL" fileprivate static let signature = "AAAABBBBCCCC" fileprivate static let salt = "AAAA" fileprivate static let timestamp: UInt64 = 12_345_678 class MockLocalPlayer: GKLocalPlayer { static var _local: MockLocalPlayer! override class var local: GKLocalPlayer { _local } override var playerID: String { GameCenterAuthProviderTests.playerID } override var alias: String { GameCenterAuthProviderTests.displayName } override var displayName: String { GameCenterAuthProviderTests.displayName } var _isAuthenticated: Bool = true override var isAuthenticated: Bool { _isAuthenticated } var _errorIdentityVerificationSignature: NSError? override func generateIdentityVerificationSignature(completionHandler: ((URL?, Data?, Data?, UInt64, Error?) -> Void)? = nil) { let url = URL(string: GameCenterAuthProviderTests.publicKeyURL) let signature = Data(base64Encoded: GameCenterAuthProviderTests.signature) let salt = Data(base64Encoded: GameCenterAuthProviderTests.salt) let timestamp = GameCenterAuthProviderTests.timestamp if _errorIdentityVerificationSignature != nil { completionHandler?(nil, nil, nil, 0, _errorIdentityVerificationSignature) } else { completionHandler?(url, signature, salt, timestamp, nil) } } } class MockAuthBackend: AuthBackendImplementationMock {} func testGetCredentialWithLocalPlayer() { // given FIRAuthBackend.setBackendImplementation(MockAuthBackend()) MockLocalPlayer._local = MockLocalPlayer() var cancellables = Set() let getCredentialExpectation = expectation(description: "Get credential") // when GameCenterAuthProvider.getCredential() .sink { completion in switch completion { case .finished: print("Finished") case let .failure(error): XCTFail("💥 Something went wrong: \(error)") } } receiveValue: { credential in do { XCTAssertTrue(Thread.isMainThread) let gameCenterCredential = try XCTUnwrap(credential as? FIRGameCenterAuthCredential) XCTAssertEqual(gameCenterCredential.displayName, Self.displayName) XCTAssertEqual(gameCenterCredential.playerID, Self.playerID) XCTAssertEqual( gameCenterCredential.publicKeyURL.absoluteString, Self.publicKeyURL ) XCTAssertEqual(gameCenterCredential.timestamp, Self.timestamp) XCTAssertEqual(gameCenterCredential.salt.base64EncodedString(), Self.salt) XCTAssertEqual( gameCenterCredential.signature.base64EncodedString(), Self.signature ) } catch { XCTFail("💥 Expect non-nil OAuth credential: \(error)") } getCredentialExpectation.fulfill() } .store(in: &cancellables) // then wait(for: [getCredentialExpectation], timeout: expectationTimeout) } func testGetCredentialPlayerNotAuthenticatedWithLocalPlayer() { // given FIRAuthBackend.setBackendImplementation(MockAuthBackend()) MockLocalPlayer._local._isAuthenticated = false var cancellables = Set() let getCredentialExpectation = expectation(description: "Get credential") // when GameCenterAuthProvider.getCredential() .sink { completion in if case let .failure(error as NSError) = completion { XCTAssertEqual(error.code, AuthErrorCode.localPlayerNotAuthenticated.rawValue) getCredentialExpectation.fulfill() } } receiveValue: { authDataResult in XCTFail("💥 result unexpected") } .store(in: &cancellables) // then wait(for: [getCredentialExpectation], timeout: expectationTimeout) } func testGetCredentialInvalidPlayerWithLocalPlayer() { // given FIRAuthBackend.setBackendImplementation(MockAuthBackend()) MockLocalPlayer._local = MockLocalPlayer() MockLocalPlayer._local ._errorIdentityVerificationSignature = GKError(.invalidPlayer) as NSError var cancellables = Set() let getCredentialExpectation = expectation(description: "Get credential") // when GameCenterAuthProvider.getCredential() .sink { completion in if case let .failure(error as NSError) = completion { XCTAssertEqual(error.code, GKError.invalidPlayer.rawValue) getCredentialExpectation.fulfill() } } receiveValue: { authDataResult in XCTFail("💥 result unexpected") } .store(in: &cancellables) // then wait(for: [getCredentialExpectation], timeout: expectationTimeout) } }