Skip to content

Commit

Permalink
add tests for response object
Browse files Browse the repository at this point in the history
  • Loading branch information
fumito-ito committed Oct 23, 2024
1 parent 7de3a5f commit fcf5db3
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 41 deletions.
67 changes: 40 additions & 27 deletions Sources/AnthropicSwiftSDK-TestUtils/HTTPMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import Foundation

public enum MockInspectType {
case none
case request((URLRequest) -> Void)
case requestHeader(([String: String]?) -> Void)
case request((URLRequest) -> Void, String?)
case requestHeader(([String: String]?) -> Void, String?)
case response(String)
}

Expand All @@ -26,50 +26,63 @@ public class HTTPMock: URLProtocol {
}

public override func startLoading() {
if case let .request(inspection) = Self.inspectType {
if case let .request(inspection, _) = Self.inspectType {
inspection(request)
}

if case let .requestHeader(inspection) = Self.inspectType {
if case let .requestHeader(inspection, _) = Self.inspectType {
inspection(request.allHTTPHeaderFields)
}

if let url = request.url,
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/2", headerFields: nil) {
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)

if case let .response(jsonString) = Self.inspectType,
let data = jsonString.data(using: .utf8) {
switch Self.inspectType {
case .none:
client?.urlProtocol(self, didLoad: getBasicJSONStringData())
case .request(_, let jsonString), .requestHeader(_, let jsonString):
guard let jsonString, let data = jsonString.data(using: .utf8) else {
client?.urlProtocol(self, didLoad: getBasicJSONStringData())
return
}
client?.urlProtocol(self, didLoad: data)
} else {
let basicResponse = """
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello!"
}
],
"model": "claude-2.1",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 6
}
case .response(let jsonString):
guard let data = jsonString.data(using: .utf8) else {
client?.urlProtocol(self, didLoad: getBasicJSONStringData())
return
}
"""
let data = basicResponse.data(using: .utf8)!
client?.urlProtocol(self, didLoad: data)
}
}

client?.urlProtocolDidFinishLoading(self)
}

private func getBasicJSONStringData() -> Data {
let basicResponse = """
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello!"
}
],
"model": "claude-2.1",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 6
}
}
"""
return basicResponse.data(using: .utf8)!
}

public override func stopLoading() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

/// https://docs.anthropic.com/en/docs/build-with-claude/message-batches#retrieving-batch-results
public enum BatchResultType: Decodable {
public enum BatchResultType: String, Decodable {
case succeeded // include the message result as jsonl
case errored
case cancelled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ public struct BatchResponse: Decodable {
/// URL to a .jsonl file containing the results of the Message Batch requests. Specified only once processing ends.
///
/// Results in the file are not guaranteed to be in the same order as requests. Use the custom_id field to match results to requests.
public let resultsURL: String?
public let resultsUrl: String?
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

public struct BatchResult: Decodable {
public let type: BatchResultType
public let message: MessagesResponse
public let message: MessagesResponse?
public let error: StreamingError?
}

public struct BatchResultResponse: Decodable {
public let customId: String
public let result: BatchResult?
public let error: StreamingError?
}
2 changes: 1 addition & 1 deletion Tests/AnthropicSwiftSDKTests/MessagesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class MessagesTests: XCTestCase {
HTTPMock.inspectType = .requestHeader({ headers in
XCTAssertEqual(headers!["x-api-key"], "This-is-test-API-key")
expectation.fulfill()
})
}, nil)

let message = Message(role: .user, content: [.text("This is test text")])
let _ = try await messages.createMessage([message], maxTokens: 1024)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ final class AnthropicAPIClientTests: XCTestCase {
XCTAssertEqual(request.httpMethod, "POST")

expectation.fulfill()
})
}, nil)

let _ = try await client.send(request: NopRequest())
await fulfillment(of: [expectation], timeout: 1.0)
Expand All @@ -51,7 +51,7 @@ final class AnthropicAPIClientTests: XCTestCase {
XCTAssertEqual(request.httpMethod, "POST")

expectation.fulfill()
})
}, nil)

