From 713c71048c80d252aed7dfb505eb428aab6eae68 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Mon, 31 Jul 2023 16:18:29 +0200 Subject: [PATCH] Refactor PetstoreConsumerTests to allow multiple versions --- Package.swift | 11 +- .../Assertions.swift | 0 Sources/PetstoreConsumerTestCore/Common.swift | 131 ++++++++++++++++++ .../TestClientTransport.swift | 0 .../TestServerTransport.swift | 0 .../FileBasedReferenceTests.swift | 50 +++++-- .../SnippetBasedReferenceTests.swift | 26 +++- .../XCTestDiagnosticCollector.swift | 4 + Tests/PetstoreConsumerTests/Common.swift | 119 +--------------- Tests/PetstoreConsumerTests/TestServer.swift | 1 + Tests/PetstoreConsumerTests/Test_Client.swift | 1 + Tests/PetstoreConsumerTests/Test_Server.swift | 1 + Tests/PetstoreConsumerTests/Test_Types.swift | 1 + 13 files changed, 209 insertions(+), 136 deletions(-) rename {Tests/PetstoreConsumerTests => Sources/PetstoreConsumerTestCore}/Assertions.swift (100%) create mode 100644 Sources/PetstoreConsumerTestCore/Common.swift rename {Tests/PetstoreConsumerTests => Sources/PetstoreConsumerTestCore}/TestClientTransport.swift (100%) rename {Tests/PetstoreConsumerTests => Sources/PetstoreConsumerTestCore}/TestServerTransport.swift (100%) diff --git a/Package.swift b/Package.swift index 8256a45b..b8f76ea9 100644 --- a/Package.swift +++ b/Package.swift @@ -123,13 +123,22 @@ let package = Package( swiftSettings: swiftSettings ), + // Common types for concrete PetstoreConsumer*Tests test targets. + .target( + name: "PetstoreConsumerTestCore", + dependencies: [ + .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime") + ], + swiftSettings: swiftSettings + ), + // PetstoreConsumerTests // Builds and tests the reference code from GeneratorReferenceTests // to ensure it actually works correctly at runtime. .testTarget( name: "PetstoreConsumerTests", dependencies: [ - .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime") + "PetstoreConsumerTestCore" ], swiftSettings: swiftSettings ), diff --git a/Tests/PetstoreConsumerTests/Assertions.swift b/Sources/PetstoreConsumerTestCore/Assertions.swift similarity index 100% rename from Tests/PetstoreConsumerTests/Assertions.swift rename to Sources/PetstoreConsumerTestCore/Assertions.swift diff --git a/Sources/PetstoreConsumerTestCore/Common.swift b/Sources/PetstoreConsumerTestCore/Common.swift new file mode 100644 index 00000000..18b7d704 --- /dev/null +++ b/Sources/PetstoreConsumerTestCore/Common.swift @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 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 OpenAPIRuntime +import Foundation + +public enum TestError: Swift.Error, LocalizedError, CustomStringConvertible { + case noHandlerFound(method: HTTPMethod, path: [RouterPathComponent]) + case invalidURLString(String) + case unexpectedValue(Any) + case unexpectedMissingRequestBody + + public var description: String { + switch self { + case .noHandlerFound(let method, let path): + return "No handler found for method \(method.name) and path \(path.stringPath)" + case .invalidURLString(let string): + return "Invalid URL string: \(string)" + case .unexpectedValue(let value): + return "Unexpected value: \(value)" + case .unexpectedMissingRequestBody: + return "Unexpected missing request body" + } + } + + public var errorDescription: String? { + description + } +} + +public extension Date { + static var test: Date { + Date(timeIntervalSince1970: 1_674_036_251) + } + + static var testString: String { + "2023-01-18T10:04:11Z" + } +} + +public extension Array where Element == RouterPathComponent { + var stringPath: String { + map(\.description).joined(separator: "/") + } +} + +public extension Response { + init( + statusCode: Int, + headers: [HeaderField] = [], + encodedBody: String + ) { + self.init( + statusCode: statusCode, + headerFields: headers, + body: Data(encodedBody.utf8) + ) + } + + static var listPetsSuccess: Self { + .init( + statusCode: 200, + headers: [ + .init(name: "content-type", value: "application/json") + ], + encodedBody: #""" + [ + { + "id": 1, + "name": "Fluffz" + } + ] + """# + ) + } +} + +public extension Data { + var pretty: String { + String(decoding: self, as: UTF8.self) + } + + static var abcdString: String { + "abcd" + } + + static var abcd: Data { + Data(abcdString.utf8) + } + + static var efghString: String { + "efgh" + } + + static var quotedEfghString: String { + #""efgh""# + } + + static var efgh: Data { + Data(efghString.utf8) + } +} + +public extension Request { + init( + path: String, + query: String? = nil, + method: HTTPMethod, + headerFields: [HeaderField] = [], + encodedBody: String + ) throws { + let body = Data(encodedBody.utf8) + self.init( + path: path, + query: query, + method: method, + headerFields: headerFields, + body: body + ) + } +} diff --git a/Tests/PetstoreConsumerTests/TestClientTransport.swift b/Sources/PetstoreConsumerTestCore/TestClientTransport.swift similarity index 100% rename from Tests/PetstoreConsumerTests/TestClientTransport.swift rename to Sources/PetstoreConsumerTestCore/TestClientTransport.swift diff --git a/Tests/PetstoreConsumerTests/TestServerTransport.swift b/Sources/PetstoreConsumerTestCore/TestServerTransport.swift similarity index 100% rename from Tests/PetstoreConsumerTests/TestServerTransport.swift rename to Sources/PetstoreConsumerTestCore/TestServerTransport.swift diff --git a/Tests/OpenAPIGeneratorReferenceTests/FileBasedReferenceTests.swift b/Tests/OpenAPIGeneratorReferenceTests/FileBasedReferenceTests.swift index c3f7c985..11e033ca 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/FileBasedReferenceTests.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/FileBasedReferenceTests.swift @@ -45,7 +45,7 @@ class FileBasedReferenceTests: XCTestCase { } func testPetstore() throws { - try _test(referenceProjectNamed: .petstore) + try _test(referenceProject: .init(name: .petstore)) } // MARK: - Private @@ -60,7 +60,8 @@ class FileBasedReferenceTests: XCTestCase { } func performReferenceTest( - _ referenceTest: TestConfig + _ referenceTest: TestConfig, + ignoredDiagnosticMessages: Set = [] ) throws { print( """ @@ -85,7 +86,8 @@ class FileBasedReferenceTests: XCTestCase { // Run the requested generator invocation let generatorPipeline = self.makeGeneratorPipeline( - config: referenceTest.asConfig + config: referenceTest.asConfig, + ignoredDiagnosticMessages: ignoredDiagnosticMessages ) let generatedOutputSource = try generatorPipeline.run(input) @@ -115,16 +117,33 @@ class FileBasedReferenceTests: XCTestCase { enum ReferenceProjectName: String, Hashable, CaseIterable { case petstore - var fileName: String { + var openAPIDocFileName: String { "\(rawValue).yaml" } - var directoryName: String { + var fixtureCodeDirectoryName: String { rawValue.capitalized } } - func _test(referenceProjectNamed name: ReferenceProjectName) throws { + struct ReferenceProject: Hashable, Equatable { + var name: ReferenceProjectName + var customDirectoryName: String? = nil + + var fixtureCodeDirectoryName: String { + customDirectoryName ?? name.fixtureCodeDirectoryName + } + + var openAPIDocFileName: String { + name.openAPIDocFileName + } + } + + func _test( + referenceProject project: ReferenceProject, + featureFlags: FeatureFlags = [], + ignoredDiagnosticMessages: Set = [] + ) throws { let modes: [GeneratorMode] = [ .types, .client, @@ -133,19 +152,23 @@ class FileBasedReferenceTests: XCTestCase { for mode in modes { try performReferenceTest( .init( - docFilePath: "Docs/\(name.fileName)", + docFilePath: "Docs/\(project.openAPIDocFileName)", mode: mode, additionalImports: [], - featureFlags: [], - referenceOutputDirectory: "ReferenceSources/\(name.directoryName)" - ) + featureFlags: featureFlags, + referenceOutputDirectory: "ReferenceSources/\(project.fixtureCodeDirectoryName)" + ), + ignoredDiagnosticMessages: ignoredDiagnosticMessages ) } } } extension FileBasedReferenceTests { - private func makeGeneratorPipeline(config: Config) -> GeneratorPipeline { + private func makeGeneratorPipeline( + config: Config, + ignoredDiagnosticMessages: Set = [] + ) -> GeneratorPipeline { let parser = YamsParser() let translator = MultiplexTranslator() let renderer = TextBasedRenderer() @@ -160,7 +183,10 @@ extension FileBasedReferenceTests { return newFile }, config: config, - diagnostics: XCTestDiagnosticCollector(test: self) + diagnostics: XCTestDiagnosticCollector( + test: self, + ignoredDiagnosticMessages: ignoredDiagnosticMessages + ) ) } diff --git a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift index d88f8119..95046c91 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift @@ -605,11 +605,15 @@ extension SnippetBasedReferenceTests { ) } - func makeTypesTranslator(componentsYAML: String) throws -> TypesFileTranslator { + func makeTypesTranslator( + featureFlags: FeatureFlags = [], + ignoredDiagnosticMessages: Set = [], + componentsYAML: String + ) throws -> TypesFileTranslator { let components = try YAMLDecoder().decode(OpenAPI.Components.self, from: componentsYAML) return TypesFileTranslator( - config: Config(mode: .types), - diagnostics: XCTestDiagnosticCollector(test: self), + config: Config(mode: .types, featureFlags: featureFlags), + diagnostics: XCTestDiagnosticCollector(test: self, ignoredDiagnosticMessages: ignoredDiagnosticMessages), components: components ) } @@ -648,23 +652,35 @@ extension SnippetBasedReferenceTests { } func assertResponsesTranslation( + featureFlags: FeatureFlags = [], + ignoredDiagnosticMessages: Set = [], _ componentsYAML: String, _ expectedSwift: String, file: StaticString = #filePath, line: UInt = #line ) throws { - let translator = try makeTypesTranslator(componentsYAML: componentsYAML) + let translator = try makeTypesTranslator( + featureFlags: featureFlags, + ignoredDiagnosticMessages: ignoredDiagnosticMessages, + componentsYAML: componentsYAML + ) let translation = try translator.translateComponentResponses(translator.components.responses) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) } func assertRequestBodiesTranslation( + featureFlags: FeatureFlags = [], + ignoredDiagnosticMessages: Set = [], _ componentsYAML: String, _ expectedSwift: String, file: StaticString = #filePath, line: UInt = #line ) throws { - let translator = try makeTypesTranslator(componentsYAML: componentsYAML) + let translator = try makeTypesTranslator( + featureFlags: featureFlags, + ignoredDiagnosticMessages: ignoredDiagnosticMessages, + componentsYAML: componentsYAML + ) let translation = try translator.translateComponentRequestBodies(translator.components.requestBodies) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) } diff --git a/Tests/OpenAPIGeneratorReferenceTests/XCTestDiagnosticCollector.swift b/Tests/OpenAPIGeneratorReferenceTests/XCTestDiagnosticCollector.swift index b59f657c..83dc8f29 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/XCTestDiagnosticCollector.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/XCTestDiagnosticCollector.swift @@ -17,8 +17,12 @@ import XCTest // A diagnostic collector that fails the running test on a warning or error. struct XCTestDiagnosticCollector: DiagnosticCollector { var test: XCTestCase + var ignoredDiagnosticMessages: Set = [] func emit(_ diagnostic: Diagnostic) { + guard !ignoredDiagnosticMessages.contains(diagnostic.message) else { + return + } print("Test emitted diagnostic: \(diagnostic.description)") switch diagnostic.severity { case .note: diff --git a/Tests/PetstoreConsumerTests/Common.swift b/Tests/PetstoreConsumerTests/Common.swift index d955d643..229e20b0 100644 --- a/Tests/PetstoreConsumerTests/Common.swift +++ b/Tests/PetstoreConsumerTests/Common.swift @@ -11,127 +11,10 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -import OpenAPIRuntime -import Foundation - -enum TestError: Swift.Error, LocalizedError, CustomStringConvertible { - case noHandlerFound(method: HTTPMethod, path: [RouterPathComponent]) - case invalidURLString(String) - case unexpectedValue(Any) - case unexpectedMissingRequestBody - - var description: String { - switch self { - case .noHandlerFound(let method, let path): - return "No handler found for method \(method.name) and path \(path.stringPath)" - case .invalidURLString(let string): - return "Invalid URL string: \(string)" - case .unexpectedValue(let value): - return "Unexpected value: \(value)" - case .unexpectedMissingRequestBody: - return "Unexpected missing request body" - } - } - - var errorDescription: String? { - description - } -} - -extension Date { - static var test: Date { - Date(timeIntervalSince1970: 1_674_036_251) - } - - static var testString: String { - "2023-01-18T10:04:11Z" - } -} - -extension Array where Element == RouterPathComponent { - var stringPath: String { - map(\.description).joined(separator: "/") - } -} - -extension Response { - init( - statusCode: Int, - headers: [HeaderField] = [], - encodedBody: String - ) { - self.init( - statusCode: statusCode, - headerFields: headers, - body: Data(encodedBody.utf8) - ) - } - - static var listPetsSuccess: Self { - .init( - statusCode: 200, - headers: [ - .init(name: "content-type", value: "application/json") - ], - encodedBody: #""" - [ - { - "id": 1, - "name": "Fluffz" - } - ] - """# - ) - } -} +import XCTest extension Operations.listPets.Output { static var success: Self { .ok(.init(headers: .init(My_Response_UUID: "abcd"), body: .json([]))) } } - -extension Data { - var pretty: String { - String(decoding: self, as: UTF8.self) - } - - static var abcdString: String { - "abcd" - } - - static var abcd: Data { - Data(abcdString.utf8) - } - - static var efghString: String { - "efgh" - } - - static var quotedEfghString: String { - #""efgh""# - } - - static var efgh: Data { - Data(efghString.utf8) - } -} - -extension Request { - init( - path: String, - query: String? = nil, - method: HTTPMethod, - headerFields: [HeaderField] = [], - encodedBody: String - ) throws { - let body = Data(encodedBody.utf8) - self.init( - path: path, - query: query, - method: method, - headerFields: headerFields, - body: body - ) - } -} diff --git a/Tests/PetstoreConsumerTests/TestServer.swift b/Tests/PetstoreConsumerTests/TestServer.swift index 7b06386c..77060c68 100644 --- a/Tests/PetstoreConsumerTests/TestServer.swift +++ b/Tests/PetstoreConsumerTests/TestServer.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import OpenAPIRuntime import Foundation +import PetstoreConsumerTestCore extension APIProtocol { func configuredServer( diff --git a/Tests/PetstoreConsumerTests/Test_Client.swift b/Tests/PetstoreConsumerTests/Test_Client.swift index 4db7b7be..2fdd4e05 100644 --- a/Tests/PetstoreConsumerTests/Test_Client.swift +++ b/Tests/PetstoreConsumerTests/Test_Client.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest import OpenAPIRuntime +import PetstoreConsumerTestCore final class Test_Client: XCTestCase { diff --git a/Tests/PetstoreConsumerTests/Test_Server.swift b/Tests/PetstoreConsumerTests/Test_Server.swift index 26f963a0..2ce84134 100644 --- a/Tests/PetstoreConsumerTests/Test_Server.swift +++ b/Tests/PetstoreConsumerTests/Test_Server.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest import OpenAPIRuntime +import PetstoreConsumerTestCore final class Test_Server: XCTestCase { diff --git a/Tests/PetstoreConsumerTests/Test_Types.swift b/Tests/PetstoreConsumerTests/Test_Types.swift index 9bf49520..46d28f9e 100644 --- a/Tests/PetstoreConsumerTests/Test_Types.swift +++ b/Tests/PetstoreConsumerTests/Test_Types.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest import OpenAPIRuntime +import PetstoreConsumerTestCore final class Test_Types: XCTestCase {