Files
hope-voice-api/ios/Tests/ChatTransportTests.swift
2026-10-04 07:23:49 -04:00

53 lines
3.2 KiB
Swift

import XCTest
@testable import Voice
private final class ChatURLProtocol: URLProtocol {
static var handler: ((URLRequest) throws -> (Int, Data))?
override class func canInit(with request: URLRequest) -> Bool { true }
override class func canonicalRequest(for request: URLRequest) -> URLRequest { request }
override func startLoading() {
do {
let (status, body) = try Self.handler!(request)
client?.urlProtocol(self, didReceive: HTTPURLResponse(url: request.url!, statusCode: status, httpVersion: nil, headerFields: nil)!, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: body); client?.urlProtocolDidFinishLoading(self)
} catch { client?.urlProtocol(self, didFailWithError: error) }
}
override func stopLoading() {}
}
final class ChatTransportTests: XCTestCase {
func testBearerSameOriginQueryAndRawUpload() async throws {
guard case .success(let config) = ServerConfig.parse("https://hope.example:9178") else { XCTFail(); return }
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [ChatURLProtocol.self]
let api = ChatAPI(config: config, token: "offline-test-bearer", session: URLSession(configuration: configuration))
ChatURLProtocol.handler = { request in
XCTAssertEqual(request.url?.host, "hope.example")
XCTAssertEqual(request.url?.scheme, "https")
XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer offline-test-bearer")
XCTAssertEqual(URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?.queryItems?.first?.value, "a & b")
return (200, Data("{\"conversations\":[],\"next_cursor\":null}".utf8))
}
_ = try await api.request("api/conversations", query: [URLQueryItem(name: "q", value: "a & b")])
ChatURLProtocol.handler = { request in
XCTAssertEqual(request.httpMethod, "POST")
XCTAssertEqual(request.url?.path, "/api/attachments")
XCTAssertEqual(request.value(forHTTPHeaderField: "X-Filename"), "notes.txt")
XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Type"), "text/plain")
return (201, Data("{}".utf8))
}
_ = try await api.upload(data: Data("test".utf8), filename: "notes.txt", mime: "text/plain")
api.cancel(); ChatURLProtocol.handler = nil
}
func testUnauthorizedAndRedirectStatusCannotBecomeSuccess() async throws {
guard case .success(let config) = ServerConfig.parse("https://hope.example") else { XCTFail(); return }
let configuration = URLSessionConfiguration.ephemeral; configuration.protocolClasses = [ChatURLProtocol.self]
let api = ChatAPI(config: config, token: "offline", session: URLSession(configuration: configuration))
for status in [401, 403, 302, 409] {
ChatURLProtocol.handler = { _ in (status, Data()) }
do { _ = try await api.request("api/conversations"); XCTFail("Expected rejection") }
catch ChatFailure.status(let actual) { XCTAssertEqual(actual, status) }
}
api.cancel(); ChatURLProtocol.handler = nil
}
}