From f7bfa91ccca76231fb097c87cc63ba489c8de8ac Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 5 Nov 2019 19:03:51 -0800 Subject: [PATCH 1/3] lots of comparison code, a few small breaking changes --- Package.resolved | 4 +- Package.swift | 2 +- Sources/JSONAPI/Document/Document.swift | 21 +- Sources/JSONAPI/Document/Includes.swift | 6 +- Sources/JSONAPI/Error/BasicJSONAPIError.swift | 6 +- .../JSONAPI/Error/GenericJSONAPIError.swift | 11 +- .../Resource Object/ResourceObject.swift | 6 + .../Comparisons/ArrayCompare.swift | 78 ++++++ .../Comparisons/AttributesCompare.swift | 78 ++++++ .../Comparisons/Comparison.swift | 68 +++++ .../Comparisons/DocumentCompare.swift | 180 +++++++++++++ .../Comparisons/DocumentDataCompare.swift | 167 ++++++++++++ .../Comparisons/IncludesCompare.swift | 66 +++++ .../Comparisons/RelationshipsCompare.swift | 105 ++++++++ .../Comparisons/ResourceObjectCompare.swift | 71 ++++++ Sources/JSONAPITesting/Optional+ZipWith.swift | 12 + .../Comparisons/AttributesCompareTests.swift | 74 ++++++ .../Comparisons/DocumentCompareTests.swift | 170 +++++++++++++ .../Comparisons/IncludesCompareTests.swift | 239 ++++++++++++++++++ .../RelationshipsCompareTests.swift | 14 + .../ResourceObjectCompareTests.swift | 68 +++++ 21 files changed, 1431 insertions(+), 15 deletions(-) create mode 100644 Sources/JSONAPITesting/Comparisons/ArrayCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/AttributesCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/Comparison.swift create mode 100644 Sources/JSONAPITesting/Comparisons/DocumentCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/IncludesCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift create mode 100644 Sources/JSONAPITesting/Comparisons/ResourceObjectCompare.swift create mode 100644 Sources/JSONAPITesting/Optional+ZipWith.swift create mode 100644 Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift create mode 100644 Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift create mode 100644 Tests/JSONAPITestingTests/Comparisons/IncludesCompareTests.swift create mode 100644 Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift create mode 100644 Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift diff --git a/Package.resolved b/Package.resolved index b1970b8..cc81a53 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/mattpolzin/Poly.git", "state": { "branch": null, - "revision": "b24fd3b41bf3126d4c6dede3708135182172af60", - "version": "2.2.0" + "revision": "18cd995be5c28c4dfdc1464e54ee0efb03e215bf", + "version": "2.3.0" } } ] diff --git a/Package.swift b/Package.swift index 2093673..5b780cb 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( targets: ["JSONAPITesting"]) ], dependencies: [ - .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.2.0")), + .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.3.0")), ], targets: [ .target( diff --git a/Sources/JSONAPI/Document/Document.swift b/Sources/JSONAPI/Document/Document.swift index 5c8160b..00d1ce0 100644 --- a/Sources/JSONAPI/Document/Document.swift +++ b/Sources/JSONAPI/Document/Document.swift @@ -20,7 +20,16 @@ public protocol EncodableJSONAPIDocument: Equatable, Encodable { typealias Body = Document.Body + /// The Body of the Document. This body is either one or more errors + /// with links and metadata attempted to parse but not guaranteed or + /// it is a successful data struct containing all the primary and + /// included resources, the metadata, and the links that this + /// document type specifies. var body: Body { get } + + /// The JSON API Spec calls this the JSON:API Object. It contains version + /// and metadata information about the API itself. + var apiDescription: APIDescription { get } } /// A `JSONAPIDocument` supports encoding and decoding of a JSON:API @@ -30,6 +39,7 @@ public protocol JSONAPIDocument: EncodableJSONAPIDocument, Decodable where Prima /// A JSON API Document represents the entire body /// of a JSON API request or the entire body of /// a JSON API response. +/// /// Note that this type uses Camel case. If your /// API uses snake case, you will want to use /// a conversion such as the one offerred by the @@ -37,15 +47,10 @@ public protocol JSONAPIDocument: EncodableJSONAPIDocument, Decodable where Prima public struct Document: EncodableJSONAPIDocument { public typealias Include = IncludeType - /// The JSON API Spec calls this the JSON:API Object. It contains version - /// and metadata information about the API itself. + // See `EncodableJSONAPIDocument` for documentation. public let apiDescription: APIDescription - /// The Body of the Document. This body is either one or more errors - /// with links and metadata attempted to parse but not guaranteed or - /// it is a successful data struct containing all the primary and - /// included resources, the metadata, and the links that this - /// document type specifies. + // See `EncodableJSONAPIDocument` for documentation. public let body: Body public enum Body: Equatable { @@ -423,6 +428,7 @@ extension Document { @dynamicMemberLookup public struct ErrorDocument: EncodableJSONAPIDocument { public var body: Document.Body { return document.body } + public var apiDescription: APIDescription { return document.apiDescription } private let document: Document @@ -450,6 +456,7 @@ extension Document { @dynamicMemberLookup public struct SuccessDocument: EncodableJSONAPIDocument { public var body: Document.Body { return document.body } + public var apiDescription: APIDescription { return document.apiDescription } private let document: Document diff --git a/Sources/JSONAPI/Document/Includes.swift b/Sources/JSONAPI/Document/Includes.swift index b6d93e0..d74171f 100644 --- a/Sources/JSONAPI/Document/Includes.swift +++ b/Sources/JSONAPI/Document/Includes.swift @@ -14,15 +14,15 @@ public typealias Include = EncodableJSONPoly /// /// If you have /// -/// `let includes: Includes> = ...` +/// let includes: Includes> = ... /// /// then you can access all `Thing1` included resources with /// -/// `let includedThings = includes[Thing1.self]` +/// let includedThings = includes[Thing1.self] public struct Includes: Encodable, Equatable { public static var none: Includes { return .init(values: []) } - let values: [I] + public let values: [I] public init(values: [I]) { self.values = values diff --git a/Sources/JSONAPI/Error/BasicJSONAPIError.swift b/Sources/JSONAPI/Error/BasicJSONAPIError.swift index 28795b9..cdabc07 100644 --- a/Sources/JSONAPI/Error/BasicJSONAPIError.swift +++ b/Sources/JSONAPI/Error/BasicJSONAPIError.swift @@ -6,7 +6,7 @@ // /// Most of the JSON:API Spec defined Error fields. -public struct BasicJSONAPIErrorPayload: Codable, Equatable, ErrorDictType { +public struct BasicJSONAPIErrorPayload: Codable, Equatable, ErrorDictType, CustomStringConvertible { /// a unique identifier for this particular occurrence of the problem public let id: IdType? // public let links: Links? // we skip this for now to avoid adding complexity to using this basic type. @@ -61,6 +61,10 @@ public struct BasicJSONAPIErrorPayload: Codable, Eq ].compactMap { $0 } return Dictionary(uniqueKeysWithValues: keysAndValues) } + + public var description: String { + return definedFields.map { "\($0.key): \($0.value)" }.sorted().joined(separator: ", ") + } } /// `BasicJSONAPIError` optionally decodes many possible fields diff --git a/Sources/JSONAPI/Error/GenericJSONAPIError.swift b/Sources/JSONAPI/Error/GenericJSONAPIError.swift index 09383e7..473caac 100644 --- a/Sources/JSONAPI/Error/GenericJSONAPIError.swift +++ b/Sources/JSONAPI/Error/GenericJSONAPIError.swift @@ -8,7 +8,7 @@ /// `GenericJSONAPIError` can be used to specify whatever error /// payload you expect to need to parse in responses and handle any /// other payload structure as `.unknownError`. -public enum GenericJSONAPIError: JSONAPIError { +public enum GenericJSONAPIError: JSONAPIError, CustomStringConvertible { case unknownError case error(ErrorPayload) @@ -35,6 +35,15 @@ public enum GenericJSONAPIError: JSONAPIError public static var unknown: Self { return .unknownError } + + public var description: String { + switch self { + case .unknownError: + return "unknown error" + case .error(let payload): + return String(describing: payload) + } + } } public extension GenericJSONAPIError { diff --git a/Sources/JSONAPI/Resource/Resource Object/ResourceObject.swift b/Sources/JSONAPI/Resource/Resource Object/ResourceObject.swift index a62d271..4c59e88 100644 --- a/Sources/JSONAPI/Resource/Resource Object/ResourceObject.swift +++ b/Sources/JSONAPI/Resource/Resource Object/ResourceObject.swift @@ -102,6 +102,12 @@ extension ResourceObjectProxy { public protocol ResourceObjectType: ResourceObjectProxy, PrimaryResource where Description: ResourceObjectDescription { associatedtype Meta: JSONAPI.Meta associatedtype Links: JSONAPI.Links + + /// Any additional metadata packaged with the entity. + var meta: Meta { get } + + /// Links related to the entity. + var links: Links { get } } public protocol IdentifiableResourceObjectType: ResourceObjectType, Relatable where EntityRawIdType: JSONAPI.RawIdType {} diff --git a/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift b/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift new file mode 100644 index 0000000..d14ced7 --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift @@ -0,0 +1,78 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/5/19. +// + +import JSONAPI + +public enum ArrayElementComparison: Equatable, CustomStringConvertible { + case same + case missing + case differentTypes(String, String) + case differentValues(String, String) + case prebuilt(String) + + public init(sameTypeComparison: Comparison) { + switch sameTypeComparison { + case .same: + self = .same + case .different(let one, let two): + self = .differentValues(one, two) + case .prebuilt(let str): + self = .prebuilt(str) + } + } + + public init(resourceObjectComparison: ResourceObjectComparison) { + guard !resourceObjectComparison.isSame else { + self = .same + return + } + + self = .prebuilt( + resourceObjectComparison + .differences + .sorted { $0.key < $1.key } + .map { "\($0.key): \($0.value)" } + .joined(separator: ", ") + ) + } + + public var description: String { + switch self { + case .same: + return "same" + case .missing: + return "missing" + case .differentTypes(let one, let two), + .differentValues(let one, let two): + return "\(one) ≠ \(two)" + case .prebuilt(let description): + return description + } + } + + public var rawValue: String { description } +} + +extension Array { + func compare(to other: Self, using compare: (Element, Element) -> ArrayElementComparison) -> [ArrayElementComparison] { + let isSelfLonger = count >= other.count + + let longer = isSelfLonger ? self : other + let shorter = isSelfLonger ? other : self + + return longer.indices.map { idx in + guard shorter.indices.contains(idx) else { + return .missing + } + + let this = longer[idx] + let other = shorter[idx] + + return compare(this, other) + } + } +} diff --git a/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift b/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift new file mode 100644 index 0000000..541c52e --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/AttributesCompare.swift @@ -0,0 +1,78 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +import JSONAPI + +extension Attributes { + public func compare(to other: Self) -> [String: Comparison] { + let mirror1 = Mirror(reflecting: self) + let mirror2 = Mirror(reflecting: other) + + var comparisons = [String: Comparison]() + + for child in mirror1.children { + guard let childLabel = child.label else { continue } + + let childDescription = attributeDescription(of: child.value) + + guard let otherChild = mirror2.children.first(where: { $0.label == childLabel }) else { + comparisons[childLabel] = .different(childDescription, "missing") + continue + } + + if (attributesEqual(child.value, otherChild.value)) { + comparisons[childLabel] = .same + } else { + let otherChildDescription = attributeDescription(of: otherChild.value) + + comparisons[childLabel] = .different(childDescription, otherChildDescription) + } + } + + return comparisons + } +} + +fileprivate func attributesEqual(_ one: Any, _ two: Any) -> Bool { + guard let attr = one as? AbstractAttribute else { + return false + } + + return attr.equals(two) +} + +fileprivate func attributeDescription(of thing: Any) -> String { + return (thing as? AbstractAttribute)?.abstractDescription ?? String(describing: thing) +} + +protocol AbstractAttribute { + var abstractDescription: String { get } + + func equals(_ other: Any) -> Bool +} + +extension Attribute: AbstractAttribute { + var abstractDescription: String { String(describing: value) } + + func equals(_ other: Any) -> Bool { + guard let attributeB = other as? Self else { + return false + } + return abstractDescription == attributeB.abstractDescription + } +} + +extension TransformedAttribute: AbstractAttribute { + var abstractDescription: String { String(describing: value) } + + func equals(_ other: Any) -> Bool { + guard let attributeB = other as? Self else { + return false + } + return abstractDescription == attributeB.abstractDescription + } +} diff --git a/Sources/JSONAPITesting/Comparisons/Comparison.swift b/Sources/JSONAPITesting/Comparisons/Comparison.swift new file mode 100644 index 0000000..a6b97ee --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/Comparison.swift @@ -0,0 +1,68 @@ +// +// Comparison.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +public enum Comparison: Equatable, CustomStringConvertible { + case same + case different(String, String) + case prebuilt(String) + + init(_ one: T, _ two: T) { + guard one == two else { + self = .different(String(describing: one), String(describing: two)) + return + } + self = .same + } + + init(reducing other: ArrayElementComparison) { + switch other { + case .same: + self = .same + case .differentTypes(let one, let two), + .differentValues(let one, let two): + self = .different(one, two) + case .missing: + self = .different("array length 1", "array length 2") + case .prebuilt(let str): + self = .prebuilt(str) + } + } + + public var description: String { + switch self { + case .same: + return "same" + case .different(let one, let two): + return "\(one) ≠ \(two)" + case .prebuilt(let str): + return str + } + } + + public var rawValue: String { description } + + public var isSame: Bool { self == .same } +} + +public typealias NamedDifferences = [String: String] + +public protocol PropertyComparable: CustomStringConvertible { + var differences: NamedDifferences { get } +} + +extension PropertyComparable { + public var description: String { + return differences + .map { "(\($0): \($1))" } + .sorted() + .joined(separator: ", ") + } + + public var rawValue: String { description } + + public var isSame: Bool { differences.isEmpty } +} diff --git a/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift b/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift new file mode 100644 index 0000000..15c417f --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift @@ -0,0 +1,180 @@ +// +// DocumentCompare.swift +// JSONAPITesting +// +// Created by Mathew Polzin on 11/4/19. +// + +import JSONAPI + +public struct DocumentComparison: Equatable, PropertyComparable { + public let apiDescription: Comparison + public let body: BodyComparison + + init(apiDescription: Comparison, body: BodyComparison) { + self.apiDescription = apiDescription + self.body = body + } + + public var differences: NamedDifferences { + return Dictionary( + [ + apiDescription != .same ? ("API Description", apiDescription.rawValue) : nil, + body != .same ? ("Body", body.rawValue) : nil + ].compactMap { $0 }, + uniquingKeysWith: { $1 } + ) + } +} + +public enum BodyComparison: Equatable, CustomStringConvertible { + case same + case dataErrorMismatch(errorOnLeft: Bool) + case differentErrors(ErrorComparison) + case differentData(DocumentDataComparison) + + public typealias ErrorComparison = [Comparison] + + static func compare(errors errors1: [E], _ meta1: M?, _ links1: L?, with errors2: [E], _ meta2: M?, _ links2: L?) -> ErrorComparison { + return errors1.compare( + to: errors2, + using: { error1, error2 in + guard error1 != error2 else { + return .same + } + + return .differentValues( + String(describing: error1), + String(describing: error2) + ) + } + ).map(Comparison.init) + [ + Comparison(meta1, meta2), + Comparison(links1, links2) + ] + } + + public var description: String { + switch self { + case .same: + return "same" + case .dataErrorMismatch(errorOnLeft: let errorOnLeft): + let errorString = "error response" + let dataString = "data response" + let left = errorOnLeft ? errorString : dataString + let right = errorOnLeft ? dataString : errorString + + return "\(left) ≠ \(right)" + case .differentErrors(let comparisons): + return comparisons + .filter { !$0.isSame } + .map { $0.rawValue } + .sorted() + .joined(separator: ", ") + case .differentData(let comparison): + return comparison.rawValue + } + } + + public var rawValue: String { description } +} + +extension Document { + public func compare(to other: Self) -> DocumentComparison where PrimaryResourceBody == SingleResourceBody, T: ResourceObjectType { + return DocumentComparison( + apiDescription: Comparison( + String(describing: apiDescription), + String(describing: other.apiDescription) + ), + body: body.compare(to: other.body) + ) + } + + public func compare(to other: Self) -> DocumentComparison where PrimaryResourceBody == SingleResourceBody, T: ResourceObjectType { + return DocumentComparison( + apiDescription: Comparison( + String(describing: apiDescription), + String(describing: other.apiDescription) + ), + body: body.compare(to: other.body) + ) + } + + public func compare(to other: Self) -> DocumentComparison where PrimaryResourceBody == ManyResourceBody, T: ResourceObjectType { + return DocumentComparison( + apiDescription: Comparison( + String(describing: apiDescription), + String(describing: other.apiDescription) + ), + body: body.compare(to: other.body) + ) + } +} + +extension Document.Body { + public func compare(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody { + guard self != other else { + return .same + } + + switch (self, other) { + case (.errors(let errors1), .errors(let errors2)): + return .differentErrors(BodyComparison.compare(errors: errors1.0, + errors1.meta, + errors1.links, + with: errors2.0, + errors2.meta, + errors2.links)) + case (.errors, .data): + return .dataErrorMismatch(errorOnLeft: true) + case (.data, .errors): + return .dataErrorMismatch(errorOnLeft: false) + case (.data(let data1), .data(let data2)): + return .differentData(data1.compare(to: data2)) + } + } + + public func compare(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody { + guard self != other else { + return .same + } + + switch (self, other) { + case (.errors(let errors1), .errors(let errors2)): + return .differentErrors(BodyComparison.compare(errors: errors1.0, + errors1.meta, + errors1.links, + with: errors2.0, + errors2.meta, + errors2.links)) + case (.errors, .data): + return .dataErrorMismatch(errorOnLeft: true) + case (.data, .errors): + return .dataErrorMismatch(errorOnLeft: false) + case (.data(let data1), .data(let data2)): + return .differentData(data1.compare(to: data2)) + } + } + + public func compare(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == ManyResourceBody { + guard self != other else { + return .same + } + + switch (self, other) { + case (.errors(let errors1), .errors(let errors2)): + return .differentErrors(BodyComparison.compare(errors: errors1.0, + errors1.meta, + errors1.links, + with: errors2.0, + errors2.meta, + errors2.links)) + case (.errors, .data): + return .dataErrorMismatch(errorOnLeft: true) + case (.data, .errors): + return .dataErrorMismatch(errorOnLeft: false) + case (.data(let data1), .data(let data2)): + return .differentData(data1.compare(to: data2)) + } + } +} diff --git a/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift b/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift new file mode 100644 index 0000000..392bab0 --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift @@ -0,0 +1,167 @@ +// +// DocumentDataCompare.swift +// +// +// Created by Mathew Polzin on 11/5/19. +// + +import JSONAPI + +public struct DocumentDataComparison: Equatable, PropertyComparable { + public let primary: PrimaryResourceBodyComparison + public let includes: IncludesComparison + public let meta: Comparison + public let links: Comparison + + init(primary: PrimaryResourceBodyComparison, includes: IncludesComparison, meta: Comparison, links: Comparison) { + self.primary = primary + self.includes = includes + self.meta = meta + self.links = links + } + + public var differences: NamedDifferences { + return Dictionary( + [ + !primary.isSame ? ("Primary Resource", primary.rawValue) : nil, + !includes.isSame ? ("Includes", includes.rawValue) : nil, + !meta.isSame ? ("Meta", meta.rawValue) : nil, + !links.isSame ? ("Links", links.rawValue) : nil + ].compactMap { $0 }, + uniquingKeysWith: { $1 } + ) + } +} + +extension Document.Body.Data { + public func compare(to other: Self) -> DocumentDataComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody { + return .init( + primary: primary.compare(to: other.primary), + includes: includes.compare(to: other.includes), + meta: Comparison(meta, other.meta), + links: Comparison(links, other.links) + ) + } + + public func compare(to other: Self) -> DocumentDataComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody { + return .init( + primary: primary.compare(to: other.primary), + includes: includes.compare(to: other.includes), + meta: Comparison(meta, other.meta), + links: Comparison(links, other.links) + ) + } + + public func compare(to other: Self) -> DocumentDataComparison where T: ResourceObjectType, PrimaryResourceBody == ManyResourceBody { + return .init( + primary: primary.compare(to: other.primary), + includes: includes.compare(to: other.includes), + meta: Comparison(meta, other.meta), + links: Comparison(links, other.links) + ) + } +} + +public enum PrimaryResourceBodyComparison: Equatable, CustomStringConvertible { + case single(ResourceObjectComparison) + case many(ManyResourceObjectComparison) + case other(Comparison) + + public var isSame: Bool { + switch self { + case .other(let comparison): + return comparison == .same + case .single(let comparison): + return comparison.isSame + case .many(let comparison): + return comparison.isSame + } + } + + public var description: String { + switch self { + case .other(let comparison): + return comparison.rawValue + case .single(let comparison): + return comparison.rawValue + case .many(let comparison): + return comparison.rawValue + } + } + + public var rawValue: String { return description } +} + +public struct ManyResourceObjectComparison: Equatable, PropertyComparable { + public let comparisons: [ArrayElementComparison] + + public init(_ comparisons: [ArrayElementComparison]) { + self.comparisons = comparisons + } + + public var differences: NamedDifferences { + return comparisons + .enumerated() + .filter { $0.element != .same } + .reduce(into: [String: String]()) { hash, next in + hash["resource \(next.offset + 1)"] = next.element.rawValue + } + } +} + +extension SingleResourceBody where Entity: ResourceObjectType { + public func compare(to other: Self) -> PrimaryResourceBodyComparison { + return .single(.init(value, other.value)) + } +} + +public protocol _OptionalResourceObjectType { + associatedtype Wrapped: ResourceObjectType + + var maybeValue: Wrapped? { get } +} + +extension Optional: _OptionalResourceObjectType where Wrapped: ResourceObjectType { + public var maybeValue: Wrapped? { + switch self { + case .none: + return nil + case .some(let value): + return value + } + } +} + +extension SingleResourceBody where Entity: _OptionalResourceObjectType { + public func compare(to other: Self) -> PrimaryResourceBodyComparison { + guard let one = value.maybeValue, + let two = other.value.maybeValue else { + return .other(Comparison(value, other.value)) + } + return .single(.init(one, two)) + } +} + +extension ManyResourceBody where Entity: ResourceObjectType { + public func compare(to other: Self) -> PrimaryResourceBodyComparison { + return .many(.init(values.compare(to: other.values, using: { r1, r2 in + let r1AsResource = r1 as? AbstractResourceObjectType + + let maybeComparison = r1AsResource + .flatMap { resource in + try? ArrayElementComparison( + resourceObjectComparison: resource.abstractCompare(to: r2) + ) + } + + guard let comparison = maybeComparison else { + return .differentValues( + String(describing: r1), + String(describing: r2) + ) + } + + return comparison + }))) + } +} diff --git a/Sources/JSONAPITesting/Comparisons/IncludesCompare.swift b/Sources/JSONAPITesting/Comparisons/IncludesCompare.swift new file mode 100644 index 0000000..c638628 --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/IncludesCompare.swift @@ -0,0 +1,66 @@ +// +// IncludesCompare.swift +// +// +// Created by Mathew Polzin on 11/4/19. +// + +import JSONAPI +import Poly + +public struct IncludesComparison: Equatable, PropertyComparable { + public let comparisons: [ArrayElementComparison] + + public init(_ comparisons: [ArrayElementComparison]) { + self.comparisons = comparisons + } + + public var differences: NamedDifferences { + return comparisons + .enumerated() + .filter { $0.element != .same } + .reduce(into: [String: String]()) { hash, next in + hash["include \(next.offset + 1)"] = next.element.rawValue + } + } +} + +extension Includes { + public func compare(to other: Self) -> IncludesComparison { + + return IncludesComparison( + values.compare(to: other.values) { thisInclude, otherInclude in + guard thisInclude != otherInclude else { + return .same + } + + let thisWrappedValue = thisInclude.value + let otherWrappedValue = otherInclude.value + guard type(of: thisWrappedValue) == type(of: otherWrappedValue) else { + return .differentTypes( + String(describing: type(of: thisWrappedValue)), + String(describing: type(of: otherWrappedValue)) + ) + } + + let thisAsAResource = thisWrappedValue as? AbstractResourceObjectType + + let maybeComparison = thisAsAResource + .flatMap { resource in + try? ArrayElementComparison( + resourceObjectComparison: resource.abstractCompare(to: otherWrappedValue) + ) + } + + guard let comparison = maybeComparison else { + return .differentValues( + String(describing: thisWrappedValue), + String(describing: otherWrappedValue) + ) + } + + return comparison + } + ) + } +} diff --git a/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift new file mode 100644 index 0000000..c217cb9 --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift @@ -0,0 +1,105 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +import JSONAPI + +extension Relationships { + public func compare(to other: Self) -> [String: Comparison] { + let mirror1 = Mirror(reflecting: self) + let mirror2 = Mirror(reflecting: other) + + var comparisons = [String: Comparison]() + + for child in mirror1.children { + guard let childLabel = child.label else { continue } + + let childDescription = relationshipDescription(of: child.value) + + guard let otherChild = mirror2.children.first(where: { $0.label == childLabel }) else { + comparisons[childLabel] = .different(childDescription, "missing") + continue + } + + if (relationshipsEqual(child.value, otherChild.value)) { + comparisons[childLabel] = .same + } else { + let otherChildDescription = relationshipDescription(of: otherChild.value) + + comparisons[childLabel] = .different(childDescription, otherChildDescription) + } + } + + return comparisons + } +} + +fileprivate func relationshipsEqual(_ one: Any, _ two: Any) -> Bool { + guard let attr = one as? AbstractRelationship else { + return false + } + + return attr.equals(two) +} + +fileprivate func relationshipDescription(of thing: Any) -> String { + return (thing as? AbstractRelationship)?.abstractDescription ?? String(describing: thing) +} + +protocol AbstractRelationship { + var abstractDescription: String { get } + + func equals(_ other: Any) -> Bool +} + +extension ToOneRelationship: AbstractRelationship { + var abstractDescription: String { + if meta is NoMetadata && links is NoLinks { + return String(describing: id) + } + + return String(describing: + ( + String(describing: id), + String(describing: meta), + String(describing: links) + ) + ) + } + + func equals(_ other: Any) -> Bool { + guard let attributeB = other as? Self else { + return false + } + return abstractDescription == attributeB.abstractDescription + } +} + +extension ToManyRelationship: AbstractRelationship { + var abstractDescription: String { + + let idsString = ids.map { String.init(describing: $0.rawValue) }.joined(separator: ", ") + + if meta is NoMetadata && links is NoLinks { + return idsString + } + + return String(describing: + ( + idsString, + String(describing: meta), + String(describing: links) + ) + ) + } + + func equals(_ other: Any) -> Bool { + guard let attributeB = other as? Self else { + return false + } + return abstractDescription == attributeB.abstractDescription + } +} diff --git a/Sources/JSONAPITesting/Comparisons/ResourceObjectCompare.swift b/Sources/JSONAPITesting/Comparisons/ResourceObjectCompare.swift new file mode 100644 index 0000000..2619008 --- /dev/null +++ b/Sources/JSONAPITesting/Comparisons/ResourceObjectCompare.swift @@ -0,0 +1,71 @@ +// +// ResourceObjectCompare.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +import JSONAPI + +public struct ResourceObjectComparison: Equatable, PropertyComparable { + public typealias ComparisonHash = [String: Comparison] + + public let id: Comparison + public let attributes: ComparisonHash + public let relationships: ComparisonHash + public let meta: Comparison + public let links: Comparison + + public init(_ one: T, _ two: T) { + id = Comparison(one.id.rawValue, two.id.rawValue) + attributes = one.attributes.compare(to: two.attributes) + relationships = one.relationships.compare(to: two.relationships) + meta = Comparison(one.meta, two.meta) + links = Comparison(one.links, two.links) + } + + public var differences: NamedDifferences { + return attributes.reduce(into: ComparisonHash()) { hash, next in + hash["'\(next.key)' attribute"] = next.value + } + .merging( + relationships.reduce(into: ComparisonHash()) { hash, next in + hash["'\(next.key)' relationship"] = next.value + }, + uniquingKeysWith: { $1 } + ) + .merging( + [ + "id": id, + "meta": meta, + "links": links + ], + uniquingKeysWith: { $1 } + ) + .filter { $1 != .same } + .mapValues { $0.rawValue } + } +} + +extension ResourceObjectType { + public func compare(to other: Self) -> ResourceObjectComparison { + return ResourceObjectComparison(self, other) + } +} + +protocol AbstractResourceObjectType { + func abstractCompare(to other: Any) throws -> ResourceObjectComparison +} + +enum AbstractCompareError: Swift.Error { + case typeMismatch +} + +extension ResourceObject: AbstractResourceObjectType { + func abstractCompare(to other: Any) throws -> ResourceObjectComparison { + guard let otherResource = other as? Self else { + throw AbstractCompareError.typeMismatch + } + return self.compare(to: otherResource) + } +} diff --git a/Sources/JSONAPITesting/Optional+ZipWith.swift b/Sources/JSONAPITesting/Optional+ZipWith.swift new file mode 100644 index 0000000..aacbb7b --- /dev/null +++ b/Sources/JSONAPITesting/Optional+ZipWith.swift @@ -0,0 +1,12 @@ +// +// Optional+ZipWith.swift +// +// Created by Mathew Polzin on 1/19/19. +// + +/// Zip two optionals together with the given operation performed on +/// the unwrapped contents. If either optional is nil, the zip +/// yields nil. +func zip(_ left: X?, _ right: Y?, with fn: (X, Y) -> Z) -> Z? { + return left.flatMap { lft in right.map { rght in fn(lft, rght) }} +} diff --git a/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift new file mode 100644 index 0000000..e7a7dca --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift @@ -0,0 +1,74 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +import XCTest +import JSONAPI +import JSONAPITesting + +final class AttributesCompareTests: XCTestCase { + func test_sameAttributes() { + let attr1 = TestAttributes( + string: "hello world", + int: 10, + bool: true, + double: 105.4, + struct: .init(value: .init()) + ) + let attr2 = attr1 + + XCTAssertEqual(attr1.compare(to: attr2), [ + "string": .same, + "int": .same, + "bool": .same, + "double": .same, + "struct": .same + ]) + } + + func test_differentAttributes() { + let attr1 = TestAttributes( + string: "hello world", + int: 10, + bool: true, + double: 105.4, + struct: .init(value: .init()) + ) + let attr2 = TestAttributes( + string: "hello", + int: 11, + bool: false, + double: 1.4, + struct: .init(value: .init(val: "there")) + ) + + XCTAssertEqual(attr1.compare(to: attr2), [ + "string": .different("hello world", "hello"), + "int": .different("10", "11"), + "bool": .different("true", "false"), + "double": .different("105.4", "1.4"), + "struct": .different("string: hello", "string: there") + ]) + } +} + +private struct TestAttributes: JSONAPI.Attributes { + let string: Attribute + let int: Attribute + let bool: Attribute + let double: Attribute + let `struct`: Attribute + + struct Struct: Equatable, Codable, CustomStringConvertible { + let string: String + + init(val: String = "hello") { + self.string = val + } + + var description: String { return "string: \(string)" } + } +} diff --git a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift new file mode 100644 index 0000000..b2e14af --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift @@ -0,0 +1,170 @@ +// +// DocumentCompareTests.swift +// +// +// Created by Mathew Polzin on 11/4/19. +// + +import XCTest +import JSONAPI +import JSONAPITesting + +final class DocumentCompareTests: XCTestCase { + func test_same() { + XCTAssertTrue(d1.compare(to: d1).differences.isEmpty) + XCTAssertTrue(d2.compare(to: d2).differences.isEmpty) + XCTAssertTrue(d3.compare(to: d3).differences.isEmpty) + XCTAssertTrue(d4.compare(to: d4).differences.isEmpty) + } + + func test_errorAndData() { + XCTAssertEqual(d1.compare(to: d2).differences, [ + "Body": "data response ≠ error response" + ]) + + XCTAssertEqual(d2.compare(to: d1).differences, [ + "Body": "error response ≠ data response" + ]) + } + + func test_differentErrors() { + XCTAssertEqual(d2.compare(to: d4).differences, [ + "Body": "status: 500, title: Internal Error ≠ status: 404, title: Not Found" + ]) + } + + func test_differentData() { + XCTAssertEqual(d3.compare(to: d5).differences, [ + "Body": "(Includes: (include 2: missing)), (Primary Resource: (resource 2: missing))" + ]) + + XCTAssertEqual(d3.compare(to: d6).differences, [ + "Body": ##"(Includes: (include 2: missing)), (Primary Resource: (resource 2: 'age' attribute: 10 ≠ 12, 'bestFriend' relationship: Optional(Id(2)) ≠ nil, 'favoriteColor' attribute: nil ≠ Optional("blue"), 'name' attribute: name ≠ Fig, id: 1 ≠ 5), (resource 3: missing))"## + ]) + } +} + +fileprivate enum TestDescription: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "test_type" + + struct Attributes: JSONAPI.Attributes { + let name: Attribute + let age: Attribute + let favoriteColor: Attribute + } + + struct Relationships: JSONAPI.Relationships { + let bestFriend: ToOneRelationship + let parents: ToManyRelationship + } +} + +fileprivate typealias TestType = ResourceObject + +fileprivate enum TestDescription2: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "test_type2" + + struct Attributes: JSONAPI.Attributes { + let name: Attribute + let age: Attribute + let favoriteColor: Attribute + } + + struct Relationships: JSONAPI.Relationships { + let bestFriend: ToOneRelationship + let parents: ToManyRelationship + } +} + +fileprivate typealias TestType2 = ResourceObject + +fileprivate typealias SingleDocument = JSONAPI.Document, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> + +fileprivate typealias ManyDocument = JSONAPI.Document, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> + +fileprivate let r1 = TestType( + id: "1", + attributes: .init( + name: "name", + age: 10, + favoriteColor: nil + ), + relationships: .init( + bestFriend: "2", + parents: ["3", "4"] + ), + meta: .none, + links: .none +) + +fileprivate let r2 = TestType( + id: "5", + attributes: .init( + name: "Fig", + age: 12, + favoriteColor: "blue" + ), + relationships: .init( + bestFriend: nil, + parents: ["3", "4"] + ), + meta: .none, + links: .none +) + +fileprivate let r3 = TestType2( + id: "2", + attributes: .init( + name: "Tully", + age: 100, + favoriteColor: "red" + ), + relationships: .init( + bestFriend: nil, + parents: [] + ), + meta: .none, + links: .none +) + +fileprivate let d1 = SingleDocument( + apiDescription: .none, + body: .init(resourceObject: r1), + includes: .none, + meta: .none, + links: .none +) + +fileprivate let d2 = SingleDocument( + apiDescription: .none, + errors: [.error(.init(id: nil, status: "500", title: "Internal Error"))] +) + +fileprivate let d3 = ManyDocument( + apiDescription: .none, + body: .init(resourceObjects: [r1, r2]), + includes: .init(values: [.init(r3)]), + meta: .none, + links: .none +) + +fileprivate let d4 = SingleDocument( + apiDescription: .none, + errors: [.error(.init(id: nil, status: "404", title: "Not Found"))] +) + +fileprivate let d5 = ManyDocument( + apiDescription: .none, + body: .init(resourceObjects: [r1]), + includes: .init(values: [.init(r3), .init(r2)]), + meta: .none, + links: .none +) + +fileprivate let d6 = ManyDocument( + apiDescription: .none, + body: .init(resourceObjects: [r1, r1, r2]), + includes: .init(values: [.init(r3), .init(r2)]), + meta: .none, + links: .none +) diff --git a/Tests/JSONAPITestingTests/Comparisons/IncludesCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/IncludesCompareTests.swift new file mode 100644 index 0000000..7d34521 --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/IncludesCompareTests.swift @@ -0,0 +1,239 @@ +// +// IncludeCompareTests.swift +// +// +// Created by Mathew Polzin on 11/4/19. +// + +import XCTest +import JSONAPI +import JSONAPITesting +import Poly + +final class IncludesCompareTests: XCTestCase { + func test_same() { + let includes1 = Includes(values: justTypeOnes) + let includes2 = Includes(values: justTypeOnes) + XCTAssertTrue(includes1.compare(to: includes2).differences.isEmpty) + + let includes3 = Includes(values: longerTypeOnes) + let includes4 = Includes(values: longerTypeOnes) + XCTAssertTrue(includes3.compare(to: includes4).differences.isEmpty) + + let includes5 = Includes(values: onesAndTwos) + let includes6 = Includes(values: onesAndTwos) + XCTAssertTrue(includes5.compare(to: includes6).differences.isEmpty) + } + + func test_missing() { + let includes1 = Includes(values: justTypeOnes) + let includes2 = Includes(values: longerTypeOnes) + XCTAssertEqual(includes1.compare(to: includes2).differences, ["include 3": "missing"]) + XCTAssertEqual(includes2.compare(to: includes1).differences, ["include 3": "missing"]) + } + + func test_typeMismatch() { + let includes1 = Includes(values: onesAndTwos) + let includes2 = Includes(values: justTypeOnes) + XCTAssertEqual(includes1.compare(to: includes2).differences, ["include 2": "ResourceObject ≠ ResourceObject"]) + XCTAssertEqual(includes2.compare(to: includes1).differences, ["include 2": "ResourceObject ≠ ResourceObject"]) + } + + func test_valueMismatch() { + let includes1 = Includes(values: onesAndTwos) + let includes2 = Includes(values: differentOnesAndTwos) + XCTAssertEqual(includes1.compare(to: includes2).differences, [ + "include 1": #"'favoriteColor' attribute: Optional("red") ≠ nil, 'name' attribute: Matt ≠ Todd, 'parents' relationship: 4, 5 ≠ 7, 8, id: 1 ≠ 2"# + ]) + } + + fileprivate let justTypeOnes: [Poly2] = [ + .a( + TestType1( + id: "1", + attributes: .init( + name: "Matt", + age: 23, + favoriteColor: "red" + ), + relationships: .init( + bestFriend: "3", + parents: ["4", "5"] + ), + meta: .none, + links: .none + ) + ), + .a( + TestType1( + id: "3", + attributes: .init( + name: "Helen", + age: 24, + favoriteColor: nil + ), + relationships: .init( + bestFriend: nil, + parents: ["2"] + ), + meta: .none, + links: .none + ) + ) + ] + + fileprivate let longerTypeOnes: [Poly2] = [ + .a( + TestType1( + id: "1", + attributes: .init( + name: "Matt", + age: 23, + favoriteColor: "red" + ), + relationships: .init( + bestFriend: "3", + parents: ["4", "5"] + ), + meta: .none, + links: .none + ) + ), + .a( + TestType1( + id: "3", + attributes: .init( + name: "Helen", + age: 24, + favoriteColor: nil + ), + relationships: .init( + bestFriend: nil, + parents: ["2"] + ), + meta: .none, + links: .none + ) + ), + .a( + TestType1( + id: "2", + attributes: .init( + name: "Troy", + age: 45, + favoriteColor: "blue" + ), + relationships: .init( + bestFriend: nil, + parents: [] + ), + meta: .none, + links: .none + ) + ) + ] + + fileprivate let onesAndTwos: [Poly2] = [ + .a( + TestType1( + id: "1", + attributes: .init( + name: "Matt", + age: 23, + favoriteColor: "red" + ), + relationships: .init( + bestFriend: "3", + parents: ["4", "5"] + ), + meta: .none, + links: .none + ) + ), + .b( + TestType2( + id: "1", + attributes: .init( + name: "Lucy", + age: 33, + favoriteColor: nil + ), + relationships: .init( + bestFriend: nil, + parents: [] + ), + meta: .none, + links: .none + ) + ) + ] + + fileprivate let differentOnesAndTwos: [Poly2] = [ + .a( + TestType1( + id: "2", + attributes: .init( + name: "Todd", + age: 23, + favoriteColor: nil + ), + relationships: .init( + bestFriend: "3", + parents: ["7", "8"] + ), + meta: .none, + links: .none + ) + ), + .b( + TestType2( + id: "1", + attributes: .init( + name: "Lucy", + age: 33, + favoriteColor: nil + ), + relationships: .init( + bestFriend: nil, + parents: [] + ), + meta: .none, + links: .none + ) + ) + ] +} + +private enum TestDescription1: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "test_type1" + + struct Attributes: JSONAPI.Attributes { + let name: Attribute + let age: Attribute + let favoriteColor: Attribute + } + + struct Relationships: JSONAPI.Relationships { + let bestFriend: ToOneRelationship + let parents: ToManyRelationship + } +} + +private typealias TestType1 = ResourceObject + +private enum TestDescription2: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "test_type2" + + struct Attributes: JSONAPI.Attributes { + let name: Attribute + let age: Attribute + let favoriteColor: Attribute + } + + struct Relationships: JSONAPI.Relationships { + let bestFriend: ToOneRelationship + let parents: ToManyRelationship + } +} + +private typealias TestType2 = ResourceObject diff --git a/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift new file mode 100644 index 0000000..cdb7fda --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift @@ -0,0 +1,14 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/5/19. +// + +import XCTest +import JSONAPI +import JSONAPITesting + +final class RelationshipsCompareTests: XCTestCase { + // TODO: write tests +} diff --git a/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift new file mode 100644 index 0000000..2c40e87 --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift @@ -0,0 +1,68 @@ +// +// File.swift +// +// +// Created by Mathew Polzin on 11/3/19. +// + +import XCTest +import JSONAPI +import JSONAPITesting + +final class ResourceObjectCompareTests: XCTestCase { + func test_same() { + print(test1.compare(to: test1).differences) + XCTAssertTrue(test1.compare(to: test1).differences.isEmpty) + XCTAssertTrue(test2.compare(to: test2).differences.isEmpty) + } + + func test_different() { + // TODO: write actual test + print(test1.compare(to: test2).differences.map { "\($0): \($1)" }.joined(separator: ", ")) + } + + fileprivate let test1 = TestType( + id: "2", + attributes: .init( + name: "James", + age: 12, + favoriteColor: "red"), + relationships: .init( + bestFriend: "3", + parents: ["4", "5"] + ), + meta: .none, + links: .none + ) + + fileprivate let test2 = TestType( + id: "3", + attributes: .init( + name: "Fred", + age: 10, + favoriteColor: .init(value: nil)), + relationships: .init( + bestFriend: nil, + parents: ["1"] + ), + meta: .none, + links: .none + ) +} + +private enum TestDescription: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "test_type" + + struct Attributes: JSONAPI.Attributes { + let name: Attribute + let age: Attribute + let favoriteColor: Attribute + } + + struct Relationships: JSONAPI.Relationships { + let bestFriend: ToOneRelationship + let parents: ToManyRelationship + } +} + +private typealias TestType = ResourceObject From 0fe5c53ada020b2709825be93a7c2cf58cec3d2a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 5 Nov 2019 19:04:47 -0800 Subject: [PATCH 2/3] generate linuxmain --- .../JSONAPITestingTests/XCTestManifests.swift | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Tests/JSONAPITestingTests/XCTestManifests.swift b/Tests/JSONAPITestingTests/XCTestManifests.swift index c916df3..b1b07fb 100644 --- a/Tests/JSONAPITestingTests/XCTestManifests.swift +++ b/Tests/JSONAPITestingTests/XCTestManifests.swift @@ -35,6 +35,28 @@ extension Attribute_LiteralTests { ] } +extension AttributesCompareTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__AttributesCompareTests = [ + ("test_differentAttributes", test_differentAttributes), + ("test_sameAttributes", test_sameAttributes), + ] +} + +extension DocumentCompareTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__DocumentCompareTests = [ + ("test_differentData", test_differentData), + ("test_differentErrors", test_differentErrors), + ("test_errorAndData", test_errorAndData), + ("test_same", test_same), + ] +} + extension EntityCheckTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -58,6 +80,18 @@ extension Id_LiteralTests { ] } +extension IncludesCompareTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__IncludesCompareTests = [ + ("test_missing", test_missing), + ("test_same", test_same), + ("test_typeMismatch", test_typeMismatch), + ("test_valueMismatch", test_valueMismatch), + ] +} + extension Relationship_LiteralTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -69,12 +103,26 @@ extension Relationship_LiteralTests { ] } +extension ResourceObjectCompareTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ResourceObjectCompareTests = [ + ("test_different", test_different), + ("test_same", test_same), + ] +} + public func __allTests() -> [XCTestCaseEntry] { return [ testCase(Attribute_LiteralTests.__allTests__Attribute_LiteralTests), + testCase(AttributesCompareTests.__allTests__AttributesCompareTests), + testCase(DocumentCompareTests.__allTests__DocumentCompareTests), testCase(EntityCheckTests.__allTests__EntityCheckTests), testCase(Id_LiteralTests.__allTests__Id_LiteralTests), + testCase(IncludesCompareTests.__allTests__IncludesCompareTests), testCase(Relationship_LiteralTests.__allTests__Relationship_LiteralTests), + testCase(ResourceObjectCompareTests.__allTests__ResourceObjectCompareTests), ] } #endif From adcc6bfb108d661f6af658c25eb36a4f1bf33266 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 5 Nov 2019 22:43:40 -0800 Subject: [PATCH 3/3] fix error after merge --- Sources/JSONAPI/Document/Document.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/JSONAPI/Document/Document.swift b/Sources/JSONAPI/Document/Document.swift index 08c5d9b..bfe3268 100644 --- a/Sources/JSONAPI/Document/Document.swift +++ b/Sources/JSONAPI/Document/Document.swift @@ -441,7 +441,6 @@ extension Document { public typealias BodyData = Document.BodyData public var body: Document.Body { return document.body } - public var apiDescription: APIDescription { return document.apiDescription } private let document: Document @@ -489,7 +488,6 @@ extension Document { public typealias BodyData = Document.BodyData public var body: Document.Body { return document.body } - public var apiDescription: APIDescription { return document.apiDescription } private let document: Document