From cd804961400260c741a2023f32386bbfa07c3d39 Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Fri, 13 Dec 2024 21:46:58 +0000 Subject: [PATCH 1/5] Confirm RuntimeErrors to HTTPResponseConvertible --- .../OpenAPIRuntime/Errors/RuntimeError.swift | 42 +++++++- .../Errors/Test_RuntimeError.swift | 98 +++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift diff --git a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift index 549e3a13..e2ed3f21 100644 --- a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift +++ b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import protocol Foundation.LocalizedError import struct Foundation.Data - +import HTTPTypes /// Error thrown by generated code. internal enum RuntimeError: Error, CustomStringConvertible, LocalizedError, PrettyStringConvertible { @@ -141,3 +141,43 @@ internal enum RuntimeError: Error, CustomStringConvertible, LocalizedError, Pret @_spi(Generated) public func throwUnexpectedResponseBody(expectedContent: String, body: any Sendable) throws -> Never { throw RuntimeError.unexpectedResponseBody(expectedContent: expectedContent, body: body) } + +/// HTTP Response status definition for ``RuntimeError``. +extension RuntimeError: HTTPResponseConvertible { + public var httpStatus: HTTPTypes.HTTPResponse.Status { + switch self { + case .invalidServerURL, + .invalidServerVariableValue: + .notFound + case .invalidExpectedContentType, + .missingCoderForCustomContentType, + .unexpectedContentTypeHeader: + .unsupportedMediaType + case .unexpectedAcceptHeader(_): + .notAcceptable + case .missingOrMalformedContentDispositionName: + .unprocessableContent + case .failedToDecodeStringConvertibleValue, + .invalidAcceptSubstring, + .invalidBase64String, + .invalidHeaderFieldName, + .malformedAcceptHeader, + .missingMultipartBoundaryContentTypeParameter, + .missingRequiredHeaderField, + .missingRequiredMultipartFormDataContentType, + .missingRequiredQueryParameter, + .missingRequiredPathParameter, + .missingRequiredRequestBody, + .pathUnset, + .unsupportedParameterStyle: + .badRequest + case .handlerFailed, + .middlewareFailed, + .missingRequiredResponseBody, + .transportFailed, + .unexpectedResponseStatus, + .unexpectedResponseBody: + .internalServerError + } + } +} diff --git a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift new file mode 100644 index 00000000..a3234369 --- /dev/null +++ b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import HTTPTypes +@_spi(Generated) @testable import OpenAPIRuntime +import XCTest + +struct MockRuntimeErrorHandler: Sendable { + var failWithError: (any Error)? = nil + func greet(_ input: String) async throws -> String { + if failWithError != nil { throw failWithError! } + guard input == "hello" else { throw TestError() } + return "bye" + } + + static let requestBody: HTTPBody = HTTPBody("hello") + static let responseBody: HTTPBody = HTTPBody("bye") +} + +final class Test_RuntimeError: XCTestCase { + func testRuntimeError_returnsCorrectStatusCode() async throws { + + let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: RuntimeError.invalidServerURL("Invalid server URL")), + middlewares: [ErrorHandlingMiddleware()]) + let response = try await server.handle( + request: .init(soar_path: "/", method: .post), + requestBody: MockHandler.requestBody, + metadata: .init(), + forOperation: "op", + using: { MockRuntimeErrorHandler.greet($0) }, + deserializer: { request, body, metadata in + let body = try XCTUnwrap(body) + return try await String(collecting: body, upTo: 10) + }, + serializer: { output, _ in fatalError() } + ) + XCTAssertEqual(response.0.status, .notFound) + } + + func testRuntimeError_withUnderlyingErrorNotConfirming_returns500() async throws { + + let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: RuntimeError.transportFailed(TestError())), + middlewares: [ErrorHandlingMiddleware()]) + let response = try await server.handle( + request: .init(soar_path: "/", method: .post), + requestBody: MockHandler.requestBody, + metadata: .init(), + forOperation: "op", + using: { MockRuntimeErrorHandler.greet($0) }, + deserializer: { request, body, metadata in + let body = try XCTUnwrap(body) + return try await String(collecting: body, upTo: 10) + }, + serializer: { output, _ in fatalError() } + ) + XCTAssertEqual(response.0.status, .internalServerError) + } + + func testRuntimeError_withUnderlyingErrorConfirming_returns500() async throws { + + let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: TestErrorConvertible.testError("Test Error")), + middlewares: [ErrorHandlingMiddleware()]) + let response = try await server.handle( + request: .init(soar_path: "/", method: .post), + requestBody: MockHandler.requestBody, + metadata: .init(), + forOperation: "op", + using: { MockRuntimeErrorHandler.greet($0) }, + deserializer: { request, body, metadata in + let body = try XCTUnwrap(body) + return try await String(collecting: body, upTo: 10) + }, + serializer: { output, _ in fatalError() } + ) + XCTAssertEqual(response.0.status, .badGateway) + } +} + +enum TestErrorConvertible: Error, HTTPResponseConvertible { + case testError(String) + + public var httpStatus: HTTPTypes.HTTPResponse.Status { + .badGateway + } +} + + From f440264e406ab86b8673892744b59dcb0d1cfeb8 Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Fri, 13 Dec 2024 21:46:58 +0000 Subject: [PATCH 2/5] Confirm RuntimeErrors to HTTPResponseConvertible --- .../Errors/Test_RuntimeError.swift | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift index a3234369..cf41627b 100644 --- a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift +++ b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift @@ -29,24 +29,6 @@ struct MockRuntimeErrorHandler: Sendable { } final class Test_RuntimeError: XCTestCase { - func testRuntimeError_returnsCorrectStatusCode() async throws { - - let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: RuntimeError.invalidServerURL("Invalid server URL")), - middlewares: [ErrorHandlingMiddleware()]) - let response = try await server.handle( - request: .init(soar_path: "/", method: .post), - requestBody: MockHandler.requestBody, - metadata: .init(), - forOperation: "op", - using: { MockRuntimeErrorHandler.greet($0) }, - deserializer: { request, body, metadata in - let body = try XCTUnwrap(body) - return try await String(collecting: body, upTo: 10) - }, - serializer: { output, _ in fatalError() } - ) - XCTAssertEqual(response.0.status, .notFound) - } func testRuntimeError_withUnderlyingErrorNotConfirming_returns500() async throws { @@ -67,7 +49,7 @@ final class Test_RuntimeError: XCTestCase { XCTAssertEqual(response.0.status, .internalServerError) } - func testRuntimeError_withUnderlyingErrorConfirming_returns500() async throws { + func testRuntimeError_withUnderlyingErrorConfirming_returnsCorrectStatusCode() async throws { let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: TestErrorConvertible.testError("Test Error")), middlewares: [ErrorHandlingMiddleware()]) @@ -90,6 +72,7 @@ final class Test_RuntimeError: XCTestCase { enum TestErrorConvertible: Error, HTTPResponseConvertible { case testError(String) + /// HTTP status code for error cases public var httpStatus: HTTPTypes.HTTPResponse.Status { .badGateway } From 52618bb31d5294d8fa570ad04a45afd38508f060 Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Wed, 18 Dec 2024 11:24:00 +0000 Subject: [PATCH 3/5] Review comments and swift-format changes --- .../OpenAPIRuntime/Errors/RuntimeError.swift | 45 ++++++------------- .../Errors/Test_RuntimeError.swift | 2 +- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift index e2ed3f21..7923dd05 100644 --- a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift +++ b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift @@ -142,41 +142,24 @@ internal enum RuntimeError: Error, CustomStringConvertible, LocalizedError, Pret throw RuntimeError.unexpectedResponseBody(expectedContent: expectedContent, body: body) } -/// HTTP Response status definition for ``RuntimeError``. +/// HTTP Response status definition for ``RuntimeError``. extension RuntimeError: HTTPResponseConvertible { + /// HTTP Status code corresponding to each error case public var httpStatus: HTTPTypes.HTTPResponse.Status { switch self { - case .invalidServerURL, - .invalidServerVariableValue: - .notFound - case .invalidExpectedContentType, - .missingCoderForCustomContentType, - .unexpectedContentTypeHeader: - .unsupportedMediaType - case .unexpectedAcceptHeader(_): - .notAcceptable - case .missingOrMalformedContentDispositionName: - .unprocessableContent - case .failedToDecodeStringConvertibleValue, - .invalidAcceptSubstring, - .invalidBase64String, - .invalidHeaderFieldName, - .malformedAcceptHeader, - .missingMultipartBoundaryContentTypeParameter, - .missingRequiredHeaderField, - .missingRequiredMultipartFormDataContentType, - .missingRequiredQueryParameter, - .missingRequiredPathParameter, - .missingRequiredRequestBody, - .pathUnset, - .unsupportedParameterStyle: + case .invalidServerURL, .invalidServerVariableValue: .notFound + case .invalidExpectedContentType, .unexpectedContentTypeHeader: .unsupportedMediaType + case .missingCoderForCustomContentType: .unprocessableContent + case .unexpectedAcceptHeader: .notAcceptable + case .failedToDecodeStringConvertibleValue, .invalidAcceptSubstring, .invalidBase64String, + .invalidHeaderFieldName, .malformedAcceptHeader, .missingMultipartBoundaryContentTypeParameter, + .missingOrMalformedContentDispositionName, .missingRequiredHeaderField, + .missingRequiredMultipartFormDataContentType, .missingRequiredQueryParameter, .missingRequiredPathParameter, + .missingRequiredRequestBody, .unsupportedParameterStyle: .badRequest - case .handlerFailed, - .middlewareFailed, - .missingRequiredResponseBody, - .transportFailed, - .unexpectedResponseStatus, - .unexpectedResponseBody: + case .pathUnset: .notFound + case .handlerFailed, .middlewareFailed, .missingRequiredResponseBody, .transportFailed, + .unexpectedResponseStatus, .unexpectedResponseBody: .internalServerError } } diff --git a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift index cf41627b..f606e3be 100644 --- a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift +++ b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift @@ -19,7 +19,7 @@ import XCTest struct MockRuntimeErrorHandler: Sendable { var failWithError: (any Error)? = nil func greet(_ input: String) async throws -> String { - if failWithError != nil { throw failWithError! } + if let failWithError { throw failWithError } guard input == "hello" else { throw TestError() } return "bye" } From f0a8282f793bd2f2d6dbbe41414ce057d2791284 Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Thu, 19 Dec 2024 11:30:12 +0000 Subject: [PATCH 4/5] Fix unit test formatting issues --- .../Errors/Test_RuntimeError.swift | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift index f606e3be..ea0cf73b 100644 --- a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift +++ b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift @@ -29,11 +29,11 @@ struct MockRuntimeErrorHandler: Sendable { } final class Test_RuntimeError: XCTestCase { - func testRuntimeError_withUnderlyingErrorNotConfirming_returns500() async throws { - - let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: RuntimeError.transportFailed(TestError())), - middlewares: [ErrorHandlingMiddleware()]) + let server = UniversalServer( + handler: MockRuntimeErrorHandler(failWithError: RuntimeError.transportFailed(TestError())), + middlewares: [ErrorHandlingMiddleware()] + ) let response = try await server.handle( request: .init(soar_path: "/", method: .post), requestBody: MockHandler.requestBody, @@ -50,9 +50,10 @@ final class Test_RuntimeError: XCTestCase { } func testRuntimeError_withUnderlyingErrorConfirming_returnsCorrectStatusCode() async throws { - - let server = UniversalServer(handler: MockRuntimeErrorHandler(failWithError: TestErrorConvertible.testError("Test Error")), - middlewares: [ErrorHandlingMiddleware()]) + let server = UniversalServer( + handler: MockRuntimeErrorHandler(failWithError: TestErrorConvertible.testError("Test Error")), + middlewares: [ErrorHandlingMiddleware()] + ) let response = try await server.handle( request: .init(soar_path: "/", method: .post), requestBody: MockHandler.requestBody, @@ -71,11 +72,6 @@ final class Test_RuntimeError: XCTestCase { enum TestErrorConvertible: Error, HTTPResponseConvertible { case testError(String) - /// HTTP status code for error cases - public var httpStatus: HTTPTypes.HTTPResponse.Status { - .badGateway - } + public var httpStatus: HTTPTypes.HTTPResponse.Status { .badGateway } } - - From 79bd32a3d9ce43aa5129f27fb38ac5cd913b048f Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Thu, 19 Dec 2024 22:46:25 +0000 Subject: [PATCH 5/5] Fix typos --- Sources/OpenAPIRuntime/Errors/RuntimeError.swift | 4 ++-- Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift index 7923dd05..6cc82d33 100644 --- a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift +++ b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift @@ -14,6 +14,7 @@ import protocol Foundation.LocalizedError import struct Foundation.Data import HTTPTypes + /// Error thrown by generated code. internal enum RuntimeError: Error, CustomStringConvertible, LocalizedError, PrettyStringConvertible { @@ -147,7 +148,7 @@ extension RuntimeError: HTTPResponseConvertible { /// HTTP Status code corresponding to each error case public var httpStatus: HTTPTypes.HTTPResponse.Status { switch self { - case .invalidServerURL, .invalidServerVariableValue: .notFound + case .invalidServerURL, .invalidServerVariableValue, .pathUnset: .notFound case .invalidExpectedContentType, .unexpectedContentTypeHeader: .unsupportedMediaType case .missingCoderForCustomContentType: .unprocessableContent case .unexpectedAcceptHeader: .notAcceptable @@ -157,7 +158,6 @@ extension RuntimeError: HTTPResponseConvertible { .missingRequiredMultipartFormDataContentType, .missingRequiredQueryParameter, .missingRequiredPathParameter, .missingRequiredRequestBody, .unsupportedParameterStyle: .badRequest - case .pathUnset: .notFound case .handlerFailed, .middlewareFailed, .missingRequiredResponseBody, .transportFailed, .unexpectedResponseStatus, .unexpectedResponseBody: .internalServerError diff --git a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift index ea0cf73b..341f1b5b 100644 --- a/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift +++ b/Tests/OpenAPIRuntimeTests/Errors/Test_RuntimeError.swift @@ -29,7 +29,7 @@ struct MockRuntimeErrorHandler: Sendable { } final class Test_RuntimeError: XCTestCase { - func testRuntimeError_withUnderlyingErrorNotConfirming_returns500() async throws { + func testRuntimeError_withUnderlyingErrorNotConforming_returns500() async throws { let server = UniversalServer( handler: MockRuntimeErrorHandler(failWithError: RuntimeError.transportFailed(TestError())), middlewares: [ErrorHandlingMiddleware()] @@ -49,7 +49,7 @@ final class Test_RuntimeError: XCTestCase { XCTAssertEqual(response.0.status, .internalServerError) } - func testRuntimeError_withUnderlyingErrorConfirming_returnsCorrectStatusCode() async throws { + func testRuntimeError_withUnderlyingErrorConforming_returnsCorrectStatusCode() async throws { let server = UniversalServer( handler: MockRuntimeErrorHandler(failWithError: TestErrorConvertible.testError("Test Error")), middlewares: [ErrorHandlingMiddleware()]