diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 8f37320..6204cc4 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -4,4 +4,10 @@ import NullCodableTests var tests = [XCTestCaseEntry]() tests += NullCodableTests.allTests() +tests += NullCodablePropertyWrapperTests.allTests() +tests += NullCodableJSONEncoderTests.allTests() +tests += NullCodableJSONDecoderTests.allTests() +tests += NullCodableComplexStructTests.allTests() +tests += NullCodableEquatableTests.allTests() +tests += NullCodableSendableTests.allTests() XCTMain(tests) diff --git a/Tests/NullCodableTests/NullCodableComplexStructTests.swift b/Tests/NullCodableTests/NullCodableComplexStructTests.swift new file mode 100644 index 0000000..fe5afa9 --- /dev/null +++ b/Tests/NullCodableTests/NullCodableComplexStructTests.swift @@ -0,0 +1,119 @@ +import Foundation +import XCTest +@testable import NullCodable + +final class NullCodableComplexStructTests: XCTestCase { + + private struct Address: Codable, Equatable { + let street: String + let city: String + let country: String + } + + private struct LineItem: Codable, Equatable { + let name: String + let quantity: Int + let price: Decimal + } + + private struct Customer: Codable, Equatable { + let name: String + @NullCodable var email: String? + let address: Address + } + + private struct Order: Codable, Equatable { + let id: String + let customer: Customer + let items: [LineItem] + @NullCodable var notes: [String]? + @NullCodable var discount: Decimal? + } + + func test_encodesComplexStructToJSON() throws { + let order = Order( + id: "order-123", + customer: Customer( + name: "Ada Lovelace", + email: nil, + address: Address( + street: "1 Analytical Engine Way", + city: "London", + country: "United Kingdom" + ) + ), + items: [ + LineItem(name: "Notebook", quantity: 2, price: 12.50), + LineItem(name: "Pen", quantity: 1, price: 3.25), + ], + notes: nil, + discount: 5 + ) + + let data = try JSONEncoder().encode(order) + let actual = try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? NSDictionary) + let expected = try XCTUnwrap(JSONSerialization.jsonObject(with: Data(""" + { + "id": "order-123", + "customer": { + "name": "Ada Lovelace", + "email": null, + "address": { + "street": "1 Analytical Engine Way", + "city": "London", + "country": "United Kingdom" + } + }, + "items": [ + { "name": "Notebook", "quantity": 2, "price": 12.5 }, + { "name": "Pen", "quantity": 1, "price": 3.25 } + ], + "notes": null, + "discount": 5 + } + """.utf8)) as? NSDictionary) + + XCTAssertTrue(actual.isEqual(expected)) + } + + func test_decodesComplexStructFromJSON() throws { + let json = """ + { + "id": "order-123", + "customer": { + "name": "Ada Lovelace", + "email": "ada@example.com", + "address": { + "street": "1 Analytical Engine Way", + "city": "London", + "country": "United Kingdom" + } + }, + "items": [ + { "name": "Notebook", "quantity": 2, "price": 12.5 }, + { "name": "Pen", "quantity": 1, "price": 3.25 } + ], + "notes": ["Gift wrap"], + "discount": null + } + """ + + let order = try JSONDecoder().decode(Order.self, from: Data(json.utf8)) + + XCTAssertEqual(order.id, "order-123") + XCTAssertEqual(order.customer.name, "Ada Lovelace") + XCTAssertEqual(order.customer.email, "ada@example.com") + XCTAssertEqual(order.customer.address.city, "London") + XCTAssertEqual(order.items, [ + LineItem(name: "Notebook", quantity: 2, price: 12.5), + LineItem(name: "Pen", quantity: 1, price: 3.25), + ]) + XCTAssertEqual(order.notes, ["Gift wrap"]) + XCTAssertNil(order.discount) + } + + static let allTests = [ + ("test_encodesComplexStructToJSON", test_encodesComplexStructToJSON), + ("test_decodesComplexStructFromJSON", test_decodesComplexStructFromJSON), + ] +} diff --git a/Tests/NullCodableTests/NullCodableEquatableTests.swift b/Tests/NullCodableTests/NullCodableEquatableTests.swift new file mode 100644 index 0000000..bbf60eb --- /dev/null +++ b/Tests/NullCodableTests/NullCodableEquatableTests.swift @@ -0,0 +1,28 @@ +import XCTest +@testable import NullCodable + +final class NullCodableEquatableTests: XCTestCase { + + func test_comparesWrappedValues() { + XCTAssertEqual( + NullCodable(wrappedValue: "value"), + NullCodable(wrappedValue: "value") + ) + XCTAssertEqual( + NullCodable(wrappedValue: nil), + NullCodable(wrappedValue: nil) + ) + XCTAssertNotEqual( + NullCodable(wrappedValue: "first"), + NullCodable(wrappedValue: "second") + ) + XCTAssertNotEqual( + NullCodable(wrappedValue: nil), + NullCodable(wrappedValue: "value") + ) + } + + static let allTests = [ + ("test_comparesWrappedValues", test_comparesWrappedValues), + ] +} diff --git a/Tests/NullCodableTests/NullCodableJSONDecoderTests.swift b/Tests/NullCodableTests/NullCodableJSONDecoderTests.swift new file mode 100644 index 0000000..50a9fa8 --- /dev/null +++ b/Tests/NullCodableTests/NullCodableJSONDecoderTests.swift @@ -0,0 +1,53 @@ +import XCTest +@testable import NullCodable + +final class NullCodableJSONDecoderTests: XCTestCase { + + func test_decodesJSONNullAsNil() throws { + let value = try JSONDecoder().decode( + NullCodable.self, + from: Data("null".utf8) + ) + + XCTAssertNil(value.wrappedValue) + } + + func test_decodesWrappedValue() throws { + let value = try JSONDecoder().decode( + NullCodable.self, + from: Data("42".utf8) + ) + + XCTAssertEqual(value.wrappedValue, 42) + } + + func test_decodesInvalidWrappedValueWithAnError() { + XCTAssertThrowsError( + try JSONDecoder().decode( + NullCodable.self, + from: Data("\"not an integer\"".utf8) + ) + ) { error in + XCTAssertTrue(error is DecodingError) + } + } + + func test_supportsCollectionValues() throws { + let value = NullCodable(wrappedValue: ["one", "two"]) + + let data = try JSONEncoder().encode(value) + let decoded = try JSONDecoder().decode( + NullCodable<[String]>.self, + from: data + ) + + XCTAssertEqual(decoded.wrappedValue, ["one", "two"]) + } + + static let allTests = [ + ("test_decodesJSONNullAsNil", test_decodesJSONNullAsNil), + ("test_decodesWrappedValue", test_decodesWrappedValue), + ("test_decodesInvalidWrappedValueWithAnError", test_decodesInvalidWrappedValueWithAnError), + ("test_supportsCollectionValues", test_supportsCollectionValues), + ] +} diff --git a/Tests/NullCodableTests/NullCodableJSONEncoderTests.swift b/Tests/NullCodableTests/NullCodableJSONEncoderTests.swift new file mode 100644 index 0000000..02a4d72 --- /dev/null +++ b/Tests/NullCodableTests/NullCodableJSONEncoderTests.swift @@ -0,0 +1,26 @@ +import XCTest +@testable import NullCodable + +final class NullCodableJSONEncoderTests: XCTestCase { + + func test_encodesNilAsJSONNull() throws { + let value = NullCodable(wrappedValue: nil) + + let data = try JSONEncoder().encode(value) + + XCTAssertEqual(String(decoding: data, as: UTF8.self), "null") + } + + func test_encodesWrappedValue() throws { + let value = NullCodable(wrappedValue: 42) + + let data = try JSONEncoder().encode(value) + + XCTAssertEqual(String(decoding: data, as: UTF8.self), "42") + } + + static let allTests = [ + ("test_encodesNilAsJSONNull", test_encodesNilAsJSONNull), + ("test_encodesWrappedValue", test_encodesWrappedValue), + ] +} diff --git a/Tests/NullCodableTests/NullCodablePropertyWrapperTests.swift b/Tests/NullCodableTests/NullCodablePropertyWrapperTests.swift new file mode 100644 index 0000000..fdea90d --- /dev/null +++ b/Tests/NullCodableTests/NullCodablePropertyWrapperTests.swift @@ -0,0 +1,26 @@ +import XCTest +@testable import NullCodable + +final class NullCodablePropertyWrapperTests: XCTestCase { + + func test_defaultValueIsNil() { + let value = NullCodable(wrappedValue: nil) + + XCTAssertNil(value.wrappedValue) + } + + func test_wrappedValueCanBeReadAndUpdated() { + var value = NullCodable(wrappedValue: "before") + + XCTAssertEqual(value.wrappedValue, "before") + + value.wrappedValue = "after" + + XCTAssertEqual(value.wrappedValue, "after") + } + + static let allTests = [ + ("test_defaultValueIsNil", test_defaultValueIsNil), + ("test_wrappedValueCanBeReadAndUpdated", test_wrappedValueCanBeReadAndUpdated), + ] +} diff --git a/Tests/NullCodableTests/NullCodableSendableTests.swift b/Tests/NullCodableTests/NullCodableSendableTests.swift new file mode 100644 index 0000000..fc6f1ca --- /dev/null +++ b/Tests/NullCodableTests/NullCodableSendableTests.swift @@ -0,0 +1,18 @@ +import XCTest +@testable import NullCodable + +final class NullCodableSendableTests: XCTestCase { + + func test_conformsToSendableWhenWrappedValueIsSendable() { + assertSendable(NullCodable(wrappedValue: "value")) + assertSendable(NullCodable(wrappedValue: 42)) + } + + private func assertSendable(_ value: T) { + _ = value + } + + static let allTests = [ + ("test_conformsToSendableWhenWrappedValueIsSendable", test_conformsToSendableWhenWrappedValueIsSendable), + ] +} diff --git a/Tests/NullCodableTests/NullCodableTests.swift b/Tests/NullCodableTests/NullCodableTests.swift index 48845a7..6a6028b 100644 --- a/Tests/NullCodableTests/NullCodableTests.swift +++ b/Tests/NullCodableTests/NullCodableTests.swift @@ -129,7 +129,7 @@ final class NullCodableTests: XCTestCase { XCTAssertEqual(test.b, 42) } - static var allTests = [ + static let allTests = [ ("test_noConformance", test_noConformance), ("test_encodable", test_encodable), ("test_decodable", test_decodable), diff --git a/Tests/NullCodableTests/XCTestManifests.swift b/Tests/NullCodableTests/XCTestManifests.swift index c12daa2..5215fed 100644 --- a/Tests/NullCodableTests/XCTestManifests.swift +++ b/Tests/NullCodableTests/XCTestManifests.swift @@ -4,6 +4,12 @@ import XCTest public func allTests() -> [XCTestCaseEntry] { return [ testCase(NullCodableTests.allTests), + testCase(NullCodablePropertyWrapperTests.allTests), + testCase(NullCodableJSONEncoderTests.allTests), + testCase(NullCodableJSONDecoderTests.allTests), + testCase(NullCodableComplexStructTests.allTests), + testCase(NullCodableEquatableTests.allTests), + testCase(NullCodableSendableTests.allTests), ] } #endif