Skip to content

Commit ecfd283

Browse files
committed
Migrate App Check not configured integration test for generateContent
1 parent 17ec791 commit ecfd283

File tree

6 files changed

+81
-26
lines changed

6 files changed

+81
-26
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import FirebaseCore
16+
17+
extension FirebaseApp {
18+
static func configure(appName: String, plistName: String) {
19+
guard let plistPath =
20+
Bundle.main.path(forResource: plistName, ofType: "plist") else {
21+
fatalError("The file '\(plistName).plist' was not found.")
22+
}
23+
guard let options = FirebaseOptions(contentsOfFile: plistPath) else {
24+
fatalError("Failed to parse options from '\(plistName).plist'.")
25+
}
26+
FirebaseApp.configure(name: appName, options: options)
27+
}
28+
}

FirebaseVertexAI/Tests/TestApp/Sources/TestApp.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@ struct TestApp: App {
2424
// Configure default Firebase App
2525
FirebaseApp.configure()
2626

27+
// Configure a Firebase App that is the same as the default app but without App Check.
28+
FirebaseApp.configure(
29+
appName: FirebaseAppNames.appCheckNotConfigured,
30+
plistName: "GoogleService-Info"
31+
)
32+
2733
// Configure a Firebase App without a billing account (i.e., the "Spark" plan).
28-
guard let plistPath =
29-
Bundle.main.path(forResource: "GoogleService-Info-Spark", ofType: "plist") else {
30-
fatalError("The file 'GoogleService-Info-Spark.plist' was not found.")
31-
}
32-
guard let options = FirebaseOptions(contentsOfFile: plistPath) else {
33-
fatalError("Failed to parse options from 'GoogleService-Info-Spark.plist'.")
34-
}
35-
FirebaseApp.configure(name: FirebaseAppNames.spark, options: options)
34+
FirebaseApp.configure(appName: FirebaseAppNames.spark, plistName: "GoogleService-Info-Spark")
3635
}
3736

