Repository: firebase/firebase-ios-sdk (language: cpp)

Issue:
Expand test coverage of App Check
### Description

Some methods (e.g. `getLimitedUseTokenWithCompletion:` are untested.

### API Proposal

_No response_

### Firebase Product(s)

App Check


Discussion / hints:
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

Fix patch:
diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h b/FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h
index 2fcd08456a2..c699d957c11 100644
--- a/FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h
+++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h
@@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface FIRAppCheckValidator : NSObject
 
+// `FIRAppCheckValidator` doesn’t provide any instance methods.
+- (instancetype)init NS_UNAVAILABLE;
+
 /** The method validates if all parameters required to send App Check token exchange requests are
  * present in the `FIROptions` instance.
  *  @param options The `FIROptions` to validate.
diff --git a/FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckValidatorTests.m b/FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckValidatorTests.m
new file mode 100644
index 00000000000..da22c5c2747
--- /dev/null
+++ b/FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckValidatorTests.m
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 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/XCTest.h>
+
+#import "FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h"
+#import "FirebaseCore/Extension/FIROptionsInternal.h"
+
+@interface FIRAppCheckValidatorTests : XCTestCase
+@end
+
+@implementation FIRAppCheckValidatorTests
+
+- (void)test_tokenExchangeMissingFieldsInOptions_noMissingFields {
+  FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{
+    kFIRGoogleAppID : @"TEST_GoogleAppID",
+    kFIRAPIKey : @"TEST_APIKey",
+    kFIRProjectID : @"TEST_ProjectID"
+  }];
+
+  NSArray *missingFields = [FIRAppCheckValidator tokenExchangeMissingFieldsInOptions:options];
+
+  XCTAssertEqual(missingFields.count, 0);
+}
+
+- (void)test_tokenExchangeMissingFieldsInOptions_singleMissingField {
+  // Google App ID is empty:
+  FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{
+    kFIRGoogleAppID : @"",
+    kFIRAPIKey : @"TEST_APIKey",
+    kFIRProjectID : @"TEST_ProjectID"
+  }];
+
+  NSArray *missingFields = [FIRAppCheckValidator tokenExchangeMissingFieldsInOptions:options];
+
+  XCTAssertTrue([missingFields isEqualToArray:@[ @"googleAppID" ]]);
+}
+
+- (void)test_tokenExchangeMissingFieldsInOptions_multipleMissingFields {
+  // Google App ID is empty, and API Key and Project ID are not set:
+  FIROptions *options =
+      [[FIROptions alloc] initInternalWithOptionsDictionary:@{kFIRGoogleAppID : @""}];
+
+  NSArray *missingFields = [FIRAppCheckValidator tokenExchangeMissingFieldsInOptions:options];
+
+  NSArray *expectedMissingFields = @[ @"APIKey", @"projectID", @"googleAppID" ];
+  XCTAssertTrue([missingFields isEqualToArray:expectedMissingFields]);
+}
+
+@end
diff --git a/FirebaseAppCheck/Tests/Unit/DebugProvider/FIRAppCheckDebugProviderTests.m b/FirebaseAppCheck/Tests/Unit/DebugProvider/FIRAppCheckDebugProviderTests.m
index 09eef3edfe6..44f44c049a6 100644
--- a/FirebaseAppCheck/Tests/Unit/DebugProvider/FIRAppCheckDebugProviderTests.m
+++ b/FirebaseAppCheck/Tests/Unit/DebugProvider/FIRAppCheckDebugProviderTests.m
@@ -100,6 +100,17 @@ - (void)testCurrentTokenShim {
   OCMVerifyAll(self.debugProviderMock);
 }
 
+#pragma mark - Local Debug Token
+
+- (void)testLocalDebugToken {
+  NSString *localToken = @"TEST_LocalDebugToken";
+  OCMExpect([self.debugProviderMock localDebugToken]).andReturn(localToken);
+
+  XCTAssertEqualObjects([self.provider localDebugToken], localToken);
+
+  OCMVerifyAll(self.debugProviderMock);
+}
+
 #pragma mark - Debug token to FAC token exchange
 
 - (void)testGetTokenSuccess {
@@ -141,4 +152,42 @@ - (void)testGetTokenAPIError {
   OCMVerifyAll(self.debugProviderMock);
 }
 
+#pragma mark - Limited-Use Token
+
+- (void)testGetLimitedUseTokenSuccess {
+  GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken"
+                                                                  expirationDate:[NSDate date]
+                                                                  receivedAtDate:[NSDate date]];
+  OCMExpect([self.debugProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:validInternalToken,
+                                                                    [NSNull null], nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertEqualObjects(token.token, validInternalToken.token);
+    XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate);
+    XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate);
+    XCTAssertNil(error);
+  }];
+
+  OCMVerifyAll(self.debugProviderMock);
+}
+
+- (void)testGetLimitedUseTokenProviderError {
+  NSError *expectedError = [NSError errorWithDomain:@"TEST_LimitedUseToken_Error"
+                                               code:-1
+                                           userInfo:nil];
+  OCMExpect([self.debugProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError,
+                                                                    nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertNil(token);
+    XCTAssertIdentical(error, expectedError);
+  }];
+
+  OCMVerifyAll(self.debugProviderMock);
+}
+
 @end
diff --git a/FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m b/FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m
index add63a9961c..04b9290adc5 100644
--- a/FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m
+++ b/FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m
@@ -131,4 +131,42 @@ - (void)testGetTokenAPIError {
   OCMVerifyAll(self.deviceCheckProviderMock);
 }
 
+#pragma mark - Limited-Use Token
+
+- (void)testGetLimitedUseTokenSuccess {
+  GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken"
+                                                                  expirationDate:[NSDate date]
+                                                                  receivedAtDate:[NSDate date]];
+  OCMExpect([self.deviceCheckProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:validInternalToken,
+                                                                    [NSNull null], nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertEqualObjects(token.token, validInternalToken.token);
+    XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate);
+    XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate);
+    XCTAssertNil(error);
+  }];
+
+  OCMVerifyAll(self.deviceCheckProviderMock);
+}
+
+- (void)testGetLimitedUseTokenProviderError {
+  NSError *expectedError = [NSError errorWithDomain:@"TEST_LimitedUseToken_Error"
+                                               code:-1
+                                           userInfo:nil];
+  OCMExpect([self.deviceCheckProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError,
+                                                                    nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertNil(token);
+    XCTAssertIdentical(error, expectedError);
+  }];
+
+  OCMVerifyAll(self.deviceCheckProviderMock);
+}
+
 @end


Test patch:
diff --git a/FirebaseAppCheck/Tests/Unit/AppAttestProvider/FIRAppAttestProviderTests.m b/FirebaseAppCheck/Tests/Unit/AppAttestProvider/FIRAppAttestProviderTests.m
index 3d8ef2f3e6c..23636a0e46c 100644
--- a/FirebaseAppCheck/Tests/Unit/AppAttestProvider/FIRAppAttestProviderTests.m
+++ b/FirebaseAppCheck/Tests/Unit/AppAttestProvider/FIRAppAttestProviderTests.m
@@ -114,4 +114,42 @@ - (void)testGetTokenAPIError {
   OCMVerifyAll(self.appAttestProviderMock);
 }
 
+#pragma mark - Limited-Use Token
+
+- (void)testGetLimitedUseTokenSuccess {
+  GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken"
+                                                                  expirationDate:[NSDate date]
+                                                                  receivedAtDate:[NSDate date]];
+  OCMExpect([self.appAttestProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:validInternalToken,
+                                                                    [NSNull null], nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertEqualObjects(token.token, validInternalToken.token);
+    XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate);
+    XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate);
+    XCTAssertNil(error);
+  }];
+
+  OCMVerifyAll(self.appAttestProviderMock);
+}
+
+- (void)testGetLimitedUseTokenProviderError {
+  NSError *expectedError = [NSError errorWithDomain:@"TEST_LimitedUseToken_Error"
+                                               code:-1
+                                           userInfo:nil];
+  OCMExpect([self.appAttestProviderMock
+      getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError,
+                                                                    nil])]);
+
+  [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token,
+                                                    NSError *_Nullable error) {
+    XCTAssertNil(token);
+    XCTAssertIdentical(error, expectedError);
+  }];
+
+  OCMVerifyAll(self.appAttestProviderMock);
+}
+
 @end