From e6f421c76fa09035b5cb7d439f964c02efddc53e Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 17 Mar 2026 09:17:57 -0500 Subject: [PATCH 1/4] switch to using OpenAPIKit's built in validation of references --- .../Parser/validateDoc.swift | 170 +----------------- .../Parser/Test_validateDoc.swift | 49 +++-- 2 files changed, 43 insertions(+), 176 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift index 48db6463..4a4834eb 100644 --- a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift +++ b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift @@ -97,168 +97,6 @@ func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String } } -/// Validates all references from an OpenAPI document represented by a ParsedOpenAPIRepresentation against its components. -/// -/// This method traverses the OpenAPI document to ensure that all references -/// within the document are valid and point to existing components. -/// -/// - Parameter doc: The OpenAPI document to validate. -/// - Throws: `Diagnostic.error` if an external reference is found or a reference is not found in components. -func validateReferences(in doc: ParsedOpenAPIRepresentation) throws { - func validateReference( - _ reference: OpenAPI.Reference, - in components: OpenAPI.Components, - location: String - ) throws { - if reference.isExternal { - throw Diagnostic.error( - message: "External references are not suppported.", - context: ["reference": reference.absoluteString, "location": location] - ) - } - if components[reference] == nil { - throw Diagnostic.error( - message: "Reference not found in components.", - context: ["reference": reference.absoluteString, "location": location] - ) - } - } - - func validateReferencesInContentTypes(_ content: OpenAPI.Content.Map, location: String) throws { - for (contentKey, contentType) in content { - switch contentType { - case .a(let ref): - try validateReference(ref, in: doc.components, location: location + "/content/\(contentKey.rawValue)") - case .b(let contentType): - if let reference: JSONReference = contentType.schema?.reference { - try validateReference( - .init(reference), - in: doc.components, - location: location + "/content/\(contentKey.rawValue)/schema" - ) - } - if let eitherExamples = contentType.examples?.values { - for example in eitherExamples { - if let reference = example.reference { - try validateReference( - reference, - in: doc.components, - location: location + "/content/\(contentKey.rawValue)/examples" - ) - } - } - } - } - } - } - - for (key, value) in doc.webhooks { - if let reference = value.reference { try validateReference(reference, in: doc.components, location: key) } - } - - for (path, pathValue) in doc.paths { - if let reference = pathValue.reference { - try validateReference(reference, in: doc.components, location: path.rawValue) - } else if let pathItem = pathValue.pathItemValue { - - for endpoint in pathItem.endpoints { - for (endpointKey, endpointValue) in endpoint.operation.callbacks { - if let reference = endpointValue.reference { - try validateReference( - reference, - in: doc.components, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/callbacks/\(endpointKey)" - ) - } - } - - for eitherParameter in endpoint.operation.parameters { - if let reference = eitherParameter.reference { - try validateReference( - reference, - in: doc.components, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/parameters" - ) - } else if let parameter = eitherParameter.parameterValue { - if let reference = parameter.schemaOrContent.schemaReference { - try validateReference( - reference, - in: doc.components, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/parameters/\(parameter.name)" - ) - } else if let content = parameter.schemaOrContent.contentValue { - try validateReferencesInContentTypes( - content, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/parameters/\(parameter.name)" - ) - } - } - } - if let reference = endpoint.operation.requestBody?.reference { - try validateReference( - reference, - in: doc.components, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/requestBody" - ) - } else if let requestBodyValue = endpoint.operation.requestBody?.requestValue { - try validateReferencesInContentTypes( - requestBodyValue.content, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/requestBody" - ) - } - - for (statusCode, eitherResponse) in endpoint.operation.responses { - if let reference = eitherResponse.reference { - try validateReference( - reference, - in: doc.components, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/responses/\(statusCode.rawValue)" - ) - } else if let responseValue = eitherResponse.responseValue { - try validateReferencesInContentTypes( - responseValue.content, - location: "\(path.rawValue)/\(endpoint.method.rawValue)/responses/\(statusCode.rawValue)" - ) - } - if let headers = eitherResponse.responseValue?.headers { - for (headerKey, eitherHeader) in headers { - if let reference = eitherHeader.reference { - try validateReference( - reference, - in: doc.components, - location: - "\(path.rawValue)/\(endpoint.method.rawValue)/responses/\(statusCode.rawValue)/headers/\(headerKey)" - ) - } else if let headerValue = eitherHeader.headerValue { - if let schemaReference = headerValue.schemaOrContent.schemaReference { - try validateReference( - schemaReference, - in: doc.components, - location: - "\(path.rawValue)/\(endpoint.method.rawValue)/responses/\(statusCode.rawValue)/headers/\(headerKey)" - ) - } else if let contentValue = headerValue.schemaOrContent.contentValue { - try validateReferencesInContentTypes( - contentValue, - location: - "\(path.rawValue)/\(endpoint.method.rawValue)/responses/\(statusCode.rawValue)/headers/\(headerKey)" - ) - } - } - } - } - } - } - - for eitherParameter in pathItem.parameters { - if let reference = eitherParameter.reference { - try validateReference(reference, in: doc.components, location: "\(path.rawValue)/parameters") - } - } - } - } -} - /// Validates all type overrides from a Config are present in the components of a ParsedOpenAPIRepresentation. /// /// This method iterates through the type overrides defined in the config and checks that for each of them a named schema is defined in the OpenAPI document. @@ -288,13 +126,15 @@ func validateTypeOverrides(_ doc: ParsedOpenAPIRepresentation, config: Config) - /// - Returns: An array of diagnostic messages representing validation warnings. /// - Throws: An error if a fatal issue is found. func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [Diagnostic] { - try validateReferences(in: doc) try validateContentTypes(in: doc) { contentType in (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil } let typeOverrideDiagnostics = validateTypeOverrides(doc, config: config) - // Run OpenAPIKit's built-in validation. + // Run OpenAPIKit's default built-in validations and additionally check + // that all references point to entries in the Components Object and all + // operations contain responses. + // // Pass `false` to `strict`, however, because we don't // want to turn schema loading warnings into errors. // We already propagate the warnings to the generator's @@ -318,7 +158,7 @@ func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [ extension OpenAPIKit.Validator { static var swiftOpenAPICustomValidator: Validator { - Validator().validating(\.operationsContainResponses) + Validator().validatingAllReferencesFoundInComponents().validating(\.operationsContainResponses) // Skip this one to be backwards compatible with previous versions of Swift OpenAPI Generator. // Even when run with strict=false, this one will cause OpenAPIKit to throw an error. Previous verions were more // lenient and Swift OpenAPI Generator would later emit a warning that it's unsupported. diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift index c8d89f3f..a665a4cb 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift @@ -421,7 +421,7 @@ final class Test_validateDoc: Test_Core { ) ]) ), - responses: [:], + responses: [200: .response()], callbacks: [.init("Callback"): .a(.component(named: "CallbackReference"))] ) ) @@ -447,7 +447,16 @@ final class Test_validateDoc: Test_Core { pathItems: ["Path2Reference": .init()] ) ) - XCTAssertNoThrow(try validateReferences(in: doc)) + XCTAssertNoThrow( + try validateDoc( + doc, + config: .init( + mode: .types, + access: Config.defaultAccessModifier, + namingStrategy: Config.defaultNamingStrategy + ) + ) + ) } func testValidateReferences_referenceNotFoundInComponents() throws { @@ -467,18 +476,27 @@ final class Test_validateDoc: Test_Core { ) ]) ), - responses: [:] + responses: [200: .response()] ) ) ) ], components: .init(schemas: ["RequestBodyContentSchema": .init(schema: .integer(.init(), .init()))]) ) - XCTAssertThrowsError(try validateReferences(in: doc)) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertThrowsError( + try validateDoc( + doc, + config: .init( + mode: .types, + access: Config.defaultAccessModifier, + namingStrategy: Config.defaultNamingStrategy + ) + ) + ) { error in + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: Reference not found in components. [context: location=/path/GET/requestBody/content/text/html/schema, reference=#/components/schemas/RequestBodyContentSchemaReference]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: JSONSchema reference points to this document and can be found in components/schemas at path: .paths['/path'].get.requestBody.content['text/html'].schema" ) } } @@ -499,11 +517,20 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) - XCTAssertThrowsError(try validateReferences(in: doc)) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertThrowsError( + try validateDoc( + doc, + config: .init( + mode: .types, + access: Config.defaultAccessModifier, + namingStrategy: Config.defaultNamingStrategy + ) + ) + ) { error in + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: External references are not suppported. [context: location=/path/GET/responses/200, reference=ExternalURL]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: Response reference points to this document and can be found in components/responses at path: .paths['/path'].get.responses.200" ) } } From b81c39118dcc411e3840258fcd32874763ab610c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 18 Mar 2026 09:03:49 -0500 Subject: [PATCH 2/4] validate content types in the same OpenAPIKit validation pass as other validations --- .../Parser/validateDoc.swift | 99 ++----------------- .../Parser/Test_validateDoc.swift | 59 ++++++----- 2 files changed, 42 insertions(+), 116 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift index 4a4834eb..e583c8f8 100644 --- a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift +++ b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift @@ -14,89 +14,6 @@ import OpenAPIKit -/// Validates all content types from an OpenAPI document represented by a ParsedOpenAPIRepresentation. -/// -/// This function iterates through the paths, endpoints, and components of the OpenAPI document, -/// checking and reporting any invalid content types using the provided validation closure. -/// -/// - Parameters: -/// - doc: The OpenAPI document representation. -/// - validate: A closure to validate each content type. -/// - Throws: An error with diagnostic information if any invalid content types are found. -func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String) -> Bool) throws { - for (path, pathValue) in doc.paths { - guard let pathItem = pathValue.pathItemValue else { continue } - for endpoint in pathItem.endpoints { - - if let eitherRequest = endpoint.operation.requestBody { - if let actualRequest = eitherRequest.requestValue { - for contentType in actualRequest.content.keys { - if !validate(contentType.rawValue) { - throw Diagnostic.error( - message: "Invalid content type string.", - context: [ - "contentType": contentType.rawValue, - "location": "\(path.rawValue)/\(endpoint.method.rawValue)/requestBody", - "recoverySuggestion": - "Must have 2 components separated by a slash '/'.", - ] - ) - } - } - } - } - - for eitherResponse in endpoint.operation.responses.values { - if let actualResponse = eitherResponse.responseValue { - for contentType in actualResponse.content.keys { - if !validate(contentType.rawValue) { - throw Diagnostic.error( - message: "Invalid content type string.", - context: [ - "contentType": contentType.rawValue, - "location": "\(path.rawValue)/\(endpoint.method.rawValue)/responses", - "recoverySuggestion": - "Must have 2 components separated by a slash '/'.", - ] - ) - } - } - } - } - } - } - - for (key, component) in doc.components.requestBodies { - let component = try doc.components.assumeLookupOnce(component) - for contentType in component.content.keys { - if !validate(contentType.rawValue) { - throw Diagnostic.error( - message: "Invalid content type string.", - context: [ - "contentType": contentType.rawValue, "location": "#/components/requestBodies/\(key.rawValue)", - "recoverySuggestion": "Must have 2 components separated by a slash '/'.", - ] - ) - } - } - } - - for (key, component) in doc.components.responses { - let component = try doc.components.assumeLookupOnce(component) - for contentType in component.content.keys { - if !validate(contentType.rawValue) { - throw Diagnostic.error( - message: "Invalid content type string.", - context: [ - "contentType": contentType.rawValue, "location": "#/components/responses/\(key.rawValue)", - "recoverySuggestion": "Must have 2 components separated by a slash '/'.", - ] - ) - } - } - } -} - /// Validates all type overrides from a Config are present in the components of a ParsedOpenAPIRepresentation. /// /// This method iterates through the type overrides defined in the config and checks that for each of them a named schema is defined in the OpenAPI document. @@ -126,14 +43,12 @@ func validateTypeOverrides(_ doc: ParsedOpenAPIRepresentation, config: Config) - /// - Returns: An array of diagnostic messages representing validation warnings. /// - Throws: An error if a fatal issue is found. func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [Diagnostic] { - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } let typeOverrideDiagnostics = validateTypeOverrides(doc, config: config) // Run OpenAPIKit's default built-in validations and additionally check - // that all references point to entries in the Components Object and all - // operations contain responses. + // that all references point to entries in the Components Object, all + // operations contain responses, and all content types parse by this + // library's code. // // Pass `false` to `strict`, however, because we don't // want to turn schema loading warnings into errors. @@ -158,7 +73,13 @@ func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [ extension OpenAPIKit.Validator { static var swiftOpenAPICustomValidator: Validator { - Validator().validatingAllReferencesFoundInComponents().validating(\.operationsContainResponses) + let contentTypesValidation: Validation = .init( + description: "Content type is of form '/'.", + check: { context in (try? _OpenAPIGeneratorCore.ContentType(string: context.subject.rawValue)) != nil } + ) + + return Validator().validatingAllReferencesFoundInComponents().validating(\.operationsContainResponses) + .validating(contentTypesValidation) // Skip this one to be backwards compatible with previous versions of Swift OpenAPI Generator. // Even when run with strict=false, this one will cause OpenAPIKit to throw an error. Previous verions were more // lenient and Swift OpenAPI Generator would later emit a warning that it's unsupported. diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift index a665a4cb..04308bc3 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift @@ -143,10 +143,11 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) + let validator = Validator.blank.validating(validateContentTypes() { contentType in + (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil + }) XCTAssertNoThrow( - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } + try doc.validate(using: validator, strict: false) ) } @@ -194,15 +195,16 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) + let validator = Validator.blank.validating(validateContentTypes() { contentType in + (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil + }) XCTAssertThrowsError( - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } + try doc.validate(using: validator, strict: false) ) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: Invalid content type string. [context: contentType=application/, location=/path1/GET/requestBody, recoverySuggestion=Must have 2 components separated by a slash '/'.]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: Content type is of form '/' at path: .paths['/path1'].get.requestBody.content['application/']" ) } } @@ -251,15 +253,16 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) + let validator = Validator.blank.validating(validateContentTypes() { contentType in + (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil + }) XCTAssertThrowsError( - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } + try doc.validate(using: validator, strict: true) ) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: Invalid content type string. [context: contentType=/plain, location=/path2/GET/responses, recoverySuggestion=Must have 2 components separated by a slash '/'.]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: Content type is of form '/' at path: .paths['/path2'].get.responses.200.content['/plain']" ) } } @@ -296,15 +299,16 @@ final class Test_validateDoc: Test_Core { "exampleRequestBody2": .init(content: [.init(rawValue: "image/")!: .content(.init(schema: .string))]), ]) ) + let validator = Validator.blank.validating(validateContentTypes() { contentType in + return (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil + }) XCTAssertThrowsError( - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } + try doc.validate(using: validator, strict: false) ) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: Invalid content type string. [context: contentType=image/, location=#/components/requestBodies/exampleRequestBody2, recoverySuggestion=Must have 2 components separated by a slash '/'.]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: Content type is of form '/' at path: .components.requestBodies.exampleRequestBody2.content['image/']" ) } } @@ -345,15 +349,16 @@ final class Test_validateDoc: Test_Core { ), ]) ) + let validator = Validator.blank.validating(validateContentTypes() { contentType in + (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil + }) XCTAssertThrowsError( - try validateContentTypes(in: doc) { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - } + try doc.validate(using: validator, strict: false) ) { error in - XCTAssertTrue(error is Diagnostic) + XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( - error.localizedDescription, - "error: Invalid content type string. [context: contentType=, location=#/components/responses/exampleRequestBody2, recoverySuggestion=Must have 2 components separated by a slash '/'.]" + OpenAPI.Error(from: error).localizedDescription, + "Failed to satisfy: Content type is of form '/' at path: .components.responses.exampleRequestBody2.content." ) } } From f2eb01e71f49122f6909ebbda9591103a5314047 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 12 May 2026 08:56:09 -0500 Subject: [PATCH 3/4] move content type validator and simplify tests of it --- .../Parser/validateDoc.swift | 15 ++++++++------ .../Parser/Test_validateDoc.swift | 20 +++++-------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift index e583c8f8..c21e6933 100644 --- a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift +++ b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift @@ -14,6 +14,14 @@ import OpenAPIKit +/// Validates all content types from an OpenAPI document can be parsed as a ContentType. +var contentTypesValidation: Validation { + .init( + description: "Content type is of form '/'.", + check: { context in (try? _OpenAPIGeneratorCore.ContentType(string: context.subject.rawValue)) != nil } + ) +} + /// Validates all type overrides from a Config are present in the components of a ParsedOpenAPIRepresentation. /// /// This method iterates through the type overrides defined in the config and checks that for each of them a named schema is defined in the OpenAPI document. @@ -73,12 +81,7 @@ func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [ extension OpenAPIKit.Validator { static var swiftOpenAPICustomValidator: Validator { - let contentTypesValidation: Validation = .init( - description: "Content type is of form '/'.", - check: { context in (try? _OpenAPIGeneratorCore.ContentType(string: context.subject.rawValue)) != nil } - ) - - return Validator().validatingAllReferencesFoundInComponents().validating(\.operationsContainResponses) + Validator().validatingAllReferencesFoundInComponents().validating(\.operationsContainResponses) .validating(contentTypesValidation) // Skip this one to be backwards compatible with previous versions of Swift OpenAPI Generator. // Even when run with strict=false, this one will cause OpenAPIKit to throw an error. Previous verions were more diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift index 04308bc3..8fa175f5 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift @@ -143,9 +143,7 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) - let validator = Validator.blank.validating(validateContentTypes() { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - }) + let validator = Validator.blank.validating(contentTypesValidation) XCTAssertNoThrow( try doc.validate(using: validator, strict: false) ) @@ -195,9 +193,7 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) - let validator = Validator.blank.validating(validateContentTypes() { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - }) + let validator = Validator.blank.validating(contentTypesValidation) XCTAssertThrowsError( try doc.validate(using: validator, strict: false) ) { error in @@ -253,9 +249,7 @@ final class Test_validateDoc: Test_Core { ], components: .noComponents ) - let validator = Validator.blank.validating(validateContentTypes() { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - }) + let validator = Validator.blank.validating(contentTypesValidation) XCTAssertThrowsError( try doc.validate(using: validator, strict: true) ) { error in @@ -299,9 +293,7 @@ final class Test_validateDoc: Test_Core { "exampleRequestBody2": .init(content: [.init(rawValue: "image/")!: .content(.init(schema: .string))]), ]) ) - let validator = Validator.blank.validating(validateContentTypes() { contentType in - return (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - }) + let validator = Validator.blank.validating(contentTypesValidation) XCTAssertThrowsError( try doc.validate(using: validator, strict: false) ) { error in @@ -349,9 +341,7 @@ final class Test_validateDoc: Test_Core { ), ]) ) - let validator = Validator.blank.validating(validateContentTypes() { contentType in - (try? _OpenAPIGeneratorCore.ContentType(string: contentType)) != nil - }) + let validator = Validator.blank.validating(contentTypesValidation) XCTAssertThrowsError( try doc.validate(using: validator, strict: false) ) { error in From 04e20aa6ff8647f805451f311269602b8c01de85 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 12 May 2026 09:02:18 -0500 Subject: [PATCH 4/4] update expected validations and format tests --- .../Parser/Test_validateDoc.swift | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift index 8fa175f5..ce15992a 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift @@ -30,16 +30,17 @@ final class Test_validateDoc: Test_Core { "All Operation Ids in Document are unique", "Server Variable\'s enum is either not defined or is non-empty (if defined).", "Server Variable\'s default must exist in enum, if enum is defined.", - "JSONSchema reference can be found in components/schemas", - "JSONSchema reference can be found in components/schemas", - "Response reference can be found in components/responses", - "Parameter reference can be found in components/parameters", - "Example reference can be found in components/examples", - "Request reference can be found in components/requestBodies", - "Header reference can be found in components/headers", - "Link reference can be found in components/links", - "Callbacks reference can be found in components/callbacks", - "PathItem reference can be found in components/pathItems", "Operations contain at least one response", + "JSONSchema reference points to this document and can be found in components/schemas", + "JSONSchema reference points to this document and can be found in components/schemas", + "Response reference points to this document and can be found in components/responses", + "Parameter reference points to this document and can be found in components/parameters", + "Example reference points to this document and can be found in components/examples", + "Request reference points to this document and can be found in components/requestBodies", + "Header reference points to this document and can be found in components/headers", + "Link reference points to this document and can be found in components/links", + "Callbacks reference points to this document and can be found in components/callbacks", + "PathItem reference points to this document and can be found in components/pathItems", + "Operations contain at least one response", "Content type is of form \'/\'.", ] ) } @@ -144,9 +145,7 @@ final class Test_validateDoc: Test_Core { components: .noComponents ) let validator = Validator.blank.validating(contentTypesValidation) - XCTAssertNoThrow( - try doc.validate(using: validator, strict: false) - ) + XCTAssertNoThrow(try doc.validate(using: validator, strict: false)) } func testValidateContentTypes_invalidContentTypesInRequestBody() throws { @@ -194,9 +193,7 @@ final class Test_validateDoc: Test_Core { components: .noComponents ) let validator = Validator.blank.validating(contentTypesValidation) - XCTAssertThrowsError( - try doc.validate(using: validator, strict: false) - ) { error in + XCTAssertThrowsError(try doc.validate(using: validator, strict: false)) { error in XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( OpenAPI.Error(from: error).localizedDescription, @@ -250,9 +247,7 @@ final class Test_validateDoc: Test_Core { components: .noComponents ) let validator = Validator.blank.validating(contentTypesValidation) - XCTAssertThrowsError( - try doc.validate(using: validator, strict: true) - ) { error in + XCTAssertThrowsError(try doc.validate(using: validator, strict: true)) { error in XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( OpenAPI.Error(from: error).localizedDescription, @@ -294,9 +289,7 @@ final class Test_validateDoc: Test_Core { ]) ) let validator = Validator.blank.validating(contentTypesValidation) - XCTAssertThrowsError( - try doc.validate(using: validator, strict: false) - ) { error in + XCTAssertThrowsError(try doc.validate(using: validator, strict: false)) { error in XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( OpenAPI.Error(from: error).localizedDescription, @@ -342,9 +335,7 @@ final class Test_validateDoc: Test_Core { ]) ) let validator = Validator.blank.validating(contentTypesValidation) - XCTAssertThrowsError( - try doc.validate(using: validator, strict: false) - ) { error in + XCTAssertThrowsError(try doc.validate(using: validator, strict: false)) { error in XCTAssertTrue(error is ValidationErrorCollection) XCTAssertEqual( OpenAPI.Error(from: error).localizedDescription,