diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 68dadfa..218fd2f 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -7,9 +7,13 @@ on: jobs: + api_breakage: + name: Check API breakage + uses: BinaryBirds/github-workflows/.github/workflows/api_breakage.yml@main + swiftlang_checks: name: Swiftlang Checks - uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@0.0.7 with: license_header_check_project_name: "project" format_check_enabled : true diff --git a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift index d4976da..03f00eb 100644 --- a/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/BoolSchemaRepresentable.swift @@ -35,8 +35,8 @@ extension BoolSchemaRepresentable { discriminator: nil, externalDocs: nil, allowedValues: nil, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift index b4a6287..b136a0d 100644 --- a/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/DoubleSchemaRepresentable.swift @@ -40,8 +40,8 @@ extension DoubleSchemaRepresentable { maximum: nil, minimum: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift index 7b641e7..923f708 100644 --- a/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/FloatSchemaRepresentable.swift @@ -40,8 +40,8 @@ extension FloatSchemaRepresentable { maximum: nil, minimum: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift index ce2d557..ef446c9 100644 --- a/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int32SchemaRepresentable.swift @@ -40,8 +40,8 @@ extension Int32SchemaRepresentable { maximum: nil, minimum: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift index 43e15a4..1f01181 100644 --- a/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/Int64SchemaRepresentable.swift @@ -40,8 +40,8 @@ extension Int64SchemaRepresentable { maximum: nil, minimum: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift index 3030ef6..1dc3bf8 100644 --- a/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/IntSchemaRepresentable.swift @@ -40,8 +40,8 @@ extension IntSchemaRepresentable { maximum: nil, minimum: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift index f0d9223..a553c9d 100644 --- a/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift +++ b/Sources/FeatherOpenAPI/Schema/StringSchemaRepresentable.swift @@ -40,8 +40,8 @@ extension StringSchemaRepresentable { maxLength: nil, pattern: nil, allowedValues: allowedValues?.map { .init($0) }, - defaultValue: .init(defaultValue), - example: .init(example) + defaultValue: defaultValue.map { .init($0) }, + example: example.map { .init($0) } ) } } diff --git a/Tests/FeatherOpenAPITests/SchemaSerializationTestSuite.swift b/Tests/FeatherOpenAPITests/SchemaSerializationTestSuite.swift new file mode 100644 index 0000000..255404c --- /dev/null +++ b/Tests/FeatherOpenAPITests/SchemaSerializationTestSuite.swift @@ -0,0 +1,84 @@ +import OpenAPIKit30 +import Testing +import Yams + +@testable import FeatherOpenAPI + +@Suite +struct SchemaSerializationTestSuite { + + @Test + func omittedStringDefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder() + .encode(OmittedStringSchema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedIntDefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder().encode(OmittedIntSchema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedInt32DefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder() + .encode(OmittedInt32Schema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedInt64DefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder() + .encode(OmittedInt64Schema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedFloatDefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder() + .encode(OmittedFloatSchema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedDoubleDefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder() + .encode(OmittedDoubleSchema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func omittedBoolDefaultAndExampleDoNotSerializeAsNull() throws { + let yaml = try YAMLEncoder().encode(OmittedBoolSchema().openAPISchema()) + #expect(yaml.contains("default: null") == false) + #expect(yaml.contains("example: null") == false) + } + + @Test + func explicitDefaultAndExampleStillSerialize() throws { + let yaml = try YAMLEncoder() + .encode(ExplicitStringSchema().openAPISchema()) + #expect(yaml.contains("default: abc")) + #expect(yaml.contains("example: xyz")) + } +} + +private struct OmittedStringSchema: StringSchemaRepresentable {} +private struct OmittedIntSchema: IntSchemaRepresentable {} +private struct OmittedInt32Schema: Int32SchemaRepresentable {} +private struct OmittedInt64Schema: Int64SchemaRepresentable {} +private struct OmittedFloatSchema: FloatSchemaRepresentable {} +private struct OmittedDoubleSchema: DoubleSchemaRepresentable {} +private struct OmittedBoolSchema: BoolSchemaRepresentable {} + +private struct ExplicitStringSchema: StringSchemaRepresentable { + var defaultValue: String? { "abc" } + var example: String? { "xyz" } +}