let _ = try await client.stream(request: NopRequest())
await fulfillment(of: [expectation], timeout: 1.0)
Expand All @@ -72,7 +72,7 @@ final class AnthropicAPIClientTests: XCTestCase {
XCTAssertEqual(headers!["anthropic-beta"], "message-batches-2024-09-24")

expectation.fulfill()
})
}, nil)

let _ = try await client.send(request: NopRequest())
await fulfillment(of: [expectation], timeout: 1.0)
Expand All @@ -93,7 +93,7 @@ final class AnthropicAPIClientTests: XCTestCase {
XCTAssertEqual(headers!["anthropic-beta"], "message-batches-2024-09-24")

expectation.fulfill()
})
}, nil)

let _ = try await client.stream(request: NopRequest())
await fulfillment(of: [expectation], timeout: 1.0)
Expand Down
90 changes: 88 additions & 2 deletions Tests/AnthropicSwiftSDKTests/Network/BatchListResponseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,91 @@ import XCTest
@testable import AnthropicSwiftSDK

final class BatchListResponseTests: XCTestCase {
// TODO: write tests
}
func testDecodeBatchListResponse() throws {
let json = """
{
"data": [
{
"id": "batch_123",
"type": "message_batch",
"processing_status": "ended",
"request_counts": {
"processing": 0,
"succeeeded": 95,
"errored": 5,
"canceled": 0,
"expired": 0
},
"ended_at": "2024-10-18T12:05:00Z",
"created_at": "2024-10-18T12:00:00Z",
"expires_at": "2024-10-19T12:00:00Z",
"cancel_initiated_at": null,
"results_url": "https://example.com/results/batch_123.jsonl"
},
{
"id": "batch_456",
"type": "message_batch",
"processing_status": "in_progress",
"request_counts": {
"processing": 100,
"succeeeded": 0,
"errored": 0,
"canceled": 0,
"expired": 0
},
"ended_at": null,
"created_at": "2024-10-18T12:10:00Z",
"expires_at": "2024-10-19T12:10:00Z",
"cancel_initiated_at": null,
"results_url": null
}
],
"has_more": true,
"first_id": "batch_123",
"last_id": "batch_456"
}
"""

let jsonData = json.data(using: .utf8)!

let response = try anthropicJSONDecoder.decode(BatchListResponse.self, from: jsonData)

XCTAssertEqual(response.data.count, 2)

// Test first batch
let firstBatch = response.data[0]
XCTAssertEqual(firstBatch.id, "batch_123")
XCTAssertEqual(firstBatch.type, .message)
XCTAssertEqual(firstBatch.processingStatus, .ended)
XCTAssertEqual(firstBatch.requestCounts.processing, 0)
XCTAssertEqual(firstBatch.requestCounts.succeeeded, 95)
XCTAssertEqual(firstBatch.requestCounts.errored, 5)
XCTAssertEqual(firstBatch.requestCounts.canceled, 0)
XCTAssertEqual(firstBatch.requestCounts.expired, 0)
XCTAssertEqual(firstBatch.endedAt, "2024-10-18T12:05:00Z")
XCTAssertEqual(firstBatch.createdAt, "2024-10-18T12:00:00Z")
XCTAssertEqual(firstBatch.expiresAt, "2024-10-19T12:00:00Z")
XCTAssertNil(firstBatch.cancelInitiatedAt)
XCTAssertEqual(firstBatch.resultsUrl, "https://example.com/results/batch_123.jsonl")

// Test second batch
let secondBatch = response.data[1]
XCTAssertEqual(secondBatch.id, "batch_456")
XCTAssertEqual(secondBatch.type, .message)
XCTAssertEqual(secondBatch.processingStatus, .inProgress)
XCTAssertEqual(secondBatch.requestCounts.processing, 100)
XCTAssertEqual(secondBatch.requestCounts.succeeeded, 0)
XCTAssertEqual(secondBatch.requestCounts.errored, 0)
XCTAssertEqual(secondBatch.requestCounts.canceled, 0)
XCTAssertEqual(secondBatch.requestCounts.expired, 0)
XCTAssertNil(secondBatch.endedAt)
XCTAssertEqual(secondBatch.createdAt, "2024-10-18T12:10:00Z")
XCTAssertEqual(secondBatch.expiresAt, "2024-10-19T12:10:00Z")
XCTAssertNil(secondBatch.cancelInitiatedAt)
XCTAssertNil(secondBatch.resultsUrl)

XCTAssertTrue(response.hasMore)
XCTAssertEqual(response.firstId, "batch_123")
XCTAssertEqual(response.lastId, "batch_456")
}
}
90 changes: 88 additions & 2 deletions Tests/AnthropicSwiftSDKTests/Network/BatchResponseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,91 @@ import XCTest
@testable import AnthropicSwiftSDK

