compare(to:) bug fixes and test additions

This commit is contained in:
Mathew Polzin
2019-11-15 16:59:00 -08:00
parent ae855c85ee
commit 1010489a02
6 changed files with 324 additions and 36 deletions
@@ -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) }
@@ -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 {
@@ -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<Int>) not supported.")
])
}
}
@@ -67,16 +97,18 @@ private struct TestAttributes: JSONAPI.Attributes {
let double: Attribute<Double>
let `struct`: Attribute<Struct>
let transformed: TransformedAttribute<Int, TestTransformer>
let optional: Attribute<Double>?
let optionalTransformed: TransformedAttribute<Int, TestTransformer>?
}
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?
}
@@ -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() {
@@ -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
}
}
@@ -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<TestTypeDescription, NoMetadata, NoLinks, String>) 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<String, ResourceObject<TestTypeDescription, NoMetadata, NoLinks, String>>) not supported.")
])
}
let t1 = ToOneRelationship<TestType, NoMetadata, NoLinks>(id: "123")
let t2 = ToOneRelationship<TestType, TestMeta, TestLinks>(id: "456", meta: .init(hello: "world"), links: .init(link: .init(url: "http://google.com")))
let t3 = ToManyRelationship<TestType, NoMetadata, NoLinks>(ids: ["123", "456"])
let t4 = ToManyRelationship<TestType, TestMeta, TestLinks>(ids: ["123", "456"], meta: .init(hello: "world"), links: .init(link: .init(url: "http://google.com")))
let t1_differentId = ToOneRelationship<TestType, NoMetadata, NoLinks>(id: "999")
let t3_differentId = ToManyRelationship<TestType, NoMetadata, NoLinks>(ids: ["999", "1010"])
let t2_differentLinks = ToOneRelationship<TestType, TestMeta, TestLinks>(id: "456", meta: .init(hello: "world"), links: .init(link: .init(url: "http://yahoo.com")))
let t4_differentLinks = ToManyRelationship<TestType, TestMeta, TestLinks>(ids: ["123", "456"], meta: .init(hello: "world"), links: .init(link: .init(url: "http://yahoo.com")))
let t2_differentMeta = ToOneRelationship<TestType, TestMeta, TestLinks>(id: "456", meta: .init(hello: "there"), links: .init(link: .init(url: "http://google.com")))
let t4_differentMeta = ToManyRelationship<TestType, TestMeta, TestLinks>(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<TestTypeDescription, NoMetadata, NoLinks, String>
struct TestMeta: JSONAPI.Meta, CustomDebugStringConvertible {
let hello: String
var debugDescription: String {
"hello: \(hello)"
}
}
struct TestLinks: JSONAPI.Links, CustomDebugStringConvertible {
let link: Link<String, NoMetadata>
var debugDescription: String {
"link: \(link.url)"
}
}
struct TestRelationships: JSONAPI.Relationships {
let a: ToOneRelationship<TestType, NoMetadata, NoLinks>?
let b: ToOneRelationship<TestType, TestMeta, TestLinks>?
let c: ToManyRelationship<TestType, NoMetadata, NoLinks>?
let d: ToManyRelationship<TestType, TestMeta, TestLinks>?
}
struct TestNonRelationships: JSONAPI.Relationships {
let a: TestType
let b: Bool
let c: Int
let d: JSONAPI.Id<String, TestType>
}
}