diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 12dc3e43..54678363 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -1122,10 +1122,12 @@ public final class AuthClient: Sendable { if let jwt { request.headerFields[.authorization] = "Bearer \(jwt)" + let (data, _) = try await api.execute(for: request, from: nil) + return try configuration.decoder.decode(User.self, from: data) + } else { + let (data, _) = try await api.authorizedExecute(for: request, from: nil) + return try configuration.decoder.decode(User.self, from: data) } - - let (data, _) = try await api.authorizedExecute(for: request, from: nil) - return try configuration.decoder.decode(User.self, from: data) } /// Updates user data, if there is a logged in user. diff --git a/Sources/Auth/AuthClientConfiguration.swift b/Sources/Auth/AuthClientConfiguration.swift index 17289a85..c7917599 100644 --- a/Sources/Auth/AuthClientConfiguration.swift +++ b/Sources/Auth/AuthClientConfiguration.swift @@ -83,7 +83,7 @@ extension AuthClient { }, autoRefreshToken: Bool = AuthClient.Configuration.defaultAutoRefreshToken ) { - let headers = headers.merging(with: Configuration.defaultHeaders) + let headers = Configuration.defaultHeaders.merging(headers) { $1 } self.url = url ?? defaultAuthURL self.headers = headers diff --git a/Sources/Auth/Internal/APIClient.swift b/Sources/Auth/Internal/APIClient.swift index 2a9e36c8..2abd1c00 100644 --- a/Sources/Auth/Internal/APIClient.swift +++ b/Sources/Auth/Internal/APIClient.swift @@ -35,12 +35,9 @@ struct APIClient: Sendable { func execute( for request: HTTPRequest, from bodyData: Data? - ) async throws -> ( - Data, - HTTPResponse - ) { + ) async throws -> (Data, HTTPResponse) { var request = request - request.headerFields = HTTPFields(configuration.headers).merging(with: request.headerFields) + request.headerFields = request.headerFields.merging(configuration.headers) { $1 } if request.headerFields[.apiVersionHeaderName] == nil { request.headerFields[.apiVersionHeaderName] = apiVersions[._20240101]!.name.rawValue diff --git a/Sources/Functions/FunctionsClient.swift b/Sources/Functions/FunctionsClient.swift index 45812090..782b8904 100644 --- a/Sources/Functions/FunctionsClient.swift +++ b/Sources/Functions/FunctionsClient.swift @@ -236,8 +236,12 @@ public final class FunctionsClient: Sendable { url: url .appendingPathComponent(functionName) .appendingQueryItems(options.query), - headerFields: mutableState.headers.merging(with: options.headers) + headerFields: mutableState.headers.merging(options.headers) { $1 } ) + + if options.body != nil && request.headerFields[.contentType] == nil { + request.headerFields[.contentType] = "application/json" + } if let region = options.region ?? region { request.headerFields[.xRegion] = region diff --git a/Sources/Functions/Types.swift b/Sources/Functions/Types.swift index afa734fc..f7356f07 100644 --- a/Sources/Functions/Types.swift +++ b/Sources/Functions/Types.swift @@ -65,7 +65,7 @@ public struct FunctionInvokeOptions: Sendable { } self.method = method - self.headers = defaultHeaders.merging(with: headers) + self.headers = defaultHeaders.merging(headers) { $1 } self.region = region self.query = query } diff --git a/Sources/Helpers/HTTP/HTTPClient.swift b/Sources/Helpers/HTTP/HTTPClient.swift index 09e83170..8b277dc5 100644 --- a/Sources/Helpers/HTTP/HTTPClient.swift +++ b/Sources/Helpers/HTTP/HTTPClient.swift @@ -32,8 +32,12 @@ package actor HTTPClient: HTTPClientType { _ request: HTTPRequest, _ bodyData: Data? ) async throws -> (Data, HTTPResponse) { - var next: @Sendable (HTTPRequest, Data?) async throws -> (Data, HTTPResponse) = { - return try await self.fetch($0, $1) + var next: @Sendable (HTTPRequest, Data?) async throws -> (Data, HTTPResponse) = { request, bodyData in + var request = request + if bodyData != nil && request.headerFields[.contentType] == nil { + request.headerFields[.contentType] = "application/json" + } + return try await self.fetch(request, bodyData) } for interceptor in interceptors.reversed() { diff --git a/Sources/Helpers/HTTP/HTTPFields.swift b/Sources/Helpers/HTTP/HTTPFields.swift index 141e1927..e06ca019 100644 --- a/Sources/Helpers/HTTP/HTTPFields.swift +++ b/Sources/Helpers/HTTP/HTTPFields.swift @@ -9,17 +9,21 @@ extension HTTPFields { return .init(keyValues, uniquingKeysWith: { $1 }) } - package mutating func merge(with other: Self) { - for field in other { - self[field.name] = field.value - } + package mutating func merge( + _ other: Self, + uniquingKeysWith combine: (String, String) throws -> String + ) rethrows { + self = try self.merging(other, uniquingKeysWith: combine) } - - package func merging(with other: Self) -> Self { + + package func merging( + _ other: Self, + uniquingKeysWith combine: (String, String) throws -> String + ) rethrows -> HTTPFields { var copy = self for field in other { - copy[field.name] = field.value + copy[field.name] = try combine(self[field.name] ?? "", field.value) } return copy diff --git a/Sources/Helpers/HTTP/LoggerInterceptor.swift b/Sources/Helpers/HTTP/LoggerInterceptor.swift index 226493be..564115a3 100644 --- a/Sources/Helpers/HTTP/LoggerInterceptor.swift +++ b/Sources/Helpers/HTTP/LoggerInterceptor.swift @@ -31,6 +31,10 @@ package struct LoggerInterceptor: HTTPClientInterceptor { ) do { + var request = request + if bodyData != nil && request.headerFields[.contentType] == nil { + request.headerFields[.contentType] = "application/json" + } let (data, response) = try await next(request, bodyData) logger.verbose( """ diff --git a/Sources/Helpers/HTTP/URLSession+HTTPRequest.swift b/Sources/Helpers/HTTP/URLSession+HTTPRequest.swift new file mode 100644 index 00000000..daf775f9 --- /dev/null +++ b/Sources/Helpers/HTTP/URLSession+HTTPRequest.swift @@ -0,0 +1,113 @@ +import Foundation +import HTTPTypes + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +#if !os(WASI) + +extension URLSessionTask { + /// The original HTTP request this task was created with. + public var originalHTTPRequest: HTTPRequest? { + self.originalRequest?.httpRequest + } + + /// The current HTTP request -- may differ from the `originalHTTPRequest` due to HTTP redirection. + public var currentHTTPRequest: HTTPRequest? { + self.currentRequest?.httpRequest + } + + /// The HTTP response received from the server. + public var httpResponse: HTTPResponse? { + (self.response as? HTTPURLResponse)?.httpResponse + } +} + +private enum HTTPTypeConversionError: Error { + case failedToConvertHTTPRequestToURLRequest + case failedToConvertURLResponseToHTTPResponse +} + +#endif + +#if canImport(FoundationNetworking) && compiler(<6) + +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +extension URLSession { + /// Convenience method to load data using an `HTTPRequest`; creates and resumes a `URLSessionDataTask` internally. + /// + /// - Parameter request: The `HTTPRequest` for which to load data. + /// - Parameter delegate: Task-specific delegate. + /// - Returns: Data and response. + public func data( + for request: HTTPRequest, + delegate: (any URLSessionTaskDelegate)? = nil + ) async throws -> (Data, HTTPResponse) { + guard let urlRequest = URLRequest(httpRequest: request) else { + throw HTTPTypeConversionError.failedToConvertHTTPRequestToURLRequest + } + let (data, urlResponse) = try await self.data(for: urlRequest, delegate: delegate) + guard let response = (urlResponse as? HTTPURLResponse)?.httpResponse else { + throw HTTPTypeConversionError.failedToConvertURLResponseToHTTPResponse + } + return (data, response) + } + + /// Convenience method to upload data using an `HTTPRequest`, creates and resumes a `URLSessionUploadTask` internally. + /// + /// - Parameter request: The `HTTPRequest` for which to upload data. + /// - Parameter bodyData: Data to upload. + /// - Parameter delegate: Task-specific delegate. + /// - Returns: Data and response. + public func upload( + for request: HTTPRequest, + from bodyData: Data, + delegate: (any URLSessionTaskDelegate)? = nil + ) async throws -> (Data, HTTPResponse) { + guard let urlRequest = URLRequest(httpRequest: request) else { + throw HTTPTypeConversionError.failedToConvertHTTPRequestToURLRequest + } + let (data, urlResponse) = try await self.upload(for: urlRequest, from: bodyData, delegate: delegate) + guard let response = (urlResponse as? HTTPURLResponse)?.httpResponse else { + throw HTTPTypeConversionError.failedToConvertURLResponseToHTTPResponse + } + return (data, response) + } +} + +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +extension URLSession { + /// Convenience method to load data using an `HTTPRequest`; creates and resumes a `URLSessionDataTask` internally. + /// + /// - Parameter request: The `HTTPRequest` for which to load data. + /// - Returns: Data and response. + public func data(for request: HTTPRequest) async throws -> (Data, HTTPResponse) { + guard let urlRequest = URLRequest(httpRequest: request) else { + throw HTTPTypeConversionError.failedToConvertHTTPRequestToURLRequest + } + let (data, urlResponse) = try await self.data(for: urlRequest) + guard let response = (urlResponse as? HTTPURLResponse)?.httpResponse else { + throw HTTPTypeConversionError.failedToConvertURLResponseToHTTPResponse + } + return (data, response) + } + + /// Convenience method to upload data using an `HTTPRequest`, creates and resumes a `URLSessionUploadTask` internally. + /// + /// - Parameter request: The `HTTPRequest` for which to upload data. + /// - Parameter bodyData: Data to upload. + /// - Returns: Data and response. + public func upload(for request: HTTPRequest, from bodyData: Data) async throws -> (Data, HTTPResponse) { + guard let urlRequest = URLRequest(httpRequest: request) else { + throw HTTPTypeConversionError.failedToConvertHTTPRequestToURLRequest + } + let (data, urlResponse) = try await self.upload(for: urlRequest, from: bodyData) + guard let response = (urlResponse as? HTTPURLResponse)?.httpResponse else { + throw HTTPTypeConversionError.failedToConvertURLResponseToHTTPResponse + } + return (data, response) + } +} + +#endif diff --git a/Sources/PostgREST/PostgrestBuilder.swift b/Sources/PostgREST/PostgrestBuilder.swift index 3726a97a..8eb1c411 100644 --- a/Sources/PostgREST/PostgrestBuilder.swift +++ b/Sources/PostgREST/PostgrestBuilder.swift @@ -102,7 +102,7 @@ public class PostgrestBuilder: @unchecked Sendable { options: FetchOptions, decode: (Data) throws -> T ) async throws -> PostgrestResponse { - let request = mutableState.withValue { + let (request, bodyData) = mutableState.withValue { $0.fetchOptions = options if $0.fetchOptions.head { @@ -130,10 +130,10 @@ public class PostgrestBuilder: @unchecked Sendable { } } - return $0.request + return ($0.request, $0.bodyData) } - let (data, response) = try await http.send(request, nil) + let (data, response) = try await http.send(request, bodyData) guard 200..<300 ~= response.status.code else { if let error = try? configuration.decoder.decode(PostgrestError.self, from: data) { diff --git a/Sources/PostgREST/PostgrestClient.swift b/Sources/PostgREST/PostgrestClient.swift index 701d07b8..2ba81f04 100644 --- a/Sources/PostgREST/PostgrestClient.swift +++ b/Sources/PostgREST/PostgrestClient.swift @@ -72,7 +72,7 @@ public final class PostgrestClient: Sendable { public init(configuration: Configuration) { _configuration = LockIsolated(configuration) _configuration.withValue { - $0.headers.merge(with: Configuration.defaultHeaders) + $0.headers.merge(Configuration.defaultHeaders) { l, _ in l } } } diff --git a/Sources/Realtime/PhoenixTransport.swift b/Sources/Realtime/PhoenixTransport.swift index 3c416f66..3daff5bc 100644 --- a/Sources/Realtime/PhoenixTransport.swift +++ b/Sources/Realtime/PhoenixTransport.swift @@ -201,7 +201,6 @@ open class URLSessionTransport: NSObject, PhoenixTransport, URLSessionWebSocketD var request = URLRequest(url: url) for (key, value) in headers { - guard let value = value as? String else { continue } request.addValue(value, forHTTPHeaderField: key) } diff --git a/Sources/Storage/StorageApi.swift b/Sources/Storage/StorageApi.swift index f22915a7..f2e040f8 100644 --- a/Sources/Storage/StorageApi.swift +++ b/Sources/Storage/StorageApi.swift @@ -35,7 +35,7 @@ public class StorageApi: @unchecked Sendable { from bodyData: Data? ) async throws -> (Data, HTTPResponse) { var request = request - request.headerFields = configuration.headers.merging(with: request.headerFields) + request.headerFields = configuration.headers.merging(request.headerFields) { $1 } let (data, response) = try await http.send(request, bodyData) diff --git a/Sources/Storage/StorageFileApi.swift b/Sources/Storage/StorageFileApi.swift index 82ac8126..91314746 100644 --- a/Sources/Storage/StorageFileApi.swift +++ b/Sources/Storage/StorageFileApi.swift @@ -80,7 +80,7 @@ public class StorageFileApi: StorageApi, @unchecked Sendable { options: FileOptions? ) async throws -> FileUploadResponse { let options = options ?? defaultFileOptions - var headers = options.headers.map { HTTPFields($0) } ?? HTTPFields() + var headers = options.headers ?? HTTPFields() if method == .post { headers[.xUpsert] = "\(options.upsert)" @@ -647,7 +647,7 @@ public class StorageFileApi: StorageApi, @unchecked Sendable { options: FileOptions? ) async throws -> SignedURLUploadResponse { let options = options ?? defaultFileOptions - var headers = options.headers.map { HTTPFields($0) } ?? HTTPFields() + var headers = options.headers ?? HTTPFields() headers[.xUpsert] = "\(options.upsert)" headers[.duplex] = options.duplex diff --git a/Sources/Supabase/SupabaseClient.swift b/Sources/Supabase/SupabaseClient.swift index ff88ef9f..196e508e 100644 --- a/Sources/Supabase/SupabaseClient.swift +++ b/Sources/Supabase/SupabaseClient.swift @@ -47,7 +47,7 @@ public final class SupabaseClient: Sendable { $0.rest = PostgrestClient( url: databaseURL, schema: options.db.schema, - headers: _headers, + headers: headers, logger: options.global.logger, fetch: { request, bodyData in if let bodyData { @@ -72,7 +72,7 @@ public final class SupabaseClient: Sendable { $0.storage = SupabaseStorageClient( configuration: StorageClientConfiguration( url: storageURL, - headers: _headers, + headers: headers, session: StorageHTTPSession { request, bodyData in if let bodyData { return try await self.uploadWithAuth(for: request, from: bodyData) @@ -100,7 +100,7 @@ public final class SupabaseClient: Sendable { if $0.functions == nil { $0.functions = FunctionsClient( url: functionsURL, - headers: _headers, + headers: headers, region: options.functions.region, logger: options.global.logger, fetch: { request, bodyData in @@ -116,14 +116,11 @@ public final class SupabaseClient: Sendable { return $0.functions! } } - - let _headers: HTTPFields + /// Headers provided to the inner clients on initialization. /// /// - Note: This collection is non-mutable, if you want to provide different headers, pass it in ``SupabaseClientOptions/GlobalOptions/headers``. - public var headers: [String: String] { - _headers.dictionary - } + public let headers: HTTPFields struct MutableState { var listenForAuthEventsTask: Task? @@ -177,14 +174,14 @@ public final class SupabaseClient: Sendable { .authorization: "Bearer \(supabaseKey)", .apiKey: supabaseKey, ] - _headers = headers.merging(with: options.global.headers) + self.headers = options.global.headers.merging(headers) { $1 } // default storage key uses the supabase project ref as a namespace let defaultStorageKey = "sb-\(supabaseURL.host!.split(separator: ".")[0])-auth-token" _auth = AuthClient( url: supabaseURL.appendingPathComponent("/auth/v1"), - headers: _headers, + headers: self.headers, flowType: options.auth.flowType, redirectToURL: options.auth.redirectToURL, storageKey: options.auth.storageKey ?? defaultStorageKey, @@ -206,13 +203,13 @@ public final class SupabaseClient: Sendable { _realtime = UncheckedSendable( RealtimeClient( supabaseURL.appendingPathComponent("/realtime/v1").absoluteString, - headers: _headers, - params: _headers.dictionary + headers: headers, + params: headers.dictionary ) ) var realtimeOptions = options.realtime - realtimeOptions.headers.merge(with: _headers) + realtimeOptions.headers.merge(self.headers) { $1 } if realtimeOptions.logger == nil { realtimeOptions.logger = options.global.logger } diff --git a/Sources/TestHelpers/HTTPClientMock.swift b/Sources/TestHelpers/HTTPClientMock.swift index 3de790de..aa09ecc9 100644 --- a/Sources/TestHelpers/HTTPClientMock.swift +++ b/Sources/TestHelpers/HTTPClientMock.swift @@ -9,14 +9,16 @@ import ConcurrencyExtras import Foundation import Helpers import XCTestDynamicOverlay +import HTTPTypes package actor HTTPClientMock: HTTPClientType { + package struct MockNotFound: Error {} - private var mocks = [@Sendable (HTTPRequest) async throws -> HTTPResponse?]() + private var mocks = [@Sendable (HTTPRequest, Data?) async throws -> (Data, HTTPResponse)?]() /// Requests received by this client in order. - package var receivedRequests: [HTTPRequest] = [] + package var receivedRequests: [(HTTPRequest, Data?)] = [] /// Responses returned by this client in order. package var returnedResponses: [Result] = [] @@ -25,12 +27,12 @@ package actor HTTPClientMock: HTTPClientType { @discardableResult package func when( - _ request: @escaping @Sendable (HTTPRequest) -> Bool, - return response: @escaping @Sendable (HTTPRequest) async throws -> HTTPResponse + _ request: @escaping @Sendable (HTTPRequest, Data?) -> Bool, + return response: @escaping @Sendable (HTTPRequest, Data?) async throws -> (Data, HTTPResponse) ) -> Self { - mocks.append { r in - if request(r) { - return try await response(r) + mocks.append { r, b in + if request(r, b) { + return try await response(r, b) } return nil } @@ -39,19 +41,19 @@ package actor HTTPClientMock: HTTPClientType { @discardableResult package func any( - _ response: @escaping @Sendable (HTTPRequest) async throws -> HTTPResponse + _ response: @escaping @Sendable (HTTPRequest, Data?) async throws -> (Data, HTTPResponse) ) -> Self { - when({ _ in true }, return: response) + when({ _, _ in true }, return: response) } - package func send(_ request: HTTPRequest) async throws -> HTTPResponse { - receivedRequests.append(request) + package func send(_ request: HTTPRequest, _ bodyData: Data?) async throws -> (Data, HTTPResponse) { + receivedRequests.append((request, bodyData)) for mock in mocks { do { - if let response = try await mock(request) { + if let (data, response) = try await mock(request, bodyData) { returnedResponses.append(.success(response)) - return response + return (data, response) } } catch { returnedResponses.append(.failure(error)) diff --git a/Tests/AuthTests/AuthClientMultipleInstancesTests.swift b/Tests/AuthTests/AuthClientMultipleInstancesTests.swift index 26998388..fe4c3231 100644 --- a/Tests/AuthTests/AuthClientMultipleInstancesTests.swift +++ b/Tests/AuthTests/AuthClientMultipleInstancesTests.swift @@ -5,10 +5,11 @@ // Created by Guilherme Souza on 05/07/24. // -@testable import Auth import TestHelpers import XCTest +@testable import Auth + final class AuthClientMultipleInstancesTests: XCTestCase { func testMultipleAuthClientInstances() { let url = URL(string: "http://localhost:54321/auth")! diff --git a/Tests/AuthTests/AuthClientTests.swift b/Tests/AuthTests/AuthClientTests.swift index 17905696..9c4fd989 100644 --- a/Tests/AuthTests/AuthClientTests.swift +++ b/Tests/AuthTests/AuthClientTests.swift @@ -7,6 +7,7 @@ import ConcurrencyExtras import CustomDump +import HTTPTypes import InlineSnapshotTesting import TestHelpers import XCTest @@ -80,8 +81,8 @@ final class AuthClientTests: XCTestCase { } func testSignOut() async throws { - sut = makeSUT { _ in - .stub() + sut = makeSUT { _, _ in + TestStub.stub() } Dependencies[sut.clientID].sessionStorage.store(.validSession) @@ -109,8 +110,8 @@ final class AuthClientTests: XCTestCase { } func testSignOutWithOthersScopeShouldNotRemoveLocalSession() async throws { - sut = makeSUT { _ in - .stub() + sut = makeSUT { _, _ in + TestStub.stub() } Dependencies[sut.clientID].sessionStorage.store(.validSession) @@ -122,14 +123,12 @@ final class AuthClientTests: XCTestCase { } func testSignOutShouldRemoveSessionIfUserIsNotFound() async throws { - sut = makeSUT { _ in + sut = makeSUT { _, _ in throw AuthError.api( message: "", errorCode: .unknown, - underlyingData: Data(), - underlyingResponse: HTTPURLResponse( - url: URL(string: "http://localhost")!, statusCode: 404, httpVersion: nil, - headerFields: nil)! + data: Data(), + response: HTTPResponse(status: .init(code: 404)) ) } @@ -155,14 +154,12 @@ final class AuthClientTests: XCTestCase { } func testSignOutShouldRemoveSessionIfJWTIsInvalid() async throws { - sut = makeSUT { _ in + sut = makeSUT { _, _ in throw AuthError.api( message: "", errorCode: .invalidCredentials, - underlyingData: Data(), - underlyingResponse: HTTPURLResponse( - url: URL(string: "http://localhost")!, statusCode: 401, httpVersion: nil, - headerFields: nil)! + data: Data(), + response: HTTPResponse(status: .init(code: 401)) ) } @@ -188,14 +185,12 @@ final class AuthClientTests: XCTestCase { } func testSignOutShouldRemoveSessionIf403Returned() async throws { - sut = makeSUT { _ in + sut = makeSUT { _, _ in throw AuthError.api( message: "", errorCode: .invalidCredentials, - underlyingData: Data(), - underlyingResponse: HTTPURLResponse( - url: URL(string: "http://localhost")!, statusCode: 403, httpVersion: nil, - headerFields: nil)! + data: Data(), + response: HTTPResponse(status: .init(code: 403)) ) } @@ -223,8 +218,8 @@ final class AuthClientTests: XCTestCase { func testSignInAnonymously() async throws { let session = Session(fromMockNamed: "anonymous-sign-in-response") - let sut = makeSUT { _ in - .stub(fromFileName: "anonymous-sign-in-response") + let sut = makeSUT { _, _ in + TestStub.stub(fromFileName: "anonymous-sign-in-response") } let eventsTask = Task { @@ -243,8 +238,8 @@ final class AuthClientTests: XCTestCase { } func testSignInWithOAuth() async throws { - let sut = makeSUT { _ in - .stub(fromFileName: "session") + let sut = makeSUT { _, _ in + TestStub.stub(fromFileName: "session") } let eventsTask = Task { @@ -266,8 +261,8 @@ final class AuthClientTests: XCTestCase { } func testGetLinkIdentityURL() async throws { - let sut = makeSUT { _ in - .stub( + let sut = makeSUT { _, _ in + TestStub.stub( """ { "url" : "https://github.com/login/oauth/authorize?client_id=1234&redirect_to=com.supabase.swift-examples://&redirect_uri=http://127.0.0.1:54321/auth/v1/callback&response_type=code&scope=user:email&skip_http_redirect=true&state=jwt" @@ -295,8 +290,8 @@ final class AuthClientTests: XCTestCase { func testLinkIdentity() async throws { let url = "https://github.com/login/oauth/authorize?client_id=1234&redirect_to=com.supabase.swift-examples://&redirect_uri=http://127.0.0.1:54321/auth/v1/callback&response_type=code&scope=user:email&skip_http_redirect=true&state=jwt" - let sut = makeSUT { _ in - .stub( + let sut = makeSUT { _, _ in + TestStub.stub( """ { "url" : "\(url)" @@ -318,12 +313,12 @@ final class AuthClientTests: XCTestCase { } func testAdminListUsers() async throws { - let sut = makeSUT { _ in - .stub( + let sut = makeSUT { _, _ in + TestStub.stub( fromFileName: "list-users-response", headers: [ - "X-Total-Count": "669", - "Link": + .xTotalCount: "669", + .link: "; rel=\"next\", ; rel=\"last\"", ] ) @@ -336,12 +331,12 @@ final class AuthClientTests: XCTestCase { } func testAdminListUsers_noNextPage() async throws { - let sut = makeSUT { _ in - .stub( + let sut = makeSUT { _, _ in + TestStub.stub( fromFileName: "list-users-response", headers: [ - "X-Total-Count": "669", - "Link": "; rel=\"last\"", + .xTotalCount: "669", + .link: "; rel=\"last\"", ] ) } @@ -378,20 +373,20 @@ final class AuthClientTests: XCTestCase { } private func makeSUT( - fetch: ((URLRequest) async throws -> HTTPResponse)? = nil + fetch: ((HTTPRequest, Data?) async throws -> (Data, HTTPResponse))? = nil ) -> AuthClient { let configuration = AuthClient.Configuration( url: clientURL, - headers: ["Apikey": "dummy.api.key"], + headers: [.apiKey: "dummy.api.key"], localStorage: storage, logger: nil, - fetch: { request in + fetch: { request, body in guard let fetch else { throw UnimplementedError() } - let response = try await fetch(request) - return (response.data, response.underlyingResponse) + let (data, response) = try await fetch(request, body) + return (data, response) } ) @@ -401,52 +396,47 @@ final class AuthClientTests: XCTestCase { } } -extension HTTPResponse { +struct TestStub { static func stub( _ body: String = "", code: Int = 200, - headers: [String: String]? = nil - ) -> HTTPResponse { - HTTPResponse( - data: body.data(using: .utf8)!, - response: HTTPURLResponse( - url: clientURL, - statusCode: code, - httpVersion: nil, + headers: HTTPFields = [:] + ) -> (Data, HTTPResponse) { + ( + Data(body.utf8), + HTTPResponse( + status: .init(code: code), headerFields: headers - )! + ) ) } static func stub( fromFileName fileName: String, code: Int = 200, - headers: [String: String]? = nil - ) -> HTTPResponse { - HTTPResponse( - data: json(named: fileName), - response: HTTPURLResponse( - url: clientURL, - statusCode: code, - httpVersion: nil, + headers: HTTPFields = [:] + ) -> (Data, HTTPResponse) { + ( + json(named: fileName), + HTTPResponse( + status: .init(code: code), headerFields: headers - )! + ) ) } static func stub( _ value: some Encodable, code: Int = 200, - headers: [String: String]? = nil - ) -> HTTPResponse { - HTTPResponse( - data: try! AuthClient.Configuration.jsonEncoder.encode(value), - response: HTTPURLResponse( - url: clientURL, - statusCode: code, - httpVersion: nil, + headers: HTTPFields = [:] + ) -> (Data, HTTPResponse) { + ( + try! AuthClient.Configuration.jsonEncoder.encode(value), + HTTPResponse( + status: .init(code: code), headerFields: headers - )! + ) ) + } } diff --git a/Tests/AuthTests/AuthErrorTests.swift b/Tests/AuthTests/AuthErrorTests.swift index 66695263..782de4d4 100644 --- a/Tests/AuthTests/AuthErrorTests.swift +++ b/Tests/AuthTests/AuthErrorTests.swift @@ -5,9 +5,11 @@ // Created by Guilherme Souza on 29/08/24. // -@testable import Auth +import HTTPTypes import XCTest +@testable import Auth + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -25,13 +27,17 @@ final class AuthErrorTests: XCTestCase { let api = AuthError.api( message: "API Error", errorCode: .emailConflictIdentityNotDeletable, - underlyingData: Data(), - underlyingResponse: HTTPURLResponse(url: URL(string: "http://localhost")!, statusCode: 400, httpVersion: nil, headerFields: nil)! + data: Data(), + response: HTTPResponse(status: .init(code: 400)) ) XCTAssertEqual(api.errorCode, .emailConflictIdentityNotDeletable) XCTAssertEqual(api.message, "API Error") - let pkceGrantCodeExchange = AuthError.pkceGrantCodeExchange(message: "PKCE failure", error: nil, code: nil) + let pkceGrantCodeExchange = AuthError.pkceGrantCodeExchange( + message: "PKCE failure", + error: nil, + code: nil + ) XCTAssertEqual(pkceGrantCodeExchange.errorCode, .unknown) XCTAssertEqual(pkceGrantCodeExchange.message, "PKCE failure") diff --git a/Tests/AuthTests/ExtractParamsTests.swift b/Tests/AuthTests/ExtractParamsTests.swift index 4ced833e..daacb291 100644 --- a/Tests/AuthTests/ExtractParamsTests.swift +++ b/Tests/AuthTests/ExtractParamsTests.swift @@ -5,9 +5,10 @@ // Created by Guilherme Souza on 23/12/23. // -@testable import Auth import XCTest +@testable import Auth + final class ExtractParamsTests: XCTestCase { func testExtractParamsInQuery() { let code = UUID().uuidString diff --git a/Tests/AuthTests/RequestsTests.swift b/Tests/AuthTests/RequestsTests.swift index a2b36c79..da3e66d0 100644 --- a/Tests/AuthTests/RequestsTests.swift +++ b/Tests/AuthTests/RequestsTests.swift @@ -5,13 +5,15 @@ // Created by Guilherme Souza on 07/10/23. // -@testable import Auth +import HTTPTypes import Helpers import InlineSnapshotTesting import SnapshotTesting import TestHelpers import XCTest +@testable import Auth + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -140,10 +142,10 @@ final class RequestsTests: XCTestCase { #if !os(Linux) && !os(Windows) func testSessionFromURL() async throws { - let sut = makeSUT(fetch: { request in - let authorizationHeader = request.allHTTPHeaderFields?["Authorization"] + let sut = makeSUT(fetch: { request, bodyData in + let authorizationHeader = request.headerFields[.authorization] XCTAssertEqual(authorizationHeader, "bearer accesstoken") - return (json(named: "user"), HTTPURLResponse.stub()) + return (json(named: "user"), HTTPResponse(status: .ok)) }) let currentDate = Date() @@ -430,7 +432,12 @@ final class RequestsTests: XCTestCase { Dependencies[sut.clientID].sessionStorage.store(.validSession) await assert { - _ = try await sut.mfa.enroll(params: MFAEnrollParams(issuer: "supabase.com", friendlyName: "test")) + _ = try await sut.mfa.enroll( + params: MFAEnrollParams( + issuer: "supabase.com", + friendlyName: "test" + ) + ) } } @@ -480,7 +487,13 @@ final class RequestsTests: XCTestCase { Dependencies[sut.clientID].sessionStorage.store(.validSession) await assert { - _ = try await sut.mfa.verify(params: .init(factorId: "123", challengeId: "123", code: "123456")) + _ = try await sut.mfa.verify( + params: .init( + factorId: "123", + challengeId: "123", + code: "123456" + ) + ) } } @@ -516,20 +529,22 @@ final class RequestsTests: XCTestCase { let configuration = AuthClient.Configuration( url: clientURL, - headers: ["Apikey": "dummy.api.key", "X-Client-Info": "gotrue-swift/x.y.z"], + headers: [.apiKey: "dummy.api.key", .xClientInfo: "gotrue-swift/x.y.z"], flowType: flowType, localStorage: InMemoryLocalStorage(), logger: nil, encoder: encoder, - fetch: { request in + fetch: { request, bodyData in DispatchQueue.main.sync { + var request = URLRequest(httpRequest: request)! + request.httpBody = bodyData assertSnapshot( of: request, as: .curl, record: record, file: file, testName: testName, line: line ) } if let fetch { - return try await fetch(request) + return try await fetch(request, bodyData) } throw UnimplementedError() @@ -539,14 +554,3 @@ final class RequestsTests: XCTestCase { return AuthClient(configuration: configuration) } } - -extension HTTPURLResponse { - fileprivate static func stub(code: Int = 200) -> HTTPURLResponse { - HTTPURLResponse( - url: clientURL, - statusCode: code, - httpVersion: nil, - headerFields: nil - )! - } -} diff --git a/Tests/AuthTests/SessionManagerTests.swift b/Tests/AuthTests/SessionManagerTests.swift index 3d866055..36c6cb3b 100644 --- a/Tests/AuthTests/SessionManagerTests.swift +++ b/Tests/AuthTests/SessionManagerTests.swift @@ -82,11 +82,13 @@ final class SessionManagerTests: XCTestCase { let (refreshSessionStream, refreshSessionContinuation) = AsyncStream.makeStream() await http.when( - { $0.url.path.contains("/token") }, - return: { _ in + { request, bodyData in + request.url!.path.contains("/token") + }, + return: { _, _ in refreshSessionCallCount.withValue { $0 += 1 } let session = await refreshSessionStream.first(where: { _ in true })! - return .stub(session) + return TestStub.stub(session) } ) diff --git a/Tests/AuthTests/StoredSessionTests.swift b/Tests/AuthTests/StoredSessionTests.swift index 9e466ec2..47a3592d 100644 --- a/Tests/AuthTests/StoredSessionTests.swift +++ b/Tests/AuthTests/StoredSessionTests.swift @@ -1,9 +1,10 @@ -@testable import Auth import ConcurrencyExtras import SnapshotTesting import TestHelpers import XCTest +@testable import Auth + final class StoredSessionTests: XCTestCase { let clientID = AuthClientID() @@ -37,11 +38,11 @@ final class StoredSessionTests: XCTestCase { appMetadata: [ "provider": "email", "providers": [ - "email", + "email" ], ], userMetadata: [ - "referrer_id": nil, + "referrer_id": nil ], aud: "authenticated", confirmationSentAt: ISO8601DateFormatter().date(from: "2022-04-09T11:57:01Z")!, @@ -65,13 +66,13 @@ final class StoredSessionTests: XCTestCase { identityId: UUID(uuidString: "859F402D-B3DE-4105-A1B9-932836D9193B")!, userId: UUID(uuidString: "859F402D-B3DE-4105-A1B9-932836D9193B")!, identityData: [ - "sub": "859f402d-b3de-4105-a1b9-932836d9193b", + "sub": "859f402d-b3de-4105-a1b9-932836d9193b" ], provider: "email", createdAt: ISO8601DateFormatter().date(from: "2022-04-09T11:57:01Z")!, lastSignInAt: ISO8601DateFormatter().date(from: "2022-04-09T11:57:01Z")!, updatedAt: ISO8601DateFormatter().date(from: "2022-04-09T11:57:01Z")! - ), + ) ], factors: nil ) diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testDeleteUser.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testDeleteUser.1.txt index 2e6dd0e4..650a5fa9 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testDeleteUser.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testDeleteUser.1.txt @@ -1,8 +1,8 @@ curl \ --request DELETE \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"should_soft_delete\":false}" \ "http://localhost:54321/auth/v1/admin/users/E621E1F8-C36C-495A-93FC-0C247A3E6E5F" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testGetLinkIdentityURL.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testGetLinkIdentityURL.1.txt index 44ad6259..80a2c5d3 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testGetLinkIdentityURL.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testGetLinkIdentityURL.1.txt @@ -1,6 +1,6 @@ curl \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/user/identities/authorize?extra_key=extra_value&provider=github&redirect_to=https://supabase.com&scopes=user:email&skip_http_redirect=true" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallenge.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallenge.1.txt index e0263d44..64fa5cc1 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallenge.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallenge.1.txt @@ -1,7 +1,7 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/factors/123/challenge" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallengePhone.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallengePhone.1.txt index 296f1940..12fbbfbd 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallengePhone.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAChallengePhone.1.txt @@ -1,9 +1,9 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"channel\":\"whatsapp\"}" \ "http://localhost:54321/auth/v1/factors/123/challenge" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollLegacy.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollLegacy.1.txt index 51778779..52c874b2 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollLegacy.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollLegacy.1.txt @@ -1,9 +1,9 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"factor_type\":\"totp\",\"friendly_name\":\"test\",\"issuer\":\"supabase.com\"}" \ "http://localhost:54321/auth/v1/factors" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollPhone.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollPhone.1.txt index 5ae9482b..0b311c6b 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollPhone.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollPhone.1.txt @@ -1,9 +1,9 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"factor_type\":\"phone\",\"friendly_name\":\"test\",\"phone\":\"+1 202-918-2132\"}" \ "http://localhost:54321/auth/v1/factors" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollTotp.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollTotp.1.txt index 51778779..52c874b2 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollTotp.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAEnrollTotp.1.txt @@ -1,9 +1,9 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"factor_type\":\"totp\",\"friendly_name\":\"test\",\"issuer\":\"supabase.com\"}" \ "http://localhost:54321/auth/v1/factors" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAUnenroll.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAUnenroll.1.txt index 75c28574..6b0764cc 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAUnenroll.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAUnenroll.1.txt @@ -1,7 +1,7 @@ curl \ --request DELETE \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/factors/123" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAVerify.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAVerify.1.txt index b336ccaf..e08455b4 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAVerify.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testMFAVerify.1.txt @@ -1,9 +1,9 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"challenge_id\":\"123\",\"code\":\"123456\",\"factor_id\":\"123\"}" \ "http://localhost:54321/auth/v1/factors/123/verify" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testReauthenticate.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testReauthenticate.1.txt index d132361a..c03004a7 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testReauthenticate.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testReauthenticate.1.txt @@ -1,6 +1,6 @@ curl \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/reauthenticate" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testRefreshSession.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testRefreshSession.1.txt index 69f5f61c..383a9e5a 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testRefreshSession.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testRefreshSession.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"refresh_token\":\"refresh-token\"}" \ "http://localhost:54321/auth/v1/token?grant_type=refresh_token" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testResendEmail.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testResendEmail.1.txt index 8f195886..5ece3cce 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testResendEmail.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testResendEmail.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"type\":\"email_change\"}" \ "http://localhost:54321/auth/v1/resend?redirect_to=https://supabase.com" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testResendPhone.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testResendPhone.1.txt index 16f203b3..5e2c9c47 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testResendPhone.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testResendPhone.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"phone\":\"+1 202-918-2132\",\"type\":\"phone_change\"}" \ "http://localhost:54321/auth/v1/resend" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testResetPasswordForEmail.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testResetPasswordForEmail.1.txt index c77f72d0..6d9ae8a6 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testResetPasswordForEmail.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testResetPasswordForEmail.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"}}" \ "http://localhost:54321/auth/v1/recover?redirect_to=https://supabase.com" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSessionFromURL.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSessionFromURL.1.txt index ca2aed24..a38ebb8a 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSessionFromURL.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSessionFromURL.1.txt @@ -1,6 +1,6 @@ curl \ - --header "Apikey: dummy.api.key" \ --header "Authorization: bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/user" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAExpiredToken.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAExpiredToken.1.txt index 70612d26..81ad7be9 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAExpiredToken.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAExpiredToken.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"refresh_token\":\"dummy-refresh-token\"}" \ "http://localhost:54321/auth/v1/token?grant_type=refresh_token" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAFutureExpirationDate.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAFutureExpirationDate.1.txt index d8d24073..0532048c 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAFutureExpirationDate.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSetSessionWithAFutureExpirationDate.1.txt @@ -1,6 +1,6 @@ curl \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjo0ODUyMTYzNTkzLCJzdWIiOiJmMzNkM2VjOS1hMmVlLTQ3YzQtODBlMS01YmQ5MTlmM2Q4YjgiLCJlbWFpbCI6ImhpQGJpbmFyeXNjcmFwaW5nLmNvIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIn0.UiEhoahP9GNrBKw_OHBWyqYudtoIlZGkrjs7Qa8hU7I" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/user" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInAnonymously.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInAnonymously.1.txt index 5fa3cf34..782dea84 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInAnonymously.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInAnonymously.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"data\":{\"custom_key\":\"custom_value\"},\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"}}" \ "http://localhost:54321/auth/v1/signup" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithEmailAndPassword.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithEmailAndPassword.1.txt index df2dfbbf..41ba1e1c 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithEmailAndPassword.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithEmailAndPassword.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"},\"password\":\"the.pass\"}" \ "http://localhost:54321/auth/v1/token?grant_type=password" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithIdToken.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithIdToken.1.txt index 37477c44..af1c88b7 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithIdToken.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithIdToken.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"access_token\":\"access-token\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"id_token\":\"id-token\",\"nonce\":\"nonce\",\"provider\":\"apple\"}" \ "http://localhost:54321/auth/v1/token?grant_type=id_token" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingEmail.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingEmail.1.txt index 19c1ceba..727004e6 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingEmail.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingEmail.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"create_user\":true,\"data\":{\"custom_key\":\"custom_value\"},\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"}}" \ "http://localhost:54321/auth/v1/otp?redirect_to=https://supabase.com" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingPhone.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingPhone.1.txt index 512cbccc..b34950d9 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingPhone.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithOTPUsingPhone.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"channel\":\"sms\",\"create_user\":true,\"data\":{\"custom_key\":\"custom_value\"},\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"},\"phone\":\"+1 202-918-2132\"}" \ "http://localhost:54321/auth/v1/otp" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithPhoneAndPassword.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithPhoneAndPassword.1.txt index c29e1e69..cb7a24ff 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithPhoneAndPassword.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithPhoneAndPassword.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"},\"password\":\"the.pass\",\"phone\":\"+1 202-918-2132\"}" \ "http://localhost:54321/auth/v1/token?grant_type=password" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingDomain.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingDomain.1.txt index 1726c3ce..711b550d 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingDomain.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingDomain.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"domain\":\"supabase.com\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"redirect_to\":\"https:\/\/supabase.com\"}" \ "http://localhost:54321/auth/v1/sso" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingProviderId.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingProviderId.1.txt index 8674ed09..5b16b3bf 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingProviderId.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignInWithSSOUsingProviderId.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"provider_id\":\"E621E1F8-C36C-495A-93FC-0C247A3E6E5F\",\"redirect_to\":\"https:\/\/supabase.com\"}" \ "http://localhost:54321/auth/v1/sso" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOut.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOut.1.txt index 1868f0c5..69649604 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOut.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOut.1.txt @@ -1,7 +1,7 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/logout?scope=global" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithLocalScope.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithLocalScope.1.txt index 151ef1ce..3cedf642 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithLocalScope.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithLocalScope.1.txt @@ -1,7 +1,7 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/logout?scope=local" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithOthersScope.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithOthersScope.1.txt index 44cf10c9..ad5d0597 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithOthersScope.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignOutWithOthersScope.1.txt @@ -1,7 +1,7 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/logout?scope=others" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithEmailAndPassword.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithEmailAndPassword.1.txt index 1f3cf59e..dd8ed048 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithEmailAndPassword.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithEmailAndPassword.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"data\":{\"custom_key\":\"custom_value\"},\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"},\"password\":\"the.pass\"}" \ "http://localhost:54321/auth/v1/signup?redirect_to=https://supabase.com" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithPhoneAndPassword.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithPhoneAndPassword.1.txt index 7bcbc227..d57109d2 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithPhoneAndPassword.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testSignUpWithPhoneAndPassword.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"data\":{\"custom_key\":\"custom_value\"},\"gotrue_meta_security\":{\"captcha_token\":\"dummy-captcha\"},\"password\":\"the.pass\",\"phone\":\"+1 202-918-2132\"}" \ "http://localhost:54321/auth/v1/signup" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testUnlinkIdentity.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testUnlinkIdentity.1.txt index cc81e321..cf6f4001 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testUnlinkIdentity.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testUnlinkIdentity.1.txt @@ -1,7 +1,7 @@ curl \ --request DELETE \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ "http://localhost:54321/auth/v1/user/identities/E621E1F8-C36C-495A-93FC-0C247A3E6E5F" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testUpdateUser.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testUpdateUser.1.txt index 45eaa5f0..5e61a0d9 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testUpdateUser.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testUpdateUser.1.txt @@ -1,9 +1,9 @@ curl \ --request PUT \ - --header "Apikey: dummy.api.key" \ --header "Authorization: Bearer accesstoken" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"data\":{\"custom_key\":\"custom_value\"},\"email\":\"example@mail.com\",\"email_change_token\":\"123456\",\"nonce\":\"abcdef\",\"password\":\"another.pass\",\"phone\":\"+1 202-918-2132\"}" \ "http://localhost:54321/auth/v1/user" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingEmail.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingEmail.1.txt index 885935d6..09aa49f4 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingEmail.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingEmail.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"email\":\"example@mail.com\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"token\":\"123456\",\"type\":\"magiclink\"}" \ "http://localhost:54321/auth/v1/verify?redirect_to=https://supabase.com" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingPhone.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingPhone.1.txt index 38304842..afaa5d9f 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingPhone.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingPhone.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"phone\":\"+1 202-918-2132\",\"token\":\"123456\",\"type\":\"sms\"}" \ "http://localhost:54321/auth/v1/verify" \ No newline at end of file diff --git a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingTokenHash.1.txt b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingTokenHash.1.txt index 2488d0c2..892d2f81 100644 --- a/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingTokenHash.1.txt +++ b/Tests/AuthTests/__Snapshots__/RequestsTests/testVerifyOTPUsingTokenHash.1.txt @@ -1,8 +1,8 @@ curl \ --request POST \ - --header "Apikey: dummy.api.key" \ --header "Content-Type: application/json" \ --header "X-Client-Info: gotrue-swift/x.y.z" \ --header "X-Supabase-Api-Version: 2024-01-01" \ + --header "apiKey: dummy.api.key" \ --data "{\"token_hash\":\"abc-def\",\"type\":\"email\"}" \ "http://localhost:54321/auth/v1/verify" \ No newline at end of file diff --git a/Tests/FunctionsTests/FunctionInvokeOptionsTests.swift b/Tests/FunctionsTests/FunctionInvokeOptionsTests.swift index 4a781007..ed9ffba5 100644 --- a/Tests/FunctionsTests/FunctionInvokeOptionsTests.swift +++ b/Tests/FunctionsTests/FunctionInvokeOptionsTests.swift @@ -28,8 +28,8 @@ final class FunctionInvokeOptionsTests: XCTestCase { let boundary = "Boundary-\(UUID().uuidString)" let contentType = "multipart/form-data; boundary=\(boundary)" let options = FunctionInvokeOptions( - headers: ["Content-Type": contentType], - body: "binary value".data(using: .utf8)! + headers: [.contentType: contentType], + body: Data("binary value".utf8) ) XCTAssertEqual(options.headers[.contentType], contentType) XCTAssertNotNil(options.body) diff --git a/Tests/FunctionsTests/FunctionsClientTests.swift b/Tests/FunctionsTests/FunctionsClientTests.swift index e4972dce..38b29745 100644 --- a/Tests/FunctionsTests/FunctionsClientTests.swift +++ b/Tests/FunctionsTests/FunctionsClientTests.swift @@ -1,10 +1,11 @@ import ConcurrencyExtras -@testable import Functions -import Helpers import HTTPTypes +import Helpers import TestHelpers import XCTest +@testable import Functions + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -13,32 +14,32 @@ final class FunctionsClientTests: XCTestCase { let url = URL(string: "http://localhost:5432/functions/v1")! let apiKey = "supabase.anon.key" - lazy var sut = FunctionsClient(url: url, headers: ["Apikey": apiKey]) + lazy var sut = FunctionsClient(url: url, headers: [.apiKey: apiKey]) func testInit() async { let client = FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: .saEast1 ) XCTAssertEqual(client.region, "sa-east-1") - XCTAssertEqual(client.headers[.init("Apikey")!], apiKey) - XCTAssertNotNil(client.headers[.init("X-Client-Info")!]) + XCTAssertEqual(client.headers[.apiKey], apiKey) + XCTAssertNotNil(client.headers[.xClientInfo]) } func testInvoke() async throws { let url = URL(string: "http://localhost:5432/functions/v1/hello_world")! let http = await HTTPClientMock() - .when { - $0.url.pathComponents.contains("hello_world") - } return: { _ in - try .stub(body: Empty()) + .when { request, bodyData in + return request.url!.pathComponents.contains("hello_world") + } return: { _, _ in + try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: self.url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, http: http ) @@ -47,24 +48,24 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke( "hello_world", - options: .init(headers: ["X-Custom-Key": "value"], body: body) + options: .init(headers: [.init("X-Custom-Key")!: "value"], body: body) ) let request = await http.receivedRequests.last - XCTAssertEqual(request?.url, url) - XCTAssertEqual(request?.method, .post) - XCTAssertEqual(request?.headers[.init("Apikey")!], apiKey) - XCTAssertEqual(request?.headers[.init("X-Custom-Key")!], "value") - XCTAssertEqual(request?.headers[.init("X-Client-Info")!], "functions-swift/\(Functions.version)") + XCTAssertEqual(request?.0.url, url) + XCTAssertEqual(request?.0.method, .post) + XCTAssertEqual(request?.0.headerFields[.apiKey], apiKey) + XCTAssertEqual(request?.0.headerFields[.init("X-Custom-Key")!], "value") + XCTAssertEqual(request?.0.headerFields[.xClientInfo], "functions-swift/\(Functions.version)") } func testInvokeWithCustomMethod() async throws { - let http = await HTTPClientMock().any { _ in try .stub(body: Empty()) } + let http = await HTTPClientMock().any { _, _ in try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, http: http ) @@ -72,15 +73,15 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke("hello-world", options: .init(method: .delete)) let request = await http.receivedRequests.last - XCTAssertEqual(request?.method, .delete) + XCTAssertEqual(request?.0.method, .delete) } func testInvokeWithQuery() async throws { - let http = await HTTPClientMock().any { _ in try .stub(body: Empty()) } + let http = await HTTPClientMock().any { _, _ in try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, http: http ) @@ -93,12 +94,12 @@ final class FunctionsClientTests: XCTestCase { ) let request = await http.receivedRequests.last - XCTAssertEqual(request?.urlRequest.url?.query, "key=value") + XCTAssertEqual(request?.0.url?.query, "key=value") } func testInvokeWithRegionDefinedInClient() async throws { let http = await HTTPClientMock() - .any { _ in try .stub(body: Empty()) } + .any { _, _ in try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: url, @@ -110,12 +111,12 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke("hello-world") let request = await http.receivedRequests.last - XCTAssertEqual(request?.headers[.xRegion], "ca-central-1") + XCTAssertEqual(request?.0.headerFields[.xRegion], "ca-central-1") } func testInvokeWithRegion() async throws { let http = await HTTPClientMock() - .any { _ in try .stub(body: Empty()) } + .any { _, _ in try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: url, @@ -127,12 +128,12 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke("hello-world", options: .init(region: .caCentral1)) let request = await http.receivedRequests.last - XCTAssertEqual(request?.headers[.xRegion], "ca-central-1") + XCTAssertEqual(request?.0.headerFields[.xRegion], "ca-central-1") } func testInvokeWithoutRegion() async throws { let http = await HTTPClientMock() - .any { _ in try .stub(body: Empty()) } + .any { _, _ in try TestStub.stub(body: Empty()) } let sut = FunctionsClient( url: url, @@ -144,16 +145,16 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke("hello-world") let request = await http.receivedRequests.last - XCTAssertNil(request?.headers[.xRegion]) + XCTAssertNil(request?.0.headerFields[.xRegion]) } func testInvoke_shouldThrow_URLError_badServerResponse() async { let sut = await FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, http: HTTPClientMock() - .any { _ in throw URLError(.badServerResponse) } + .any { _, _ in throw URLError(.badServerResponse) } ) do { @@ -168,10 +169,10 @@ final class FunctionsClientTests: XCTestCase { func testInvoke_shouldThrow_FunctionsError_httpError() async { let sut = await FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, http: HTTPClientMock() - .any { _ in try .stub(body: Empty(), statusCode: 300) } + .any { _, _ in try TestStub.stub(body: Empty(), statusCode: 300) } ) do { try await sut.invoke("hello_world") @@ -186,10 +187,10 @@ final class FunctionsClientTests: XCTestCase { func testInvoke_shouldThrow_FunctionsError_relayError() async { let sut = await FunctionsClient( url: url, - headers: ["Apikey": apiKey], + headers: [.apiKey: apiKey], region: nil, - http: HTTPClientMock().any { _ in - try .stub( + http: HTTPClientMock().any { _, _ in + try TestStub.stub( body: Empty(), headers: [.xRelayError: "true"] ) @@ -211,23 +212,18 @@ final class FunctionsClientTests: XCTestCase { } } -extension Helpers.HTTPResponse { +struct TestStub { static func stub( body: any Encodable, statusCode: Int = 200, headers: HTTPFields = .init() - ) throws -> Helpers.HTTPResponse { + ) throws -> (Data, HTTPResponse) { let data = try JSONEncoder().encode(body) - let response = HTTPURLResponse( - url: URL(string: "http://127.0.0.1")!, - statusCode: statusCode, - httpVersion: nil, - headerFields: headers.dictionary - )! - return HTTPResponse( - data: data, - response: response + let response = HTTPResponse( + status: .init(code: statusCode), + headerFields: headers ) + return (data, response) } } diff --git a/Tests/FunctionsTests/RequestTests.swift b/Tests/FunctionsTests/RequestTests.swift index 30d8bab1..e786db47 100644 --- a/Tests/FunctionsTests/RequestTests.swift +++ b/Tests/FunctionsTests/RequestTests.swift @@ -5,10 +5,15 @@ // Created by Guilherme Souza on 23/04/24. // -@testable import Functions import SnapshotTesting import XCTest +@testable import Functions + +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + final class RequestTests: XCTestCase { let url = URL(string: "http://localhost:5432/functions/v1")! let apiKey = "supabase.anon.key" @@ -33,7 +38,10 @@ final class RequestTests: XCTestCase { func testInvokeWithCustomHeader() async { await snapshot { - try await $0.invoke("hello-world", options: .init(headers: ["x-custom-key": "custom value"])) + try await $0.invoke( + "hello-world", + options: .init(headers: [.init("x-custom-key")!: "custom value"]) + ) } } @@ -52,10 +60,19 @@ final class RequestTests: XCTestCase { ) async { let sut = FunctionsClient( url: url, - headers: ["apikey": apiKey, "x-client-info": "functions-swift/x.y.z"] - ) { request in + headers: [.apiKey: apiKey, .xClientInfo: "functions-swift/x.y.z"] + ) { request, bodyData in await MainActor.run { - assertSnapshot(of: request, as: .curl, record: record, file: file, testName: testName, line: line) + var request = URLRequest(httpRequest: request)! + request.httpBody = bodyData + assertSnapshot( + of: request, + as: .curl, + record: record, + file: file, + testName: testName, + line: line + ) } throw NSError(domain: "Error", code: 0, userInfo: nil) } diff --git a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithBody.1.txt b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithBody.1.txt index a8f7bbe3..e33899f3 100644 --- a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithBody.1.txt +++ b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithBody.1.txt @@ -1,7 +1,7 @@ curl \ --request POST \ --header "Content-Type: application/json" \ - --header "apikey: supabase.anon.key" \ - --header "x-client-info: functions-swift/x.y.z" \ + --header "X-Client-Info: functions-swift/x.y.z" \ + --header "apiKey: supabase.anon.key" \ --data "{\"name\":\"Supabase\"}" \ "http://localhost:5432/functions/v1/hello-world" \ No newline at end of file diff --git a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomHeader.1.txt b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomHeader.1.txt index 3efebb9b..f0cafce8 100644 --- a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomHeader.1.txt +++ b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomHeader.1.txt @@ -1,6 +1,6 @@ curl \ --request POST \ - --header "apikey: supabase.anon.key" \ - --header "x-client-info: functions-swift/x.y.z" \ + --header "X-Client-Info: functions-swift/x.y.z" \ + --header "apiKey: supabase.anon.key" \ --header "x-custom-key: custom value" \ "http://localhost:5432/functions/v1/hello-world" \ No newline at end of file diff --git a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomMethod.1.txt b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomMethod.1.txt index a4460b69..178c9291 100644 --- a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomMethod.1.txt +++ b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomMethod.1.txt @@ -1,5 +1,5 @@ curl \ --request PATCH \ - --header "apikey: supabase.anon.key" \ - --header "x-client-info: functions-swift/x.y.z" \ + --header "X-Client-Info: functions-swift/x.y.z" \ + --header "apiKey: supabase.anon.key" \ "http://localhost:5432/functions/v1/hello-world" \ No newline at end of file diff --git a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomRegion.1.txt b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomRegion.1.txt index b7ebf5c7..65edd1ba 100644 --- a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomRegion.1.txt +++ b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithCustomRegion.1.txt @@ -1,6 +1,6 @@ curl \ --request POST \ - --header "apikey: supabase.anon.key" \ - --header "x-client-info: functions-swift/x.y.z" \ + --header "X-Client-Info: functions-swift/x.y.z" \ + --header "apiKey: supabase.anon.key" \ --header "x-region: ap-northeast-1" \ "http://localhost:5432/functions/v1/hello-world" \ No newline at end of file diff --git a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithDefaultOptions.1.txt b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithDefaultOptions.1.txt index 053472bb..9e1d0834 100644 --- a/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithDefaultOptions.1.txt +++ b/Tests/FunctionsTests/__Snapshots__/RequestTests/testInvokeWithDefaultOptions.1.txt @@ -1,5 +1,5 @@ curl \ --request POST \ - --header "apikey: supabase.anon.key" \ - --header "x-client-info: functions-swift/x.y.z" \ + --header "X-Client-Info: functions-swift/x.y.z" \ + --header "apiKey: supabase.anon.key" \ "http://localhost:5432/functions/v1/hello-world" \ No newline at end of file diff --git a/Tests/HelpersTests/ObservationTokenTests.swift b/Tests/HelpersTests/ObservationTokenTests.swift index 16eec655..6deb021e 100644 --- a/Tests/HelpersTests/ObservationTokenTests.swift +++ b/Tests/HelpersTests/ObservationTokenTests.swift @@ -7,9 +7,10 @@ import ConcurrencyExtras import Foundation -@testable import Helpers import XCTest +@testable import Helpers + final class ObservationTokenTests: XCTestCase { func testRemove() { let handle = ObservationToken() diff --git a/Tests/IntegrationTests/AuthClientIntegrationTests.swift b/Tests/IntegrationTests/AuthClientIntegrationTests.swift index d72637c8..fd64b382 100644 --- a/Tests/IntegrationTests/AuthClientIntegrationTests.swift +++ b/Tests/IntegrationTests/AuthClientIntegrationTests.swift @@ -5,12 +5,13 @@ // Created by Guilherme Souza on 27/03/24. // -@testable import Auth import ConcurrencyExtras import CustomDump import TestHelpers import XCTest +@testable import Auth + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -24,8 +25,8 @@ final class AuthClientIntegrationTests: XCTestCase { configuration: AuthClient.Configuration( url: URL(string: "\(DotEnv.SUPABASE_URL)/auth/v1")!, headers: [ - "apikey": key, - "Authorization": "Bearer \(key)", + .apiKey: key, + .authorization: "Bearer \(key)", ], localStorage: InMemoryLocalStorage(), logger: nil diff --git a/Tests/IntegrationTests/PostgrestIntegrationTests.swift b/Tests/IntegrationTests/PostgrestIntegrationTests.swift index 6336fcfc..a8398ebf 100644 --- a/Tests/IntegrationTests/PostgrestIntegrationTests.swift +++ b/Tests/IntegrationTests/PostgrestIntegrationTests.swift @@ -38,7 +38,7 @@ final class IntegrationTests: XCTestCase { let client = PostgrestClient( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "Apikey": DotEnv.SUPABASE_ANON_KEY, + .apiKey: DotEnv.SUPABASE_ANON_KEY ], logger: nil ) diff --git a/Tests/IntegrationTests/Potsgrest/PostgresTransformsTests.swift b/Tests/IntegrationTests/Potsgrest/PostgresTransformsTests.swift index a0c5925e..99278637 100644 --- a/Tests/IntegrationTests/Potsgrest/PostgresTransformsTests.swift +++ b/Tests/IntegrationTests/Potsgrest/PostgresTransformsTests.swift @@ -14,7 +14,7 @@ final class PostgrestTransformsTests: XCTestCase { configuration: PostgrestClient.Configuration( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "apikey": DotEnv.SUPABASE_ANON_KEY + .apiKey: DotEnv.SUPABASE_ANON_KEY ], logger: nil ) diff --git a/Tests/IntegrationTests/Potsgrest/PostgrestBasicTests.swift b/Tests/IntegrationTests/Potsgrest/PostgrestBasicTests.swift index 9912b4d1..ee86afb9 100644 --- a/Tests/IntegrationTests/Potsgrest/PostgrestBasicTests.swift +++ b/Tests/IntegrationTests/Potsgrest/PostgrestBasicTests.swift @@ -14,7 +14,7 @@ final class PostgrestBasicTests: XCTestCase { configuration: PostgrestClient.Configuration( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "apikey": DotEnv.SUPABASE_ANON_KEY, + .apiKey: DotEnv.SUPABASE_ANON_KEY ], logger: nil ) diff --git a/Tests/IntegrationTests/Potsgrest/PostgrestFilterTests.swift b/Tests/IntegrationTests/Potsgrest/PostgrestFilterTests.swift index 9ec7e3b1..b6fa13be 100644 --- a/Tests/IntegrationTests/Potsgrest/PostgrestFilterTests.swift +++ b/Tests/IntegrationTests/Potsgrest/PostgrestFilterTests.swift @@ -14,7 +14,7 @@ final class PostgrestFilterTests: XCTestCase { configuration: PostgrestClient.Configuration( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "apikey": DotEnv.SUPABASE_ANON_KEY, + .apiKey: DotEnv.SUPABASE_ANON_KEY ], logger: nil ) diff --git a/Tests/IntegrationTests/Potsgrest/PostgrestResourceEmbeddingTests.swift b/Tests/IntegrationTests/Potsgrest/PostgrestResourceEmbeddingTests.swift index c4acfbda..3f9d7af1 100644 --- a/Tests/IntegrationTests/Potsgrest/PostgrestResourceEmbeddingTests.swift +++ b/Tests/IntegrationTests/Potsgrest/PostgrestResourceEmbeddingTests.swift @@ -14,7 +14,7 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { configuration: PostgrestClient.Configuration( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "apikey": DotEnv.SUPABASE_ANON_KEY, + .apiKey: DotEnv.SUPABASE_ANON_KEY ], logger: nil ) diff --git a/Tests/IntegrationTests/RealtimeIntegrationTests.swift b/Tests/IntegrationTests/RealtimeIntegrationTests.swift index 4b2b543a..000640ca 100644 --- a/Tests/IntegrationTests/RealtimeIntegrationTests.swift +++ b/Tests/IntegrationTests/RealtimeIntegrationTests.swift @@ -8,23 +8,24 @@ import ConcurrencyExtras import CustomDump import PostgREST -@testable import Realtime import Supabase import TestHelpers import XCTest +@testable import Realtime + final class RealtimeIntegrationTests: XCTestCase { let realtime = RealtimeClientV2( url: URL(string: "\(DotEnv.SUPABASE_URL)/realtime/v1")!, options: RealtimeClientOptions( - headers: ["apikey": DotEnv.SUPABASE_ANON_KEY] + headers: [.apiKey: DotEnv.SUPABASE_ANON_KEY] ) ) let db = PostgrestClient( url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1")!, headers: [ - "apikey": DotEnv.SUPABASE_ANON_KEY, + .apiKey: DotEnv.SUPABASE_ANON_KEY ] ) @@ -73,14 +74,14 @@ final class RealtimeIntegrationTests: XCTestCase { [ "event": "test", "payload": [ - "value": 1, + "value": 1 ], "type": "broadcast", ], [ "event": "test", "payload": [ - "value": 2, + "value": 2 ], "type": "broadcast", ], @@ -151,7 +152,7 @@ final class RealtimeIntegrationTests: XCTestCase { expectNoDifference( joins, [ - [], // This is the first PRESENCE_STATE event. + [], // This is the first PRESENCE_STATE event. [UserState(email: "test@supabase.com")], [UserState(email: "test2@supabase.com")], [], @@ -161,7 +162,7 @@ final class RealtimeIntegrationTests: XCTestCase { expectNoDifference( leaves, [ - [], // This is the first PRESENCE_STATE event. + [], // This is the first PRESENCE_STATE event. [], [UserState(email: "test@supabase.com")], [UserState(email: "test2@supabase.com")], diff --git a/Tests/IntegrationTests/StorageClientIntegrationTests.swift b/Tests/IntegrationTests/StorageClientIntegrationTests.swift index 2d073eab..df32ed7f 100644 --- a/Tests/IntegrationTests/StorageClientIntegrationTests.swift +++ b/Tests/IntegrationTests/StorageClientIntegrationTests.swift @@ -14,7 +14,7 @@ final class StorageClientIntegrationTests: XCTestCase { configuration: StorageClientConfiguration( url: URL(string: "\(DotEnv.SUPABASE_URL)/storage/v1")!, headers: [ - "Authorization": "Bearer \(DotEnv.SUPABASE_SERVICE_ROLE_KEY)", + .authorization: "Bearer \(DotEnv.SUPABASE_SERVICE_ROLE_KEY)" ], logger: nil ) @@ -36,7 +36,10 @@ final class StorageClientIntegrationTests: XCTestCase { buckets = try await storage.listBuckets() XCTAssertTrue(buckets.contains { $0.id == bucket.id }) - try await storage.updateBucket(bucketName, options: BucketOptions(allowedMimeTypes: ["image/jpeg"])) + try await storage.updateBucket( + bucketName, + options: BucketOptions(allowedMimeTypes: ["image/jpeg"]) + ) bucket = try await storage.getBucket(bucketName) XCTAssertEqual(bucket.allowedMimeTypes, ["image/jpeg"]) diff --git a/Tests/IntegrationTests/StorageFileIntegrationTests.swift b/Tests/IntegrationTests/StorageFileIntegrationTests.swift index 188090a9..02f5a912 100644 --- a/Tests/IntegrationTests/StorageFileIntegrationTests.swift +++ b/Tests/IntegrationTests/StorageFileIntegrationTests.swift @@ -5,10 +5,11 @@ // Created by Guilherme Souza on 07/05/24. // +import HTTPTypes +import Helpers import InlineSnapshotTesting import Storage import XCTest -import Helpers #if canImport(FoundationNetworking) import FoundationNetworking @@ -19,7 +20,7 @@ final class StorageFileIntegrationTests: XCTestCase { configuration: StorageClientConfiguration( url: URL(string: "\(DotEnv.SUPABASE_URL)/storage/v1")!, headers: [ - "Authorization": "Bearer \(DotEnv.SUPABASE_SERVICE_ROLE_KEY)" + .authorization: "Bearer \(DotEnv.SUPABASE_SERVICE_ROLE_KEY)" ], logger: nil ) @@ -378,9 +379,13 @@ final class StorageFileIntegrationTests: XCTestCase { let publicURL = try storage.from(bucketName).getPublicURL(path: uploadPath) - let (_, response) = try await URLSession.shared.data(from: publicURL) - let httpResponse = try XCTUnwrap(response as? HTTPURLResponse) - let cacheControl = try XCTUnwrap(httpResponse.value(forHTTPHeaderField: "cache-control")) + let request = HTTPRequest( + method: .get, + url: publicURL + ) + + let (_, response) = try await URLSession.shared.data(for: request) + let cacheControl = try XCTUnwrap(response.headerFields[.cacheControl]) XCTAssertEqual(cacheControl, "public, max-age=14400") } diff --git a/Tests/PostgRESTTests/BuildURLRequestTests.swift b/Tests/PostgRESTTests/BuildURLRequestTests.swift index c4ba68d1..f909f4b5 100644 --- a/Tests/PostgRESTTests/BuildURLRequestTests.swift +++ b/Tests/PostgRESTTests/BuildURLRequestTests.swift @@ -1,5 +1,6 @@ import ConcurrencyExtras import Foundation +import HTTPTypes import Helpers import SnapshotTesting import XCTest @@ -49,15 +50,18 @@ final class BuildURLRequestTests: XCTestCase { let client = PostgrestClient( url: url, schema: nil, - headers: ["X-Client-Info": "postgrest-swift/x.y.z"], + headers: [.xClientInfo: "postgrest-swift/x.y.z"], logger: nil, - fetch: { request in + fetch: { request, bodyData in guard let runningTestCase = await runningTestCase.value else { XCTFail("execute called without a runningTestCase set.") - return (Data(), URLResponse.empty()) + return (Data(), HTTPResponse(status: .ok)) } await MainActor.run { [runningTestCase] in + var request = URLRequest(httpRequest: request)! + request.httpBody = bodyData + assertSnapshot( of: request, as: .curl, @@ -69,7 +73,7 @@ final class BuildURLRequestTests: XCTestCase { ) } - return (Data(), URLResponse.empty()) + return (Data(), HTTPResponse(status: .ok)) }, encoder: encoder ) @@ -251,26 +255,7 @@ final class BuildURLRequestTests: XCTestCase { func testSessionConfiguration() { let client = PostgrestClient(url: url, schema: nil, logger: nil) - let clientInfoHeader = client.configuration.headers["X-Client-Info"] + let clientInfoHeader = client.configuration.headers[.xClientInfo] XCTAssertNotNil(clientInfoHeader) } } - -extension URLResponse { - // Windows and Linux don't have the ability to empty initialize a URLResponse like `URLResponse()` - // so - // We provide a function that can give us the right value on an platform. - // See https://github.com/apple/swift-corelibs-foundation/pull/4778 - fileprivate static func empty() -> URLResponse { - #if os(Windows) || os(Linux) - URLResponse( - url: .init(string: "https://supabase.com")!, - mimeType: nil, - expectedContentLength: 0, - textEncodingName: nil - ) - #else - URLResponse() - #endif - } -} diff --git a/Tests/PostgRESTTests/JSONTests.swift b/Tests/PostgRESTTests/JSONTests.swift index dabf2d97..5b339933 100644 --- a/Tests/PostgRESTTests/JSONTests.swift +++ b/Tests/PostgRESTTests/JSONTests.swift @@ -5,16 +5,17 @@ // Created by Guilherme Souza on 01/07/24. // -@testable import PostgREST import XCTest +@testable import PostgREST + final class JSONTests: XCTestCase { func testDecodeJSON() throws { let json = """ - { - "created_at": "2024-06-15T18:12:04+00:00" - } - """.data(using: .utf8)! + { + "created_at": "2024-06-15T18:12:04+00:00" + } + """.data(using: .utf8)! struct Value: Decodable { var createdAt: Date diff --git a/Tests/PostgRESTTests/PostgrestBuilderTests.swift b/Tests/PostgRESTTests/PostgrestBuilderTests.swift index 1fa049a4..41466c28 100644 --- a/Tests/PostgRESTTests/PostgrestBuilderTests.swift +++ b/Tests/PostgRESTTests/PostgrestBuilderTests.swift @@ -5,19 +5,23 @@ // Created by Guilherme Souza on 20/08/24. // -@testable import PostgREST import XCTest +@testable import PostgREST + final class PostgrestBuilderTests: XCTestCase { let url = URL(string: "http://localhost:54321/rest/v1")! func testCustomHeaderOnAPerCallBasis() throws { - let postgrest1 = PostgrestClient(url: url, headers: ["apikey": "foo"], logger: nil) - let postgrest2 = try postgrest1.rpc("void_func").setHeader(name: .init("apikey")!, value: "bar") + let postgrest1 = PostgrestClient(url: url, headers: [.apiKey: "foo"], logger: nil) + let postgrest2 = try postgrest1.rpc("void_func").setHeader(name: .apiKey, value: "bar") // Original client object isn't affected - XCTAssertEqual(postgrest1.from("users").select().mutableState.request.headers[.init("apikey")!], "foo") + XCTAssertEqual( + postgrest1.from("users").select().mutableState.request.headerFields[.apiKey], + "foo" + ) // Derived client object uses new header value - XCTAssertEqual(postgrest2.mutableState.request.headers[.init("apikey")!], "bar") + XCTAssertEqual(postgrest2.mutableState.request.headerFields[.apiKey], "bar") } } diff --git a/Tests/PostgRESTTests/PostgrestResponseTests.swift b/Tests/PostgRESTTests/PostgrestResponseTests.swift index 09637818..9c65311e 100644 --- a/Tests/PostgRESTTests/PostgrestResponseTests.swift +++ b/Tests/PostgRESTTests/PostgrestResponseTests.swift @@ -1,3 +1,4 @@ +import HTTPTypes import XCTest @testable import PostgREST @@ -10,12 +11,10 @@ class PostgrestResponseTests: XCTestCase { func testInit() { // Prepare data and response let data = Data() - let response = HTTPURLResponse( - url: URL(string: "http://example.com")!, - statusCode: 200, - httpVersion: nil, - headerFields: ["Content-Range": "bytes 0-100/200"] - )! + let response = HTTPResponse( + status: .init(code: 200), + headerFields: [.contentRange: "bytes 0-100/200"] + ) let value = "Test Value" // Create the PostgrestResponse instance @@ -32,12 +31,10 @@ class PostgrestResponseTests: XCTestCase { func testInitWithNoCount() { // Prepare data and response let data = Data() - let response = HTTPURLResponse( - url: URL(string: "http://example.com")!, - statusCode: 200, - httpVersion: nil, - headerFields: ["Content-Range": "*"] - )! + let response = HTTPResponse( + status: .init(code: 200), + headerFields: [.contentRange: "*"] + ) let value = "Test Value" // Create the PostgrestResponse instance diff --git a/Tests/RealtimeTests/MockWebSocketClient.swift b/Tests/RealtimeTests/MockWebSocketClient.swift index bcabc958..5580b122 100644 --- a/Tests/RealtimeTests/MockWebSocketClient.swift +++ b/Tests/RealtimeTests/MockWebSocketClient.swift @@ -7,9 +7,10 @@ import ConcurrencyExtras import Foundation -@testable import Realtime import XCTestDynamicOverlay +@testable import Realtime + #if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Tests/RealtimeTests/PostgresJoinConfigTests.swift b/Tests/RealtimeTests/PostgresJoinConfigTests.swift index bb695d18..64db6c56 100644 --- a/Tests/RealtimeTests/PostgresJoinConfigTests.swift +++ b/Tests/RealtimeTests/PostgresJoinConfigTests.swift @@ -5,9 +5,10 @@ // Created by Guilherme Souza on 26/12/23. // -@testable import Realtime import XCTest +@testable import Realtime + final class PostgresJoinConfigTests: XCTestCase { func testSameConfigButDifferentIdAreEqual() { let config1 = PostgresJoinConfig( diff --git a/Tests/RealtimeTests/RealtimeTests.swift b/Tests/RealtimeTests/RealtimeTests.swift index 35a318cc..b7956208 100644 --- a/Tests/RealtimeTests/RealtimeTests.swift +++ b/Tests/RealtimeTests/RealtimeTests.swift @@ -1,5 +1,6 @@ import ConcurrencyExtras import CustomDump +import HTTPTypes import Helpers import InlineSnapshotTesting import TestHelpers @@ -33,7 +34,7 @@ final class RealtimeTests: XCTestCase { sut = RealtimeClientV2( url: url, options: RealtimeClientOptions( - headers: ["apikey": apiKey], + headers: [.apiKey: apiKey], heartbeatInterval: 1, reconnectDelay: 1, timeoutInterval: 2 @@ -298,17 +299,12 @@ final class RealtimeTests: XCTestCase { } func testBroadcastWithHTTP() async throws { - await http.when { - $0.url.path.hasSuffix("broadcast") - } return: { _ in - HTTPResponse( - data: "{}".data(using: .utf8)!, - response: HTTPURLResponse( - url: self.sut.broadcastURL, - statusCode: 200, - httpVersion: nil, - headerFields: nil - )! + await http.when { request, bodyData in + request.url!.path.hasSuffix("broadcast") + } return: { _, _ in + ( + Data("{}".utf8), + HTTPResponse(status: .init(code: 200)) ) } @@ -319,7 +315,10 @@ final class RealtimeTests: XCTestCase { try await channel.broadcast(event: "test", message: ["value": 42]) let request = await http.receivedRequests.last - assertInlineSnapshot(of: request?.urlRequest, as: .raw(pretty: true)) { + var urlRequest = request.map { URLRequest(httpRequest: $0.0) } + urlRequest??.httpBody = request?.1 + + assertInlineSnapshot(of: urlRequest as? URLRequest, as: .raw(pretty: true)) { """ POST https://localhost:54321/realtime/v1/api/broadcast Authorization: Bearer anon.api.key diff --git a/Tests/RealtimeTests/_PushTests.swift b/Tests/RealtimeTests/_PushTests.swift index 67efc7a1..871c0bed 100644 --- a/Tests/RealtimeTests/_PushTests.swift +++ b/Tests/RealtimeTests/_PushTests.swift @@ -6,10 +6,11 @@ // import ConcurrencyExtras -@testable import Realtime import TestHelpers import XCTest +@testable import Realtime + final class _PushTests: XCTestCase { var ws: MockWebSocketClient! var socket: RealtimeClientV2! @@ -27,7 +28,7 @@ final class _PushTests: XCTestCase { socket = RealtimeClientV2( url: URL(string: "https://localhost:54321/v1/realtime")!, options: RealtimeClientOptions( - headers: ["apiKey": "apikey"] + headers: [.apiKey: "apikey"] ), ws: ws, http: HTTPClientMock() diff --git a/Tests/StorageTests/SupabaseStorageClient+Test.swift b/Tests/StorageTests/SupabaseStorageClient+Test.swift index ac10137f..bb9dace9 100644 --- a/Tests/StorageTests/SupabaseStorageClient+Test.swift +++ b/Tests/StorageTests/SupabaseStorageClient+Test.swift @@ -18,9 +18,9 @@ extension SupabaseStorageClient { configuration: StorageClientConfiguration( url: URL(string: supabaseURL)!, headers: [ - "Authorization": "Bearer \(apiKey)", - "Apikey": apiKey, - "X-Client-Info": "storage-swift/x.y.z", + .authorization: "Bearer \(apiKey)", + .apiKey: apiKey, + .xClientInfo: "storage-swift/x.y.z", ], session: session, logger: nil diff --git a/Tests/StorageTests/SupabaseStorageTests.swift b/Tests/StorageTests/SupabaseStorageTests.swift index 5742538d..e05dd777 100644 --- a/Tests/StorageTests/SupabaseStorageTests.swift +++ b/Tests/StorageTests/SupabaseStorageTests.swift @@ -1,5 +1,6 @@ import CustomDump import Foundation +import HTTPTypes import InlineSnapshotTesting import XCTest import XCTestDynamicOverlay @@ -15,8 +16,7 @@ final class SupabaseStorageTests: XCTestCase { let bucketId = "tests" var sessionMock = StorageHTTPSession( - fetch: unimplemented("StorageHTTPSession.fetch"), - upload: unimplemented("StorageHTTPSession.upload") + fetch: unimplemented("StorageHTTPSession.fetch") ) func testGetPublicURL() throws { @@ -58,24 +58,20 @@ final class SupabaseStorageTests: XCTestCase { } func testCreateSignedURLs() async throws { - sessionMock.fetch = { _ in + sessionMock.fetch = { _, _ in ( - """ - [ - { - "signedURL": "/sign/file1.txt?token=abc.def.ghi" - }, - { - "signedURL": "/sign/file2.txt?token=abc.def.ghi" - }, - ] - """.data(using: .utf8)!, - HTTPURLResponse( - url: self.supabaseURL, - statusCode: 200, - httpVersion: nil, - headerFields: nil - )! + Data( + """ + [ + { + "signedURL": "/sign/file1.txt?token=abc.def.ghi" + }, + { + "signedURL": "/sign/file2.txt?token=abc.def.ghi" + }, + ] + """.utf8), + HTTPResponse(status: .init(code: 200)) ) } @@ -96,16 +92,19 @@ final class SupabaseStorageTests: XCTestCase { func testUploadData() async throws { testingBoundary.setValue("alamofire.boundary.c21f947c1c7b0c57") - sessionMock.fetch = { request in + sessionMock.fetch = { request, bodyData in + var request = URLRequest(httpRequest: request)! + request.httpBody = bodyData + assertInlineSnapshot(of: request, as: .curl) { #""" curl \ --request POST \ - --header "Apikey: test.api.key" \ --header "Authorization: Bearer test.api.key" \ --header "Cache-Control: max-age=14400" \ --header "Content-Type: multipart/form-data; boundary=alamofire.boundary.c21f947c1c7b0c57" \ --header "X-Client-Info: storage-swift/x.y.z" \ + --header "apiKey: test.api.key" \ --header "x-upsert: false" \ --data "--alamofire.boundary.c21f947c1c7b0c57\#r Content-Disposition: form-data; name=\"cacheControl\"\#r @@ -126,18 +125,16 @@ final class SupabaseStorageTests: XCTestCase { """# } return ( - """ - { - "Id": "tests/file1.txt", - "Key": "tests/file1.txt" - } - """.data(using: .utf8)!, - HTTPURLResponse( - url: self.supabaseURL, - statusCode: 200, - httpVersion: nil, - headerFields: nil - )! + Data( + """ + { + "Id": "tests/file1.txt", + "Key": "tests/file1.txt" + } + """.utf8), + HTTPResponse( + status: .init(code: 200) + ) ) } @@ -157,33 +154,31 @@ final class SupabaseStorageTests: XCTestCase { func testUploadFileURL() async throws { testingBoundary.setValue("alamofire.boundary.c21f947c1c7b0c57") - sessionMock.fetch = { request in + sessionMock.fetch = { request, bodyData in + var request = URLRequest(httpRequest: request)! + request.httpBody = bodyData assertInlineSnapshot(of: request, as: .curl) { #""" curl \ --request POST \ - --header "Apikey: test.api.key" \ --header "Authorization: Bearer test.api.key" \ --header "Cache-Control: max-age=3600" \ --header "Content-Type: multipart/form-data; boundary=alamofire.boundary.c21f947c1c7b0c57" \ --header "X-Client-Info: storage-swift/x.y.z" \ + --header "apiKey: test.api.key" \ --header "x-upsert: false" \ "http://localhost:54321/storage/v1/object/tests/sadcat.jpg" """# } return ( - """ - { - "Id": "tests/file1.txt", - "Key": "tests/file1.txt" - } - """.data(using: .utf8)!, - HTTPURLResponse( - url: self.supabaseURL, - statusCode: 200, - httpVersion: nil, - headerFields: nil - )! + Data( + """ + { + "Id": "tests/file1.txt", + "Key": "tests/file1.txt" + } + """.utf8), + HTTPResponse(status: .init(code: 200)) ) } diff --git a/Tests/SupabaseTests/SupabaseClientTests.swift b/Tests/SupabaseTests/SupabaseClientTests.swift index c487177d..0137cda2 100644 --- a/Tests/SupabaseTests/SupabaseClientTests.swift +++ b/Tests/SupabaseTests/SupabaseClientTests.swift @@ -1,10 +1,12 @@ -@testable import Auth import CustomDump -@testable import Functions +import HTTPTypes import IssueReporting +import XCTest + +@testable import Auth +@testable import Functions @testable import Realtime @testable import Supabase -import XCTest final class AuthLocalStorageMock: AuthLocalStorage { func store(key _: String, value _: Data) throws {} @@ -27,7 +29,7 @@ final class SupabaseClientTests: XCTestCase { let logger = Logger() let customSchema = "custom_schema" let localStorage = AuthLocalStorageMock() - let customHeaders = ["header_field": "header_value"] + let customHeaders: HTTPFields = [.init("header_field")!: "header_value"] let client = SupabaseClient( supabaseURL: URL(string: "https://project-ref.supabase.co")!, @@ -47,7 +49,7 @@ final class SupabaseClientTests: XCTestCase { region: .apNortheast1 ), realtime: RealtimeClientOptions( - headers: ["custom_realtime_header_key": "custom_realtime_header_value"] + headers: [.init("custom_realtime_header_key")!: "custom_realtime_header_value"] ) ) ) @@ -62,15 +64,14 @@ final class SupabaseClientTests: XCTestCase { ) XCTAssertEqual( - client.headers, + client.headers, [ - "X-Client-Info": "supabase-swift/\(Supabase.version)", - "Apikey": "ANON_KEY", - "header_field": "header_value", - "Authorization": "Bearer ANON_KEY", + .xClientInfo: "supabase-swift/\(Supabase.version)", + .apiKey: "ANON_KEY", + .init("header_field")!: "header_value", + .authorization: "Bearer ANON_KEY", ] ) - expectNoDifference(client._headers.dictionary, client.headers) XCTAssertEqual(client.functions.region, "ap-northeast-1") @@ -78,9 +79,10 @@ final class SupabaseClientTests: XCTestCase { XCTAssertEqual(realtimeURL.absoluteString, "https://project-ref.supabase.co/realtime/v1") let realtimeOptions = client.realtimeV2.options - let expectedRealtimeHeader = client._headers.merging(with: [ - .init("custom_realtime_header_key")!: "custom_realtime_header_value"] - ) + let expectedRealtimeHeader = client.headers.merging([ + .init("custom_realtime_header_key")!: "custom_realtime_header_value" + ]) { $1 } + expectNoDifference(realtimeOptions.headers, expectedRealtimeHeader) XCTAssertIdentical(realtimeOptions.logger as? Logger, logger)