Skip to content

Commit ebd9444

Browse files
committed
empty urlMetadata array tests
1 parent 1f410ae commit ebd9444

File tree

4 files changed

+104
-21
lines changed

4 files changed

+104
-21
lines changed

FirebaseAI/Tests/Unit/GenerativeModelGoogleAITests.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,31 @@ final class GenerativeModelGoogleAITests: XCTestCase {
380380
let successURLMetadata = urlContextMetadata.urlMetadata[1]
381381
XCTAssertEqual(successURLMetadata.retrievalStatus, .success)
382382

383-
let errorURLMetadata = urlContextMetadata.urlMetadata[2]
383+
let errorURLMetadata = try XCTUnwrap(urlContextMetadata.urlMetadata[2])
384384
XCTAssertEqual(errorURLMetadata.retrievalStatus, .error)
385385
}
386386

387+
func testGenerateContent_success_urlContext_emptyURLMetadata() async throws {
388+
let json = """
389+
{
390+
"candidates": [
391+
{
392+
"content": { "role": "model", "parts": [ { "text": "Some text." } ] },
393+
"finishReason": "STOP",
394+
"urlContextMetadata": { "urlMetadata": [] }
395+
}
396+
]
397+
}
398+
"""
399+
MockURLProtocol.requestHandler = nil
400+
MockURLProtocol.dataRequestHandler = try GenerativeModelTestUtil.httpRequestHandler(json: json)
401+
402+
let response = try await model.generateContent(testPrompt)
403+
404+
let candidate = try XCTUnwrap(response.candidates.first)
405+
XCTAssertNil(candidate.urlContextMetadata)
406+
}
407+
387408
func testGenerateContent_failure_invalidAPIKey() async throws {
388409
let expectedStatusCode = 400
389410
MockURLProtocol.requestHandler = try GenerativeModelTestUtil.httpRequestHandler(

FirebaseAI/Tests/Unit/GenerativeModelVertexAITests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,27 @@ final class GenerativeModelVertexAITests: XCTestCase {
555555
XCTAssertEqual(urlMetadata.retrievalStatus, .error)
556556
}
557557

558+
func testGenerateContent_success_urlContext_emptyURLMetadata() async throws {
559+
let json = """
560+
{
561+
"candidates": [
562+
{
563+
"content": { "role": "model", "parts": [ { "text": "Some text." } ] },
564+
"finishReason": "STOP",
565+
"urlContextMetadata": { "urlMetadata": [] }
566+
}
567+
]
568+
}
569+
"""
570+
MockURLProtocol.requestHandler = nil
571+
MockURLProtocol.dataRequestHandler = try GenerativeModelTestUtil.httpRequestHandler(json: json)
572+
573+
let response = try await model.generateContent(testPrompt)
574+
575+
let candidate = try XCTUnwrap(response.candidates.first)
576+
XCTAssertNil(candidate.urlContextMetadata)
577+
}
578+
558579
func testGenerateContent_success_image_invalidSafetyRatingsIgnored() async throws {
559580
MockURLProtocol.requestHandler = try GenerativeModelTestUtil.httpRequestHandler(
560581
forResource: "unary-success-image-invalid-safety-ratings",

FirebaseAI/Tests/Unit/MockURLProtocol.swift

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ class MockURLProtocol: URLProtocol, @unchecked Sendable {
2121
URLResponse,
2222
AsyncLineSequence<URL.AsyncBytes>?
2323
))?
24+
25+
nonisolated(unsafe) static var dataRequestHandler: ((URLRequest) throws -> (
26+
URLResponse,
27+
Data?
28+
))?
29+
2430
override class func canInit(with request: URLRequest) -> Bool {
2531
#if os(watchOS)
2632
print("MockURLProtocol cannot be used on watchOS.")
@@ -33,34 +39,44 @@ class MockURLProtocol: URLProtocol, @unchecked Sendable {
3339
override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request }
3440

3541
override func startLoading() {
36-
guard let requestHandler = MockURLProtocol.requestHandler else {
37-
fatalError("`requestHandler` is nil.")
38-
}
3942
guard let client = client else {
4043
fatalError("`client` is nil.")
4144
}
4245

43-
Task {
44-
let (response, stream) = try requestHandler(self.request)
45-
client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
46-
if let stream = stream {
47-
do {
48-
for try await line in stream {
49-
guard let data = line.data(using: .utf8) else {
50-
fatalError("Failed to convert \"\(line)\" to UTF8 data.")
46+
if let requestHandler = MockURLProtocol.requestHandler {
47+
Task {
48+
let (response, stream) = try requestHandler(self.request)
49+
client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
50+
if let stream = stream {
51+
do {
52+
for try await line in stream {
53+
guard let data = line.data(using: .utf8) else {
54+
fatalError("Failed to convert \"\(line)\" to UTF8 data.")
55+
}
56+
client.urlProtocol(self, didLoad: data)
57+
// Add a newline character since AsyncLineSequence strips them when reading line by
58+
// line;
59+
// without the following, the whole file is delivered as a single line.
60+
client.urlProtocol(self, didLoad: "\n".data(using: .utf8)!)
5161
}
52-
client.urlProtocol(self, didLoad: data)
53-
// Add a newline character since AsyncLineSequence strips them when reading line by
54-
// line;
55-
// without the following, the whole file is delivered as a single line.
56-
client.urlProtocol(self, didLoad: "\n".data(using: .utf8)!)
62+
} catch {
63+
client.urlProtocol(self, didFailWithError: error)
64+
XCTFail("Unexpected failure reading lines from stream: \(error.localizedDescription)")
5765
}
58-
} catch {
59-
client.urlProtocol(self, didFailWithError: error)
60-
XCTFail("Unexpected failure reading lines from stream: \(error.localizedDescription)")
6166
}
67+
client.urlProtocolDidFinishLoading(self)
68+
}
69+
} else if let dataRequestHandler = MockURLProtocol.dataRequestHandler {
70+
Task {
71+
let (response, data) = try dataRequestHandler(self.request)
72+
client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
73+
if let data = data {
74+
client.urlProtocol(self, didLoad: data)
75+
}
76+
client.urlProtocolDidFinishLoading(self)
6277
}
63-
client.urlProtocolDidFinishLoading(self)
78+
} else {
79+
fatalError("No request handler set.")
6480
}
6581
}
6682

FirebaseAI/Tests/Unit/TestUtilities/GenerativeModelTestUtil.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ enum GenerativeModelTestUtil {
7979
#endif // os(watchOS)
8080
}
8181

82+
/// Returns an HTTP request handler that returns the given `json` string as a response.
83+
static func httpRequestHandler(json: String,
84+
statusCode: Int = 200) throws -> ((URLRequest) throws -> (
85+
URLResponse,
86+
Data?
87+
)) {
88+
// Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
89+
// https://developer.apple.com/documentation/foundation/urlprotocol for details.
90+
#if os(watchOS)
91+
throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
92+
#else // os(watchOS)
93+
let data = try XCTUnwrap(json.data(using: .utf8))
94+
return { request in
95+
let requestURL = try XCTUnwrap(request.url)
96+
let response = try XCTUnwrap(HTTPURLResponse(
97+
url: requestURL,
98+
statusCode: statusCode,
99+
httpVersion: nil,
100+
headerFields: nil
101+
))
102+
return (response, data)
103+
}
104+
#endif // os(watchOS)
105+
}
106+
82107
static func nonHTTPRequestHandler() throws -> ((URLRequest) -> (
83108
URLResponse,
84109
AsyncLineSequence<URL.AsyncBytes>?

0 commit comments

Comments
 (0)