3837
var body: some Scene {

FirebaseVertexAI/Tests/TestApp/Tests/Integration/GenerateContentIntegrationTests.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,10 @@ struct GenerateContentIntegrationTests {
116116
#expect(candidatesTokensDetails.tokenCount == usageMetadata.candidatesTokenCount)
117117
}
118118

119+
// MARK: Streaming Tests
120+
119121
@Test(arguments: InstanceConfig.allConfigs)
120-
func testGenerateContentStream(_ config: InstanceConfig) async throws {
122+
func generateContentStream(_ config: InstanceConfig) async throws {
121123
let expectedText = """
122124
1. Mercury
123125
2. Venus
@@ -160,4 +162,34 @@ struct GenerateContentIntegrationTests {
160162
let text = textValues.joined().trimmingCharacters(in: .whitespacesAndNewlines)
161163
#expect(text == expectedText)
162164
}
165+
166+
// MARK: - App Check Tests
167+
168+
@Test(arguments: [
169+
InstanceConfig.vertexV1AppCheckNotConfigured,
170+
InstanceConfig.vertexV1BetaAppCheckNotConfigured,
171+
// App Check is not supported on the Generative Language Developer API endpoint since it
172+
// bypasses the Vertex AI in Firebase proxy.
173+
])
174+
func generateContent_appCheckNotConfigured_shouldFail(_ config: InstanceConfig) async throws {
175+
let model = VertexAI.componentInstance(config).generativeModel(
176+
modelName: ModelNames.gemini2Flash
177+
)
178+
let prompt = "Where is Google headquarters located? Answer with the city name only."
179+
180+
try await #require {
181+
_ = try await model.generateContent(prompt)
182+
} throws: {
183+
guard let error = $0 as? GenerateContentError else {
184+
Issue.record("Expected a \(GenerateContentError.self); got \($0.self).")
185+
return false
186+
}
187+
guard case let .internalError(underlyingError) = error else {
188+
Issue.record("Expected a \(GenerateContentError.internalError.self); got \(error.self).")
189+
return false
190+
}
191+
192+
return String(describing: underlyingError).contains("Firebase App Check token is invalid")
193+
}
194+
}
163195
}

FirebaseVertexAI/Tests/TestApp/Tests/Integration/IntegrationTests.swift

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,6 @@ final class IntegrationTests: XCTestCase {
6767
storage = Storage.storage()
6868
}
6969

70-
// MARK: - Generate Content
71-
72-
func testGenerateContent_appCheckNotConfigured_shouldFail() async throws {
73-
let app = try FirebaseApp.defaultNamedCopy(name: FirebaseAppNames.appCheckNotConfigured)
74-
addTeardownBlock { await app.delete() }
75-
let vertex = VertexAI.vertexAI(app: app)
76-
let model = vertex.generativeModel(modelName: "gemini-2.0-flash")
77-
let prompt = "Where is Google headquarters located? Answer with the city name only."
78-
79-
do {
80-
_ = try await model.generateContent(prompt)
81-
XCTFail("Expected a Firebase App Check error; none thrown.")
82-
} catch let GenerateContentError.internalError(error) {
83-
XCTAssertTrue(String(describing: error).contains("Firebase App Check token is invalid"))
84-
}
85-
}
86-
8770
// MARK: - Count Tokens
8871

8972
func testCountTokens_text() async throws {

FirebaseVertexAI/Tests/TestApp/Tests/Utilities/InstanceConfig.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ struct InstanceConfig {
3434
)
3535
static let allConfigs = [vertexV1, vertexV1Beta, developerV1, developerV1Beta]
3636

37+
static let vertexV1AppCheckNotConfigured = InstanceConfig(
38+
appName: FirebaseAppNames.appCheckNotConfigured,
39+
apiConfig: APIConfig(service: .vertexAI, version: .v1)
40+
)
41+
static let vertexV1BetaAppCheckNotConfigured = InstanceConfig(
42+
appName: FirebaseAppNames.appCheckNotConfigured,
43+
apiConfig: APIConfig(service: .vertexAI, version: .v1beta)
44+
)
45+
3746
let appName: String?
3847
let location: String?
3948
let apiConfig: APIConfig

FirebaseVertexAI/Tests/TestApp/VertexAITestApp.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
8692F29E2CC9477800539E8F /* FirebaseVertexAI in Frameworks */ = {isa = PBXBuildFile; productRef = 8692F29D2CC9477800539E8F /* FirebaseVertexAI */; };
2424
8698D7462CD3CF3600ABA833 /* FirebaseAppTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8698D7452CD3CF2F00ABA833 /* FirebaseAppTestUtils.swift */; };
2525
8698D7482CD4332B00ABA833 /* TestAppCheckProviderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8698D7472CD4332B00ABA833 /* TestAppCheckProviderFactory.swift */; };
26+
86CC31352D91EE9E0087E964 /* FirebaseAppUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CC31342D91EE9E0087E964 /* FirebaseAppUtils.swift */; };
2627
86D77DFC2D7A5340003D155D /* GenerateContentIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86D77DFB2D7A5340003D155D /* GenerateContentIntegrationTests.swift */; };
2728
86D77DFE2D7B5C86003D155D /* GoogleService-Info-Spark.plist in Resources */ = {isa = PBXBuildFile; fileRef = 86D77DFD2D7B5C86003D155D /* GoogleService-Info-Spark.plist */; };
2829
86D77E022D7B63AF003D155D /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86D77E012D7B63AC003D155D /* Constants.swift */; };
@@ -55,6 +56,7 @@
5556
868A7C552CCC271300E449DD /* TestApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TestApp.entitlements; sourceTree = "<group>"; };
5657
8698D7452CD3CF2F00ABA833 /* FirebaseAppTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseAppTestUtils.swift; sourceTree = "<group>"; };
5758
8698D7472CD4332B00ABA833 /* TestAppCheckProviderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAppCheckProviderFactory.swift; sourceTree = "<group>"; };
59+
86CC31342D91EE9E0087E964 /* FirebaseAppUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseAppUtils.swift; sourceTree = "<group>"; };
5860
86D77DFB2D7A5340003D155D /* GenerateContentIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateContentIntegrationTests.swift; sourceTree = "<group>"; };
5961
86D77DFD2D7B5C86003D155D /* GoogleService-Info-Spark.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info-Spark.plist"; sourceTree = "<group>"; };
6062
86D77E012D7B63AC003D155D /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
@@ -129,6 +131,7 @@
129131
8698D7472CD4332B00ABA833 /* TestAppCheckProviderFactory.swift */,
130132
8661385D2CC943DD00F4B78E /* ContentView.swift */,
131133
86D77E012D7B63AC003D155D /* Constants.swift */,
134+
86CC31342D91EE9E0087E964 /* FirebaseAppUtils.swift */,
132135
);
133136
path = Sources;
134137
sourceTree = "<group>";
@@ -275,6 +278,7 @@
275278
isa = PBXSourcesBuildPhase;
276279
buildActionMask = 2147483647;
277280
files = (
281+
86CC31352D91EE9E0087E964 /* FirebaseAppUtils.swift in Sources */,
278282
8661385E2CC943DD00F4B78E /* ContentView.swift in Sources */,
279283
8661385C2CC943DD00F4B78E /* TestApp.swift in Sources */,
280284
8698D7482CD4332B00ABA833 /* TestAppCheckProviderFactory.swift in Sources */,

0 commit comments

Comments
 (0)