final class BatchResponseTests: XCTestCase {
// TODO: write tests
}
func testDecodeBatchResponse() throws {
let json = """
{
"id": "batch_123456",
"type": "message_batch",
"processing_status": "ended",
"request_counts": {
"processing": 0,
"succeeeded": 95,
"errored": 3,
"canceled": 1,
"expired": 1
},
"ended_at": "2024-10-18T15:30:00Z",
"created_at": "2024-10-18T15:00:00Z",
"expires_at": "2024-10-19T15:00:00Z",
"cancel_initiated_at": null,
"results_url": "https://example.com/results/batch_123456.jsonl"
}
"""

let jsonData = json.data(using: .utf8)!
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let batchResponse = try decoder.decode(BatchResponse.self, from: jsonData)

XCTAssertEqual(batchResponse.id, "batch_123456")
XCTAssertEqual(batchResponse.type, .message)
XCTAssertEqual(batchResponse.processingStatus, .ended)

XCTAssertEqual(batchResponse.requestCounts.processing, 0)
XCTAssertEqual(batchResponse.requestCounts.succeeeded, 95)
XCTAssertEqual(batchResponse.requestCounts.errored, 3)
XCTAssertEqual(batchResponse.requestCounts.canceled, 1)
XCTAssertEqual(batchResponse.requestCounts.expired, 1)

XCTAssertEqual(batchResponse.endedAt, "2024-10-18T15:30:00Z")
XCTAssertEqual(batchResponse.createdAt, "2024-10-18T15:00:00Z")
XCTAssertEqual(batchResponse.expiresAt, "2024-10-19T15:00:00Z")
XCTAssertNil(batchResponse.cancelInitiatedAt)
XCTAssertEqual(batchResponse.resultsUrl, "https://example.com/results/batch_123456.jsonl")
}

func testDecodeBatchResponseWithCancellation() throws {
let json = """
{
"id": "batch_789012",
"type": "message_batch",
"processing_status": "canceling",
"request_counts": {
"processing": 50,
"succeeeded": 40,
"errored": 10,
"canceled": 0,
"expired": 0
},
"ended_at": null,
"created_at": "2024-10-18T16:00:00Z",
"expires_at": "2024-10-19T16:00:00Z",
"cancel_initiated_at": "2024-10-18T16:30:00Z",
"results_url": null
}
"""

let jsonData = json.data(using: .utf8)!
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let batchResponse = try decoder.decode(BatchResponse.self, from: jsonData)

XCTAssertEqual(batchResponse.id, "batch_789012")
XCTAssertEqual(batchResponse.type, .message)
XCTAssertEqual(batchResponse.processingStatus, .canceling)

XCTAssertEqual(batchResponse.requestCounts.processing, 50)
XCTAssertEqual(batchResponse.requestCounts.succeeeded, 40)
XCTAssertEqual(batchResponse.requestCounts.errored, 10)
XCTAssertEqual(batchResponse.requestCounts.canceled, 0)
XCTAssertEqual(batchResponse.requestCounts.expired, 0)

XCTAssertNil(batchResponse.endedAt)
XCTAssertEqual(batchResponse.createdAt, "2024-10-18T16:00:00Z")
XCTAssertEqual(batchResponse.expiresAt, "2024-10-19T16:00:00Z")
XCTAssertEqual(batchResponse.cancelInitiatedAt, "2024-10-18T16:30:00Z")
XCTAssertNil(batchResponse.resultsUrl)
}
}
Loading

0 comments on commit fcf5db3

Please sign in to comment.