From 1010489a02425b4e59f0fe5f564987a97f9d6d80 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 15 Nov 2019 16:59:00 -0800 Subject: [PATCH] compare(to:) bug fixes and test additions --- .../Comparisons/AttributesCompare.swift | 54 +++++- .../Comparisons/RelationshipsCompare.swift | 52 +++++- .../Comparisons/AttributesCompareTests.swift | 67 +++++-- .../Comparisons/DocumentCompareTests.swift | 2 + .../Optional+AbstractWrapper.swift | 16 ++ .../RelationshipsCompareTests.swift | 169 +++++++++++++++++- 6 files changed, 324 insertions(+), 36 deletions(-) create mode 100644 Tests/JSONAPITestingTests/Comparisons/Optional+AbstractWrapper.swift diff --git a/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift b/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift index 147432c..83aeea2 100644 --- a/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift @@ -1,5 +1,5 @@ // -// File.swift +// AttributesCompare.swift // // // Created by Mathew Polzin on 11/3/19. @@ -24,12 +24,16 @@ extension Attributes { continue } - if (attributesEqual(child.value, otherChild.value)) { - comparisons[childLabel] = .same - } else { - let otherChildDescription = attributeDescription(of: otherChild.value) + do { + if (try attributesEqual(child.value, otherChild.value)) { + comparisons[childLabel] = .same + } else { + let otherChildDescription = attributeDescription(of: otherChild.value) - comparisons[childLabel] = .different(childDescription, otherChildDescription) + comparisons[childLabel] = .different(childDescription, otherChildDescription) + } + } catch let error { + comparisons[childLabel] = .prebuilt(String(describing: error)) } } @@ -37,9 +41,20 @@ extension Attributes { } } -fileprivate func attributesEqual(_ one: Any, _ two: Any) -> Bool { +enum AttributeCompareError: Swift.Error, CustomStringConvertible { + case nonAttributeTypeProperty(String) + + var description: String { + switch self { + case .nonAttributeTypeProperty(let type): + return "comparison on non-JSON:API Attribute type (\(type)) not supported." + } + } +} + +fileprivate func attributesEqual(_ one: Any, _ two: Any) throws -> Bool { guard let attr = one as? AbstractAttribute else { - return false + throw AttributeCompareError.nonAttributeTypeProperty(String(describing: type(of: one))) } return attr.equals(two) @@ -55,6 +70,29 @@ protocol AbstractAttribute { func equals(_ other: Any) -> Bool } +extension Optional: AbstractAttribute where Wrapped: AbstractAttribute { + var abstractDescription: String { + switch self { + case .none: + return "nil" + case .some(let rel): + return rel.abstractDescription + } + } + + func equals(_ other: Any) -> Bool { + switch self { + case .none: + return (other as? _AbstractWrapper).map { $0.abstractSelf == nil } ?? false + case .some(let rel): + guard case let .some(otherVal) = (other as? _AbstractWrapper)?.abstractSelf else { + return rel.equals(other) + } + return rel.equals(otherVal) + } + } +} + extension Attribute: AbstractAttribute { var abstractDescription: String { String(describing: value) } diff --git a/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift index 0c24386..9a8c010 100644 --- a/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift @@ -24,12 +24,16 @@ extension Relationships { continue } - if (relationshipsEqual(child.value, otherChild.value)) { - comparisons[childLabel] = .same - } else { - let otherChildDescription = relationshipDescription(of: otherChild.value) + do { + if (try relationshipsEqual(child.value, otherChild.value)) { + comparisons[childLabel] = .same + } else { + let otherChildDescription = relationshipDescription(of: otherChild.value) - comparisons[childLabel] = .different(childDescription, otherChildDescription) + comparisons[childLabel] = .different(childDescription, otherChildDescription) + } + } catch let error { + comparisons[childLabel] = .prebuilt(String(describing: error)) } } @@ -37,9 +41,20 @@ extension Relationships { } } -fileprivate func relationshipsEqual(_ one: Any, _ two: Any) -> Bool { +enum RelationshipCompareError: Swift.Error, CustomStringConvertible { + case nonRelationshipTypeProperty(String) + + var description: String { + switch self { + case .nonRelationshipTypeProperty(let type): + return "comparison on non-JSON:API Relationship type (\(type)) not supported." + } + } +} + +fileprivate func relationshipsEqual(_ one: Any, _ two: Any) throws -> Bool { guard let attr = one as? AbstractRelationship else { - return false + throw RelationshipCompareError.nonRelationshipTypeProperty(String(describing: type(of: one))) } return attr.equals(two) @@ -55,6 +70,29 @@ protocol AbstractRelationship { func equals(_ other: Any) -> Bool } +extension Optional: AbstractRelationship where Wrapped: AbstractRelationship { + var abstractDescription: String { + switch self { + case .none: + return "nil" + case .some(let rel): + return rel.abstractDescription + } + } + + func equals(_ other: Any) -> Bool { + switch self { + case .none: + return (other as? _AbstractWrapper).map { $0.abstractSelf == nil } ?? false + case .some(let rel): + guard case let .some(otherVal) = (other as? _AbstractWrapper)?.abstractSelf else { + return rel.equals(other) + } + return rel.equals(otherVal) + } + } +} + extension ToOneRelationship: AbstractRelationship { var abstractDescription: String { if meta is NoMetadata && links is NoLinks { diff --git a/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift index e54031c..6141be6 100644 --- a/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift @@ -1,5 +1,5 @@ // -// File.swift +// AttributesCompareTests.swift // // // Created by Mathew Polzin on 11/3/19. @@ -17,7 +17,9 @@ final class AttributesCompareTests: XCTestCase { bool: true, double: 105.4, struct: .init(value: .init()), - transformed: try .init(rawValue: 10) + transformed: try .init(rawValue: 10), + optional: .init(value: 20), + optionalTransformed: try .init(rawValue: 10) ) let attr2 = attr1 @@ -27,7 +29,9 @@ final class AttributesCompareTests: XCTestCase { "bool": .same, "double": .same, "struct": .same, - "transformed": .same + "transformed": .same, + "optional": .same, + "optionalTransformed": .same ]) } @@ -38,7 +42,9 @@ final class AttributesCompareTests: XCTestCase { bool: true, double: 105.4, struct: .init(value: .init()), - transformed: try .init(rawValue: 10) + transformed: try .init(rawValue: 10), + optional: nil, + optionalTransformed: nil ) let attr2 = TestAttributes( string: "hello", @@ -46,7 +52,9 @@ final class AttributesCompareTests: XCTestCase { bool: false, double: 1.4, struct: .init(value: .init(val: "there")), - transformed: try .init(rawValue: 11) + transformed: try .init(rawValue: 11), + optional: .init(value: 20.5), + optionalTransformed: try .init(rawValue: 10) ) XCTAssertEqual(attr1.compare(to: attr2), [ @@ -55,7 +63,29 @@ final class AttributesCompareTests: XCTestCase { "bool": .different("true", "false"), "double": .different("105.4", "1.4"), "struct": .different("string: hello", "string: there"), - "transformed": .different("10", "11") + "transformed": .different("10", "11"), + "optional": .different("nil", "20.5"), + "optionalTransformed": .different("nil", "10") + ]) + } + + func test_nonAttributeTypes() { + let attr1 = NonAttributeTest( + string: "hello", + int: 10, + double: 11.2, + bool: true, + struct: .init(), + optional: nil + ) + + XCTAssertEqual(attr1.compare(to: attr1), [ + "string": .prebuilt("comparison on non-JSON:API Attribute type (String) not supported."), + "int": .prebuilt("comparison on non-JSON:API Attribute type (Int) not supported."), + "double": .prebuilt("comparison on non-JSON:API Attribute type (Double) not supported."), + "bool": .prebuilt("comparison on non-JSON:API Attribute type (Bool) not supported."), + "struct": .prebuilt("comparison on non-JSON:API Attribute type (Struct) not supported."), + "optional": .prebuilt("comparison on non-JSON:API Attribute type (Optional) not supported.") ]) } } @@ -67,16 +97,18 @@ private struct TestAttributes: JSONAPI.Attributes { let double: Attribute let `struct`: Attribute let transformed: TransformedAttribute + let optional: Attribute? + let optionalTransformed: TransformedAttribute? +} - struct Struct: Equatable, Codable, CustomStringConvertible { - let string: String +private struct Struct: Equatable, Codable, CustomStringConvertible { + let string: String - init(val: String = "hello") { - self.string = val - } - - var description: String { return "string: \(string)" } + init(val: String = "hello") { + self.string = val } + + var description: String { return "string: \(string)" } } private enum TestTransformer: Transformer { @@ -84,3 +116,12 @@ private enum TestTransformer: Transformer { return "\(value)" } } + +private struct NonAttributeTest: JSONAPI.Attributes { + let string: String + let int: Int + let double: Double + let bool: Bool + let `struct`: Struct + let optional: Int? +} diff --git a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift index 9cbf93f..8b464e6 100644 --- a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift @@ -21,6 +21,8 @@ final class DocumentCompareTests: XCTestCase { XCTAssertTrue(d8.compare(to: d8).differences.isEmpty) XCTAssertTrue(d9.compare(to: d9).differences.isEmpty) XCTAssertTrue(d10.compare(to: d10).differences.isEmpty) + + XCTAssertEqual(String(describing: d1.compare(to: d1).body), "same") } func test_errorAndData() { diff --git a/Tests/JSONAPITestingTests/Comparisons/Optional+AbstractWrapper.swift b/Tests/JSONAPITestingTests/Comparisons/Optional+AbstractWrapper.swift new file mode 100644 index 0000000..2a068d0 --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/Optional+AbstractWrapper.swift @@ -0,0 +1,16 @@ +// +// Optional+AbstractWrapper.swift +// JSONAPITesting +// +// Created by Mathew Polzin on 11/15/19. +// + +protocol _AbstractWrapper { + var abstractSelf: Any? { get } +} + +extension Optional: _AbstractWrapper { + var abstractSelf: Any? { + return self + } +} diff --git a/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift index b2dc83e..44b720a 100644 --- a/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift @@ -1,5 +1,5 @@ // -// File.swift +// RelationshipCompareTests.swift // // // Created by Mathew Polzin on 11/5/19. @@ -11,22 +11,175 @@ import JSONAPITesting final class RelationshipsCompareTests: XCTestCase { func test_same() { - // TODO: write test + let r1 = TestRelationships( + a: t1, + b: t2, + c: t3, + d: t4 + ) + let r2 = r1 + + XCTAssertTrue(r1.compare(to: r2).allSatisfy { $0.value == .same }) + + let r3 = TestRelationships( + a: t1_differentId, + b: t2_differentLinks, + c: t3_differentId, + d: t4_differentLinks + ) + let r4 = r3 + + XCTAssertTrue(r3.compare(to: r4).allSatisfy { $0.value == .same }) + + let r5 = TestRelationships( + a: nil, + b: nil, + c: nil, + d: nil + ) + let r6 = r5 + + XCTAssertTrue(r5.compare(to: r6).allSatisfy { $0.value == .same }) } func test_differentIds() { - // TODO: write test - } + let r1 = TestRelationships( + a: t1, + b: nil, + c: t3, + d: nil + ) - func test_differentTypes() { - // TODO: write test + let r2 = TestRelationships( + a: t1_differentId, + b: nil, + c: t3_differentId, + d: nil + ) + + XCTAssertEqual(r1.compare(to: r2), [ + "a": .different("Id(123)", "Id(999)"), + "b": .same, + "c": .different("123, 456", "999, 1010"), + "d": .same + ]) } func test_differentMetadata() { - // TODO: write test + let r1 = TestRelationships( + a: nil, + b: t2, + c: nil, + d: t4 + ) + + let r2 = TestRelationships( + a: nil, + b: t2_differentMeta, + c: nil, + d: t4_differentMeta + ) + + XCTAssertEqual(r1.compare(to: r2), [ + "a": .same, + "b": .different(#"("Id(456)", "hello: world", "link: http://google.com")"#, #"("Id(456)", "hello: there", "link: http://google.com")"#), + "c": .same, + "d": .different(#"("123, 456", "hello: world", "link: http://google.com")"#, #"("123, 456", "hello: there", "link: http://google.com")"#) + ]) } func test_differentLinks() { - // TODO: write tests + let r1 = TestRelationships( + a: nil, + b: t2, + c: nil, + d: t4 + ) + + let r2 = TestRelationships( + a: nil, + b: t2_differentLinks, + c: nil, + d: t4_differentLinks + ) + + XCTAssertEqual(r1.compare(to: r2), [ + "a": .same, + "b": .different(#"("Id(456)", "hello: world", "link: http://google.com")"#, #"("Id(456)", "hello: world", "link: http://yahoo.com")"#), + "c": .same, + "d": .different(#"("123, 456", "hello: world", "link: http://google.com")"#, #"("123, 456", "hello: world", "link: http://yahoo.com")"#) + ]) + } + + func test_nonRelationshipTypes() { + let r1 = TestNonRelationships( + a: .init(attributes: .none, relationships: .none, meta: .none, links: .none), + b: false, + c: 10, + d: "1234" + ) + + XCTAssertEqual(r1.compare(to: r1), [ + "a": .prebuilt("comparison on non-JSON:API Relationship type (ResourceObject) not supported."), + "b": .prebuilt("comparison on non-JSON:API Relationship type (Bool) not supported."), + "c": .prebuilt("comparison on non-JSON:API Relationship type (Int) not supported."), + "d": .prebuilt("comparison on non-JSON:API Relationship type (Id>) not supported.") + ]) + } + + let t1 = ToOneRelationship(id: "123") + let t2 = ToOneRelationship(id: "456", meta: .init(hello: "world"), links: .init(link: .init(url: "http://google.com"))) + let t3 = ToManyRelationship(ids: ["123", "456"]) + let t4 = ToManyRelationship(ids: ["123", "456"], meta: .init(hello: "world"), links: .init(link: .init(url: "http://google.com"))) + + let t1_differentId = ToOneRelationship(id: "999") + let t3_differentId = ToManyRelationship(ids: ["999", "1010"]) + + let t2_differentLinks = ToOneRelationship(id: "456", meta: .init(hello: "world"), links: .init(link: .init(url: "http://yahoo.com"))) + let t4_differentLinks = ToManyRelationship(ids: ["123", "456"], meta: .init(hello: "world"), links: .init(link: .init(url: "http://yahoo.com"))) + + let t2_differentMeta = ToOneRelationship(id: "456", meta: .init(hello: "there"), links: .init(link: .init(url: "http://google.com"))) + let t4_differentMeta = ToManyRelationship(ids: ["123", "456"], meta: .init(hello: "there"), links: .init(link: .init(url: "http://google.com"))) +} + +// MARK: - Test Types +extension RelationshipsCompareTests { + enum TestTypeDescription: ResourceObjectDescription { + static let jsonType: String = "test" + + typealias Attributes = NoAttributes + typealias Relationships = NoRelationships + } + + typealias TestType = ResourceObject + + struct TestMeta: JSONAPI.Meta, CustomDebugStringConvertible { + let hello: String + + var debugDescription: String { + "hello: \(hello)" + } + } + + struct TestLinks: JSONAPI.Links, CustomDebugStringConvertible { + let link: Link + + var debugDescription: String { + "link: \(link.url)" + } + } + + struct TestRelationships: JSONAPI.Relationships { + let a: ToOneRelationship? + let b: ToOneRelationship? + let c: ToManyRelationship? + let d: ToManyRelationship? + } + + struct TestNonRelationships: JSONAPI.Relationships { + let a: TestType + let b: Bool + let c: Int + let d: JSONAPI.Id } }