2026-09-23 15:22:08 -07:00
|
|
|
// Copyright 2026 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 XCTest
|
|
|
|
|
#if canImport(GoogleUtilities)
|
|
|
|
|
import GoogleUtilities
|
|
|
|
|
#endif
|
|
|
|
|
@testable import AppCheckCore
|
|
|
|
|
|
|
|
|
|
class MockAppCheckDebugProviderAPIService: NSObject, AppCheckCoreDebugProviderAPIServiceProtocol {
|
|
|
|
|
var passedDebugToken: String?
|
|
|
|
|
var passedLimitedUse: Bool?
|
|
|
|
|
|
|
|
|
|
var tokenResult: Result<AppCheckCoreToken, Error>?
|
|
|
|
|
var limitedUseTokenResult: Result<AppCheckCoreToken, Error>?
|
|
|
|
|
|
|
|
|
|
func appCheckToken(debugToken: String, limitedUse: Bool) async throws -> AppCheckCoreToken {
|
|
|
|
|
passedDebugToken = debugToken
|
|
|
|
|
passedLimitedUse = limitedUse
|
|
|
|
|
|
|
|
|
|
let result = limitedUse ? limitedUseTokenResult : tokenResult
|
|
|
|
|
|
|
|
|
|
guard let result = result else {
|
|
|
|
|
throw NSError(domain: "MockError", code: -1, userInfo: nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch result {
|
|
|
|
|
case let .success(token):
|
|
|
|
|
return token
|
|
|
|
|
case let .failure(error):
|
|
|
|
|
throw error
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class AppCheckCoreDebugProviderTests: XCTestCase {
|
|
|
|
|
let kDebugTokenEnvKey = "AppCheckDebugToken"
|
|
|
|
|
let kFirebaseDebugTokenEnvKey = "FIRAAppCheckDebugToken"
|
|
|
|
|
let kDebugTokenUserDefaultsKey = AppCheckCoreDebugProvider.debugTokenUserDefaultsKey
|
|
|
|
|
let kDebugTokenRegisteredUserDefaultsKey = AppCheckCoreDebugProvider
|
|
|
|
|
.debugTokenRegisteredUserDefaultsKeyPrefix
|
|
|
|
|
|
|
|
|
|
var provider: AppCheckCoreDebugProvider!
|
|
|
|
|
var fakeAPIService: MockAppCheckDebugProviderAPIService!
|
|
|
|
|
|
|
|
|
|
override func setUp() {
|
|
|
|
|
super.setUp()
|
|
|
|
|
fakeAPIService = MockAppCheckDebugProviderAPIService()
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func tearDown() {
|
|
|
|
|
provider = nil
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenRegisteredUserDefaultsKey)
|
2026-11-05 16:47:46 -05:00
|
|
|
// The prefix alone is not a real key; also clear the flag for the suite's default provider.
|
|
|
|
|
UserDefaults.standard.removeObject(
|
|
|
|
|
forKey: AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app"
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-09-23 15:22:08 -07:00
|
|
|
super.tearDown()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Debug token generating/storing
|
|
|
|
|
|
|
|
|
|
// IMMUTABLE TEST: Do not edit, update, or remove under any circumstance.
|
|
|
|
|
// This test locks in an immutable backwards compatibility contract with App Check 11.
|
|
|
|
|
// If this test fails, revert changes to the implementation instead of modifying this test.
|
|
|
|
|
func testDebugTokenUserDefaultsKeys_MatchContract() {
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
AppCheckCoreDebugProvider.debugTokenUserDefaultsKey,
|
|
|
|
|
"GACAppCheckDebugToken",
|
|
|
|
|
"Debug token key mismatch breaks App Check 11 migration compatibility."
|
|
|
|
|
)
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
AppCheckCoreDebugProvider.debugTokenRegisteredUserDefaultsKeyPrefix,
|
|
|
|
|
"GACAppCheckDebugTokenRegistered",
|
|
|
|
|
"Registered token key prefix mismatch breaks App Check 11 migration compatibility."
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLegacyDebugTokenCompatibility() {
|
|
|
|
|
let legacyKey = kDebugTokenUserDefaultsKey
|
|
|
|
|
let legacyToken = "legacy_stored_debug_token"
|
|
|
|
|
UserDefaults.standard.set(legacyToken, forKey: legacyKey)
|
|
|
|
|
defer {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: legacyKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let debugProvider = AppCheckCoreDebugProvider(
|
|
|
|
|
apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
debugProvider.currentDebugToken(),
|
|
|
|
|
legacyToken,
|
|
|
|
|
"Debug token mismatch breaks App Check 11 migration compatibility."
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testSetDebugToken_CompatibilityWithLegacyGACStorage() {
|
|
|
|
|
let legacyKey = kDebugTokenUserDefaultsKey
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: legacyKey)
|
|
|
|
|
defer {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: legacyKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let debugProvider = AppCheckCoreDebugProvider(
|
|
|
|
|
apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
let generatedToken = debugProvider.currentDebugToken()
|
|
|
|
|
let storedLegacyValue = UserDefaults.standard.string(forKey: legacyKey)
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
storedLegacyValue,
|
|
|
|
|
generatedToken,
|
|
|
|
|
"Generated debug token was not stored under legacy key."
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLegacyRegisteredDebugTokenCompatibility() async throws {
|
|
|
|
|
let serviceName = "test-service"
|
|
|
|
|
let resourceName = "projects/test-project/apps/test-app"
|
|
|
|
|
let expectedLegacyRegisteredKey =
|
|
|
|
|
"GACAppCheckDebugTokenRegistered_test-service_projects_test-project_apps_test-app"
|
|
|
|
|
|
|
|
|
|
// 1. Verify key resolution matches legacy v11 format.
|
|
|
|
|
let resolvedKey = AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: serviceName,
|
|
|
|
|
resourceName: resourceName
|
|
|
|
|
)
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
resolvedKey,
|
|
|
|
|
expectedLegacyRegisteredKey,
|
|
|
|
|
"Resolved registered token key does not match legacy v11 format."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 2. Verify write-back persists to UserDefaults using legacy key format.
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: expectedLegacyRegisteredKey)
|
|
|
|
|
defer {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: expectedLegacyRegisteredKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let validToken = AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date().addingTimeInterval(3600)
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .success(validToken)
|
|
|
|
|
|
|
|
|
|
let debugProvider = AppCheckCoreDebugProvider(
|
|
|
|
|
apiService: fakeAPIService,
|
|
|
|
|
serviceName: serviceName,
|
|
|
|
|
resourceName: resourceName,
|
|
|
|
|
environment: [:]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
XCTAssertFalse(
|
|
|
|
|
UserDefaults.standard.bool(forKey: expectedLegacyRegisteredKey),
|
|
|
|
|
"Precondition: Key should not be registered before getToken()."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
_ = try await debugProvider.getToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertTrue(
|
|
|
|
|
UserDefaults.standard.bool(forKey: expectedLegacyRegisteredKey),
|
|
|
|
|
"Registered flag was not stored under legacy key format GACAppCheckDebugTokenRegistered_<service>_<resource>."
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenEnvironmentVariableSetAndTokenStored() {
|
|
|
|
|
UserDefaults.standard.set("stored token", forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let envToken = "env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: envToken])
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenFirebaseAndCoreEnvironmentVariablesSetAndTokenStored() {
|
|
|
|
|
UserDefaults.standard.set("stored token", forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let envToken = "env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [
|
|
|
|
|
kDebugTokenEnvKey: envToken,
|
|
|
|
|
kFirebaseDebugTokenEnvKey: "firebase env token",
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenFirebaseEnvironmentVariableSetAndTokenStored() {
|
|
|
|
|
UserDefaults.standard.set("stored token", forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let envToken = "env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kFirebaseDebugTokenEnvKey: envToken])
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenFirebaseAndCoreEnvironmentVariablesSet() {
|
|
|
|
|
let envToken = "env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [
|
|
|
|
|
kDebugTokenEnvKey: envToken,
|
|
|
|
|
kFirebaseDebugTokenEnvKey: "firebase env token",
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenNoEnvironmentVariableAndTokenStored() {
|
|
|
|
|
let storedToken = "stored token"
|
|
|
|
|
UserDefaults.standard.set(storedToken, forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), storedToken)
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), storedToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenNoEnvironmentVariableAndNoTokenStored() {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey))
|
|
|
|
|
|
|
|
|
|
let generatedToken = provider.currentDebugToken()
|
|
|
|
|
XCTAssertNotNil(generatedToken)
|
|
|
|
|
|
|
|
|
|
// Check if the generated token is stored to the user defaults.
|
|
|
|
|
XCTAssertEqual(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey), generatedToken)
|
|
|
|
|
|
|
|
|
|
// Check if the same token is used once generated.
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), generatedToken)
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-06 12:23:01 -05:00
|
|
|
// MARK: - Debug token source
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsLocalWhenNoEnvironmentVariableOrProvidedToken() {
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .local)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsLocalWhenEmptyDebugTokenProvided() {
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .local)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsLocalWhenWhitespaceOnlyDebugTokenProvided() {
|
|
|
|
|
let storedToken = "stored token"
|
|
|
|
|
UserDefaults.standard.set(storedToken, forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: " \n")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .local)
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), storedToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenTrimsWhitespaceFromProvidedDebugToken() {
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: " provided token\n")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .provided)
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), "provided token")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsProvidedWhenDebugTokenProvided() {
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "provided token")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .provided)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsEnvironmentVariableWhenEnvironmentVariableSet() {
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kFirebaseDebugTokenEnvKey: "env token"])
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .environmentVariable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testDebugTokenSourceIsEnvironmentVariableWhenTokenAlsoProvided() {
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: "same token"],
|
|
|
|
|
debugToken: "same token")
|
|
|
|
|
|
|
|
|
|
// An environment variable wins even when it matches the provided token.
|
|
|
|
|
XCTAssertEqual(provider.debugTokenSource, .environmentVariable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Provided debug token
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenDebugTokenProvidedAndNoEnvironmentVariable() {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let providedToken = "provided token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: providedToken)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), providedToken)
|
|
|
|
|
// The provided token must not be persisted, nor a local token generated in its place.
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenDebugTokenProvidedAndTokenStored() {
|
|
|
|
|
UserDefaults.standard.set("stored token", forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let providedToken = "provided token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: providedToken)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), providedToken)
|
|
|
|
|
XCTAssertEqual(provider.localDebugToken(), "stored token")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenEnvironmentVariableSetAndDebugTokenProvided() {
|
|
|
|
|
let envToken = "env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: envToken],
|
|
|
|
|
debugToken: "provided token")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenFirebaseEnvironmentVariableSetAndDebugTokenProvided() {
|
|
|
|
|
let envToken = "firebase env token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kFirebaseDebugTokenEnvKey: envToken],
|
|
|
|
|
debugToken: "provided token")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), envToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenEmptyDebugTokenProvidedAndTokenStored() {
|
|
|
|
|
let storedToken = "stored token"
|
|
|
|
|
UserDefaults.standard.set(storedToken, forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "")
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), storedToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCurrentTokenWhenEmptyDebugTokenProvidedAndNoTokenStored() {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "")
|
|
|
|
|
|
|
|
|
|
let generatedToken = provider.currentDebugToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertFalse(generatedToken.isEmpty)
|
|
|
|
|
XCTAssertEqual(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey), generatedToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLocalDebugTokenIsIndependentOfProvidedDebugToken() {
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
let providedToken = "provided token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: providedToken)
|
|
|
|
|
|
|
|
|
|
let localToken = provider.localDebugToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertNotEqual(localToken, providedToken)
|
|
|
|
|
XCTAssertEqual(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey), localToken)
|
|
|
|
|
XCTAssertEqual(provider.currentDebugToken(), providedToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetTokenSuccessWithProvidedDebugTokenDoesNotSetRegisteredFlag() async throws {
|
|
|
|
|
let registeredKey = AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app"
|
|
|
|
|
)
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: registeredKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "provided token")
|
|
|
|
|
fakeAPIService.tokenResult = .success(AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date().addingTimeInterval(3600)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
_ = try await provider.getToken()
|
|
|
|
|
|
|
|
|
|
// Only the locally generated token is tracked as registered; a provided token that later
|
|
|
|
|
// disappears must not suppress the registration instructions for the local token.
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.object(forKey: registeredKey))
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-05 16:47:46 -05:00
|
|
|
func testGetTokenSuccessWithEnvironmentDebugTokenDoesNotSetRegisteredFlag() async throws {
|
|
|
|
|
let registeredKey = AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app"
|
|
|
|
|
)
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: registeredKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: "env token"])
|
|
|
|
|
fakeAPIService.tokenResult = .success(AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date().addingTimeInterval(3600)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
_ = try await provider.getToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.object(forKey: registeredKey))
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-06 12:23:01 -05:00
|
|
|
func testGetTokenFailureWithProvidedDebugTokenPreservesRegisteredFlag() async throws {
|
|
|
|
|
let registeredKey = AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app"
|
|
|
|
|
)
|
|
|
|
|
UserDefaults.standard.set(true, forKey: registeredKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: "provided token")
|
|
|
|
|
fakeAPIService.tokenResult = .failure(NSError(domain: "testDomain", code: -1, userInfo: nil))
|
|
|
|
|
|
|
|
|
|
await XCTAssertThrowsErrorAsync(try await provider.getToken())
|
|
|
|
|
|
|
|
|
|
// The local token was not exchanged, so its registration state must be left untouched.
|
|
|
|
|
XCTAssertTrue(UserDefaults.standard.bool(forKey: registeredKey))
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-05 16:47:46 -05:00
|
|
|
func testGetTokenFailureWithEnvironmentDebugTokenPreservesRegisteredFlag() async throws {
|
|
|
|
|
let registeredKey = AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app"
|
|
|
|
|
)
|
|
|
|
|
UserDefaults.standard.set(true, forKey: registeredKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: "env token"])
|
|
|
|
|
fakeAPIService.tokenResult = .failure(NSError(domain: "testDomain", code: -1, userInfo: nil))
|
|
|
|
|
|
|
|
|
|
await XCTAssertThrowsErrorAsync(try await provider.getToken())
|
|
|
|
|
|
|
|
|
|
XCTAssertTrue(UserDefaults.standard.bool(forKey: registeredKey))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Asserts that `expression` throws, since `XCTAssertThrowsError` does not accept async
|
|
|
|
|
/// expressions.
|
|
|
|
|
private func XCTAssertThrowsErrorAsync<T>(_ expression: @autoclosure () async throws -> T,
|
|
|
|
|
file: StaticString = #filePath,
|
|
|
|
|
line: UInt = #line) async {
|
|
|
|
|
do {
|
|
|
|
|
_ = try await expression()
|
|
|
|
|
XCTFail("Expected error to be thrown", file: file, line: line)
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-06 12:23:01 -05:00
|
|
|
func testGetTokenUsesProvidedDebugToken() async throws {
|
|
|
|
|
let providedToken = "provided token"
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [:],
|
|
|
|
|
debugToken: providedToken)
|
|
|
|
|
let validToken = AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date(),
|
|
|
|
|
receivedAt: Date()
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .success(validToken)
|
|
|
|
|
|
|
|
|
|
let token = try await provider.getToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(token.token, validToken.token)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, providedToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-23 15:22:08 -07:00
|
|
|
// MARK: - Debug token to FAC token exchange
|
|
|
|
|
|
|
|
|
|
func testGetTokenSuccess() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let validToken = AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date(),
|
|
|
|
|
receivedAt: Date()
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .success(validToken)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get token.
|
|
|
|
|
let token = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(token.token, validToken.token)
|
|
|
|
|
XCTAssertEqual(token.expirationDate, validToken.expirationDate)
|
|
|
|
|
XCTAssertEqual(token.receivedAtDate, validToken.receivedAtDate)
|
|
|
|
|
|
|
|
|
|
// 3. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetTokenAPIError() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let apiError = NSError(domain: "testGetTokenAPIError", code: -1, userInfo: nil)
|
|
|
|
|
fakeAPIService.tokenResult = .failure(apiError)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get token.
|
|
|
|
|
do {
|
|
|
|
|
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XCTFail("Expected error to be thrown")
|
|
|
|
|
} catch let error as NSError {
|
|
|
|
|
XCTAssertEqual(error, apiError)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetLimitedUseTokenSuccess() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let validToken = AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date(),
|
|
|
|
|
receivedAt: Date()
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.limitedUseTokenResult = .success(validToken)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get limited-use token.
|
|
|
|
|
let token = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getLimitedUseToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(token.token, validToken.token)
|
|
|
|
|
XCTAssertEqual(token.expirationDate, validToken.expirationDate)
|
|
|
|
|
XCTAssertEqual(token.receivedAtDate, validToken.receivedAtDate)
|
|
|
|
|
|
|
|
|
|
// 3. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetLimitedUseTokenAPIError() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let apiError = NSError(domain: "testGetLimitedUseTokenAPIError", code: -1, userInfo: nil)
|
|
|
|
|
fakeAPIService.limitedUseTokenResult = .failure(apiError)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get limited-use token.
|
|
|
|
|
do {
|
|
|
|
|
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getLimitedUseToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XCTFail("Expected error to be thrown")
|
|
|
|
|
} catch let error as NSError {
|
|
|
|
|
XCTAssertEqual(error, apiError)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetTokenSuccessSetsRegisteredFlag() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let validToken = AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date(),
|
|
|
|
|
receivedAt: Date()
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .success(validToken)
|
|
|
|
|
|
|
|
|
|
// The mirror way to get registeredUserDefaultsKey, or since we know it, we can just hardcode or
|
|
|
|
|
// access it
|
|
|
|
|
let registeredKey =
|
|
|
|
|
"\(kDebugTokenRegisteredUserDefaultsKey)_test-service_projects_test-project_apps_test-app"
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: registeredKey)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get token.
|
|
|
|
|
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Verify flag is now YES.
|
|
|
|
|
XCTAssertTrue(UserDefaults.standard.bool(forKey: registeredKey))
|
|
|
|
|
|
|
|
|
|
// 4. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetTokenPermanentFailureClearsRegisteredFlag() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let apiError = NSError(
|
|
|
|
|
domain: "testGetTokenPermanentFailureClearsRegisteredFlag",
|
|
|
|
|
code: -1,
|
|
|
|
|
userInfo: nil
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .failure(apiError)
|
|
|
|
|
|
|
|
|
|
let registeredKey =
|
|
|
|
|
"\(kDebugTokenRegisteredUserDefaultsKey)_test-service_projects_test-project_apps_test-app"
|
|
|
|
|
UserDefaults.standard.set(true, forKey: registeredKey)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get token.
|
|
|
|
|
do {
|
|
|
|
|
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XCTFail("Expected error to be thrown")
|
|
|
|
|
} catch _ {
|
|
|
|
|
// expected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Verify flag is cleared.
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.object(forKey: registeredKey))
|
|
|
|
|
|
|
|
|
|
// 4. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetTokenNetworkFailureDoesNotClearRegisteredFlag() async throws {
|
|
|
|
|
// 1. Stub API service.
|
|
|
|
|
let expectedDebugToken = provider.currentDebugToken()
|
|
|
|
|
let networkError = NSError(
|
|
|
|
|
domain: AppCheckCoreErrorDomain,
|
|
|
|
|
code: AppCheckCoreErrorCode.serverUnreachable.rawValue,
|
|
|
|
|
userInfo: nil
|
|
|
|
|
)
|
|
|
|
|
fakeAPIService.tokenResult = .failure(networkError)
|
|
|
|
|
|
|
|
|
|
let registeredKey =
|
|
|
|
|
"\(kDebugTokenRegisteredUserDefaultsKey)_test-service_projects_test-project_apps_test-app"
|
|
|
|
|
UserDefaults.standard.set(true, forKey: registeredKey)
|
|
|
|
|
|
|
|
|
|
// 2. Validate get token.
|
|
|
|
|
do {
|
|
|
|
|
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<
|
|
|
|
|
AppCheckCoreToken,
|
|
|
|
|
Error
|
|
|
|
|
>) in
|
|
|
|
|
provider.getToken { token, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
} else if let token = token {
|
|
|
|
|
continuation.resume(returning: token)
|
|
|
|
|
} else {
|
|
|
|
|
continuation.resume(throwing: NSError(domain: "TestError", code: -1, userInfo: nil))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XCTFail("Expected error to be thrown")
|
|
|
|
|
} catch let error as NSError {
|
|
|
|
|
XCTAssertEqual(error, networkError)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Verify flag is still YES.
|
|
|
|
|
XCTAssertTrue(UserDefaults.standard.bool(forKey: registeredKey))
|
|
|
|
|
|
|
|
|
|
// 4. Verify fakes.
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, expectedDebugToken)
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedLimitedUse, false)
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-05 16:47:46 -05:00
|
|
|
func testGetTokenWithEnvironmentDebugTokenDoesNotCreateLocalToken() async throws {
|
|
|
|
|
// `setUp` creates a provider without an environment variable, which stores a local token.
|
|
|
|
|
UserDefaults.standard.removeObject(forKey: kDebugTokenUserDefaultsKey)
|
|
|
|
|
provider = AppCheckCoreDebugProvider(apiService: fakeAPIService,
|
|
|
|
|
serviceName: "test-service",
|
|
|
|
|
resourceName: "projects/test-project/apps/test-app",
|
|
|
|
|
environment: [kDebugTokenEnvKey: "env token"])
|
|
|
|
|
fakeAPIService.tokenResult = .success(AppCheckCoreToken(
|
|
|
|
|
token: "valid_token",
|
|
|
|
|
expirationDate: Date().addingTimeInterval(3600)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
_ = try await provider.getToken()
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(fakeAPIService.passedDebugToken, "env token")
|
|
|
|
|
// The local token is only generated, stored, and logged when it is the token in use.
|
|
|
|
|
XCTAssertNil(UserDefaults.standard.string(forKey: kDebugTokenUserDefaultsKey))
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-23 15:22:08 -07:00
|
|
|
// MARK: - Registered UserDefaults key construction
|
|
|
|
|
|
|
|
|
|
/// Backfilled from the v11 suite
|
|
|
|
|
/// (`testRegisteredUserDefaultsKeyForServiceName_resourceName`). This key
|
|
|
|
|
/// namespaces the "debug token already registered" flag, so a change to its
|
|
|
|
|
/// construction would silently orphan the flag for every existing install and
|
|
|
|
|
/// cause a spurious re-registration.
|
|
|
|
|
func testRegisteredUserDefaultsKeyForServiceNameResourceName() {
|
|
|
|
|
// Slashes in the resource name must be sanitized to underscores.
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "app1", resourceName: "projects/p1/apps/a1"
|
|
|
|
|
),
|
|
|
|
|
"GACAppCheckDebugTokenRegistered_app1_projects_p1_apps_a1"
|
|
|
|
|
)
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "app2", resourceName: "projects/p2/apps/a2"
|
|
|
|
|
),
|
|
|
|
|
"GACAppCheckDebugTokenRegistered_app2_projects_p2_apps_a2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Empty components fall back to "default".
|
|
|
|
|
//
|
|
|
|
|
// Note: v11 also covered `nil` for both parameters. Swift's type system
|
|
|
|
|
// makes that unrepresentable here (both are non-optional `String`), so the
|
|
|
|
|
// empty-string case is the only reachable fallback.
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
AppCheckCoreDebugProvider.registeredUserDefaultsKey(
|
|
|
|
|
forServiceName: "", resourceName: ""
|
|
|
|
|
),
|
|
|
|
|
"GACAppCheckDebugTokenRegistered_default_default"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|