Compare commits

...

15 Commits

Author SHA1 Message Date
Mathew Polzin f41521e33b Update example indentation 2019-11-16 00:06:49 -08:00
Mathew Polzin 45ec7ba753 Update SwiftPM package manifest example for version 3 2019-11-16 00:03:46 -08:00
Mathew Polzin 6ab4237c97 update podspec 2019-11-15 23:58:01 -08:00
Mathew Polzin 75711648a4 more minor documentation fixes 2019-11-15 23:48:49 -08:00
Mathew Polzin 2b4209ccb1 A couple more clarifications/corrections 2019-11-15 23:41:12 -08:00
Mathew Polzin ebd11df104 Merge pull request #54 from mattpolzin/beta/3x
Beta/3x
2019-11-15 23:30:01 -08:00
Mathew Polzin 96da1b4e21 update documentation 2019-11-15 23:15:32 -08:00
Mathew Polzin c7696d83fa update Playground pages to run 2019-11-15 17:46:53 -08:00
Mathew Polzin 8ee04d8932 generate linuxmain 2019-11-15 17:38:04 -08:00
Mathew Polzin 4a7a14b1b0 Add test coverage for resource object compare(to:) 2019-11-15 17:36:42 -08:00
Mathew Polzin a6b7d7a94a woops, abstract wrapper protocol landed in wrong module by accident 2019-11-15 17:05:45 -08:00
Mathew Polzin 1010489a02 compare(to:) bug fixes and test additions 2019-11-15 16:59:00 -08:00
Mathew Polzin ae855c85ee going through and fleshing out tests. minor adjustments and bug fixes. 2019-11-15 08:30:17 -08:00
Mathew Polzin 440b649577 Merge pull request #49 from mattpolzin/alpha/3x
Alpha/3x
2019-11-12 18:38:31 -08:00
Mathew Polzin 54551617b4 Add more errors for the resource object type property 2019-11-12 18:34:34 -08:00
38 changed files with 1927 additions and 753 deletions
@@ -37,7 +37,7 @@ typealias ToManyRelationship<Entity: Relatable> = JSONAPI.ToManyRelationship<Ent
// JSON:API Documents for this particular API to have Metadata, Links,
// useful Errors, or an APIDescription (The *SPEC* calls this
// "API Description" the "JSON:API Object").
typealias Document<PrimaryResourceBody: JSONAPI.ResourceBody, IncludeType: JSONAPI.Include> = JSONAPI.Document<PrimaryResourceBody, NoMetadata, NoLinks, IncludeType, NoAPIDescription, BasicJSONAPIError<String>>
typealias Document<PrimaryResourceBody: JSONAPI.CodableResourceBody, IncludeType: JSONAPI.Include> = JSONAPI.Document<PrimaryResourceBody, NoMetadata, NoLinks, IncludeType, NoAPIDescription, BasicJSONAPIError<String>>
// MARK: Entity Definitions
@@ -64,11 +64,11 @@ if case let .data(bodyData) = peopleResponse.body {
// MARK: - Work in the abstract
print("-----")
func process<T: JSONAPIDocument>(document: T) {
guard case let .data(body) = document.body else {
func process<T: CodableJSONAPIDocument>(document: T) {
guard let body = document.body.data else {
return
}
let x: T.Body.Data = body
let x: T.BodyData = body
}
process(document: peopleResponse)
+2 -2
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |spec|
#
spec.name = "MP-JSONAPI"
spec.version = "2.5.0"
spec.version = "3.0.0"
spec.summary = "Swift Codable JSON API framework."
# This description is used to generate tags and improve search results.
@@ -136,6 +136,6 @@ See the JSON API Spec here: https://jsonapi.org/format/
# spec.requires_arc = true
# spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
spec.dependency "Poly", "~> 2.2"
spec.dependency "Poly", "~> 2.3.1"
end
+60 -638
View File
File diff suppressed because it is too large Load Diff
+13 -7
View File
@@ -408,8 +408,6 @@ extension Document: Decodable, CodableJSONAPIDocument where PrimaryResourceBody:
throw DocumentDecodingError(error)
}
// TODO come back to this and make robust
guard let metaVal = meta else {
throw JSONAPICodingError.missingOrMalformedMetadata(path: decoder.codingPath)
}
@@ -494,6 +492,10 @@ extension Document {
public static func ==(lhs: Document, rhs: ErrorDocument) -> Bool {
return lhs == rhs.document
}
public static func ==(lhs: ErrorDocument, rhs: Document) -> Bool {
return lhs.document == rhs
}
}
/// A Document that only supports success bodies. This is useful if you wish to pass around a
@@ -534,7 +536,7 @@ extension Document {
/// `nil` if the Document is an error response. Otherwise,
/// a structure containing the primary resource, any included
/// resources, metadata, and links.
var data: BodyData? {
public var data: BodyData? {
return document.body.data
}
@@ -545,7 +547,7 @@ extension Document {
/// resources dependening on the `PrimaryResourceBody` type.
///
/// See `SingleResourceBody` and `ManyResourceBody`.
var primaryResource: PrimaryResourceBody? {
public var primaryResource: PrimaryResourceBody? {
return document.body.primaryResource
}
@@ -553,25 +555,29 @@ extension Document {
///
/// `nil` if the Document is an error document. Otherwise,
/// zero or more includes.
var includes: Includes<IncludeType>? {
public var includes: Includes<IncludeType>? {
return document.body.includes
}
/// The metadata for the error or data document or `nil` if
/// no metadata is found.
var meta: MetaType? {
public var meta: MetaType? {
return document.body.meta
}
/// The links for the error or data document or `nil` if
/// no links are found.
var links: LinksType? {
public var links: LinksType? {
return document.body.links
}
public static func ==(lhs: Document, rhs: SuccessDocument) -> Bool {
return lhs == rhs.document
}
public static func ==(lhs: SuccessDocument, rhs: Document) -> Bool {
return lhs.document == rhs
}
}
}
@@ -40,14 +40,12 @@ public enum DocumentDecodingError: Swift.Error, Equatable {
private enum Location: Equatable {
case data
case other
init(_ context: DecodingError.Context) {
if context.codingPath.contains(where: { $0.stringValue == "data" }) {
self = .data
} else {
self = .other
init?(_ context: DecodingError.Context) {
guard context.codingPath.contains(where: { $0.stringValue == "data" }) else {
return nil
}
self = .data
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
// Created by Mathew Polzin on 11/24/18.
//
/// A Links structure should contain nothing but JSONAPI.Link properties.
/// A Links structure should contain nothing but `JSONAPI.Link` properties.
public protocol Links: Codable, Equatable {}
/// Use NoLinks where no links should belong to a JSON API component
@@ -414,7 +414,13 @@ public extension ResourceObject {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResourceObjectCodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
let type: String
do {
type = try container.decode(String.self, forKey: .type)
} catch let error as DecodingError {
throw ResourceObjectDecodingError(error)
?? error
}
guard ResourceObject.jsonType == type else {
throw ResourceObjectDecodingError(
@@ -102,13 +102,18 @@ public struct ResourceObjectDecodingError: Swift.Error, Equatable {
extension ResourceObjectDecodingError: CustomStringConvertible {
public var description: String {
switch cause {
case .keyNotFound where subjectName == ResourceObjectDecodingError.entireObject:
return "\(location) object is required and missing."
case .keyNotFound where location == .type:
return "'type' (a.k.a. JSON:API type name) is required and missing."
case .keyNotFound:
if subjectName == ResourceObjectDecodingError.entireObject {
return "\(location) object is required and missing."
}
return "'\(subjectName)' \(location.singular) is required and missing."
case .valueNotFound where location == .type:
return "'\(location.singular)' (a.k.a. JSON:API type name) is not nullable but null was found."
case .valueNotFound:
return "'\(subjectName)' \(location.singular) is not nullable but null."
return "'\(subjectName)' \(location.singular) is not nullable but null was found."
case .typeMismatch(expectedTypeName: let expected) where location == .type:
return "'\(location.singular)' (a.k.a. the JSON:API type name) is not a \(expected) as expected."
case .typeMismatch(expectedTypeName: let expected):
return "'\(subjectName)' \(location.singular) is not a \(expected) as expected."
case .jsonTypeMismatch(expectedType: let expected, foundType: let found) where location == .type:
@@ -158,7 +158,7 @@ struct SparseFieldKeyedEncodingContainer<Key, SparseKey>: KeyedEncodingContainer
forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
guard shouldAllow(key: key) else {
return KeyedEncodingContainer(
// TODO: not needed by JSONAPI library, but for completeness could
// NOTE: not needed by JSONAPI library, but for completeness could
// add an EmptyObjectEncoder that can be returned here so that
// at least nothing gets encoded within the nested container
SparseFieldKeyedEncodingContainer<NestedKey, SparseKey>(wrapping: wrappedContainer.nestedContainer(keyedBy: keyType,
@@ -176,7 +176,7 @@ struct SparseFieldKeyedEncodingContainer<Key, SparseKey>: KeyedEncodingContainer
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
guard shouldAllow(key: key) else {
// TODO: not needed by JSONAPI library, but for completeness could
// NOTE: not needed by JSONAPI library, but for completeness could
// add an EmptyObjectEncoder that can be returned here so that
// at least nothing gets encoded within the nested container
return wrappedContainer.nestedUnkeyedContainer(forKey: key)
@@ -14,17 +14,6 @@ public enum ArrayElementComparison: Equatable, CustomStringConvertible {
case differentValues(String, String)
case prebuilt(String)
public init(sameTypeComparison: BasicComparison) {
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
@@ -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) }
@@ -33,10 +33,10 @@ public enum BodyComparison: Equatable, CustomStringConvertible {
case differentErrors(ErrorComparison)
case differentData(DocumentDataComparison)
public typealias ErrorComparison = [BasicComparison]
public typealias ErrorComparison = [String: BasicComparison]
static func compare<E: JSONAPIError, M: JSONAPI.Meta, L: JSONAPI.Links>(errors errors1: [E], _ meta1: M?, _ links1: L?, with errors2: [E], _ meta2: M?, _ links2: L?) -> ErrorComparison {
return errors1.compare(
let errorComparisons = errors1.compare(
to: errors2,
using: { error1, error2 in
guard error1 != error2 else {
@@ -48,9 +48,19 @@ public enum BodyComparison: Equatable, CustomStringConvertible {
String(describing: error2)
)
}
).map(BasicComparison.init) + [
BasicComparison(meta1, meta2),
BasicComparison(links1, links2)
).map(BasicComparison.init)
.filter { !$0.isSame }
.map { $0.rawValue }
.joined(separator: ", ")
let errorComparisonString = errorComparisons.isEmpty
? nil
: errorComparisons
return [
"Errors": errorComparisonString.map { BasicComparison.prebuilt("(\($0))") } ?? .same,
"Metadata": BasicComparison(meta1, meta2),
"Links": BasicComparison(links1, links2)
]
}
@@ -67,8 +77,8 @@ public enum BodyComparison: Equatable, CustomStringConvertible {
return "\(left) ≠ \(right)"
case .differentErrors(let comparisons):
return comparisons
.filter { !$0.isSame }
.map { $0.rawValue }
.filter { !$0.value.isSame }
.map { "\($0.key): \($0.value.rawValue)" }
.sorted()
.joined(separator: ", ")
case .differentData(let comparison):
@@ -104,7 +114,7 @@ extension DocumentBody where Self: Equatable, PrimaryResourceBody: TestableResou
return .differentErrors(
BodyComparison.compare(
errors: errors1, meta, links,
with: errors2, meta, links
with: errors2, other.meta, other.links
)
)
}
@@ -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
// RelationshipsCompare.swift
//
//
// Created by Mathew Polzin on 11/3/19.
@@ -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 {
@@ -0,0 +1,86 @@
//
// ArrayCompareTests.swift
// JSONAPITestingTests
//
// Created by Mathew Polzin on 11/14/19.
//
import XCTest
@testable import JSONAPITesting
final class ArrayCompareTests: XCTestCase {
func test_same() {
let arr1 = ["a", "b", "c"]
let arr2 = ["a", "b", "c"]
let comparison = arr1.compare(to: arr2) { str1, str2 in
str1 == str2 ? .same : .differentValues(str1, str2)
}
XCTAssertEqual(
comparison,
[.same, .same, .same]
)
XCTAssertEqual(comparison.map { $0.description }, ["same", "same", "same"])
XCTAssertEqual(comparison.map(BasicComparison.init(reducing:)), [.same, .same, .same])
XCTAssertEqual(comparison.map(BasicComparison.init(reducing:)).map { $0.description }, ["same", "same", "same"])
}
func test_differentLengths() {
let arr1 = ["a", "b", "c"]
let arr2 = ["a", "b"]
let comparison1 = arr1.compare(to: arr2) { str1, str2 in
str1 == str2 ? .same : .differentValues(str1, str2)
}
XCTAssertEqual(
comparison1,
[.same, .same, .missing]
)
XCTAssertEqual(comparison1.map { $0.description }, ["same", "same", "missing"])
XCTAssertEqual(comparison1.map(BasicComparison.init(reducing:)), [.same, .same, .different("array length 1", "array length 2")])
let comparison2 = arr2.compare(to: arr1) { str1, str2 in
str1 == str2 ? .same : .differentValues(str1, str2)
}
XCTAssertEqual(
comparison2,
[.same, .same, .missing]
)
XCTAssertEqual(comparison2.map { $0.description }, ["same", "same", "missing"])
XCTAssertEqual(comparison2.map(BasicComparison.init(reducing:)), [.same, .same, .different("array length 1", "array length 2")])
}
func test_differentValues() {
let arr1 = ["c", "b", "a"]
let arr2 = ["a", "b", "c"]
let comparison = arr1.compare(to: arr2) { str1, str2 in
str1 == str2 ? .same : .differentValues(str1, str2)
}
XCTAssertEqual(
comparison,
[.differentValues("c", "a"), .same, .differentValues("a", "c")]
)
XCTAssertEqual(comparison.map { $0.description }, ["c ≠ a", "same", "a ≠ c"])
}
func test_reducePrebuilt() {
let prebuilt = ArrayElementComparison.prebuilt("hello world")
XCTAssertEqual(BasicComparison(reducing: prebuilt), .prebuilt("hello world"))
XCTAssertEqual(BasicComparison(reducing: prebuilt).description, "hello world")
}
}
@@ -1,5 +1,5 @@
//
// File.swift
// AttributesCompareTests.swift
//
//
// Created by Mathew Polzin on 11/3/19.
@@ -10,13 +10,16 @@ import JSONAPI
import JSONAPITesting
final class AttributesCompareTests: XCTestCase {
func test_sameAttributes() {
func test_sameAttributes() throws {
let attr1 = TestAttributes(
string: "hello world",
int: 10,
bool: true,
double: 105.4,
struct: .init(value: .init())
struct: .init(value: .init()),
transformed: try .init(rawValue: 10),
optional: .init(value: 20),
optionalTransformed: try .init(rawValue: 10)
)
let attr2 = attr1
@@ -25,24 +28,33 @@ final class AttributesCompareTests: XCTestCase {
"int": .same,
"bool": .same,
"double": .same,
"struct": .same
"struct": .same,
"transformed": .same,
"optional": .same,
"optionalTransformed": .same
])
}
func test_differentAttributes() {
func test_differentAttributes() throws {
let attr1 = TestAttributes(
string: "hello world",
int: 10,
bool: true,
double: 105.4,
struct: .init(value: .init())
struct: .init(value: .init()),
transformed: try .init(rawValue: 10),
optional: nil,
optionalTransformed: nil
)
let attr2 = TestAttributes(
string: "hello",
int: 11,
bool: false,
double: 1.4,
struct: .init(value: .init(val: "there"))
struct: .init(value: .init(val: "there")),
transformed: try .init(rawValue: 11),
optional: .init(value: 20.5),
optionalTransformed: try .init(rawValue: 10)
)
XCTAssertEqual(attr1.compare(to: attr2), [
@@ -50,7 +62,30 @@ final class AttributesCompareTests: XCTestCase {
"int": .different("10", "11"),
"bool": .different("true", "false"),
"double": .different("105.4", "1.4"),
"struct": .different("string: hello", "string: there")
"struct": .different("string: hello", "string: there"),
"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.")
])
}
}
@@ -61,14 +96,32 @@ private struct TestAttributes: JSONAPI.Attributes {
let bool: Attribute<Bool>
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
}
init(val: String = "hello") {
self.string = val
}
var description: String { return "string: \(string)" }
var description: String { return "string: \(string)" }
}
private enum TestTransformer: Transformer {
static func transform(_ value: Int) throws -> String {
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() {
@@ -34,8 +36,33 @@ final class DocumentCompareTests: XCTestCase {
}
func test_differentErrors() {
XCTAssertEqual(d2.compare(to: d4).differences, [
"Body": "status: 500, title: Internal Error ≠ status: 404, title: Not Found"
let comparison = d2.compare(to: d4)
XCTAssertEqual(comparison.differences, [
"Body": "Errors: (status: 500, title: Internal Error ≠ status: 404, title: Not Found)"
])
XCTAssertEqual(String(describing: comparison), "(Body: Errors: (status: 500, title: Internal Error ≠ status: 404, title: Not Found))")
}
func test_sameErrorsDifferentMetadata() {
let errors = [
BasicJSONAPIError<String>.error(.init(id: nil, status: "500", title: "Internal Error"))
]
let doc1 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "1", meta: .none),
errors: errors,
meta: nil,
links: nil
)
let doc2 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "1", meta: .none),
errors: errors,
meta: .init(total: 11),
links: nil
)
XCTAssertEqual(doc1.compare(to: doc2).differences, [
"Body": "Metadata: nil ≠ Optional(total: 11)"
])
}
@@ -60,6 +87,24 @@ final class DocumentCompareTests: XCTestCase {
"Body": ##"(Primary Resource: (resource 1: 'age' attribute: 10 ≠ 12, 'bestFriend' relationship: Optional(Id(2)) ≠ nil, 'favoriteColor' attribute: nil ≠ Optional("blue"), 'name' attribute: name ≠ Fig, id: 1 ≠ 5))"##
])
}
func test_differentMetadata() {
XCTAssertEqual(d11.compare(to: d12).differences, [
"Body": "(Meta: total: 10 ≠ total: 10000)"
])
}
func test_differentLinks() {
XCTAssertEqual(d11.compare(to: d13).differences, [
"Body": ##"(Links: TestLinks(link: JSONAPI.Link<Swift.String, JSONAPI.NoMetadata>(url: "http://google.com", meta: No Metadata)) ≠ TestLinks(link: JSONAPI.Link<Swift.String, JSONAPI.NoMetadata>(url: "http://yahoo.com", meta: No Metadata)))"##
])
}
func test_differentAPIDescription() {
XCTAssertEqual(d11.compare(to: d14).differences, [
"API Description": ##"APIDescription<NoMetadata>(version: "10", meta: No Metadata) ≠ APIDescription<NoMetadata>(version: "1", meta: No Metadata)"##
])
}
}
fileprivate enum TestDescription: JSONAPI.ResourceObjectDescription {
@@ -98,6 +143,22 @@ fileprivate typealias TestType2 = ResourceObject<TestDescription2, NoMetadata, N
fileprivate typealias SingleDocument = JSONAPI.Document<SingleResourceBody<TestType>, NoMetadata, NoLinks, Include2<TestType, TestType2>, NoAPIDescription, BasicJSONAPIError<String>>
fileprivate struct TestMetadata: JSONAPI.Meta, CustomStringConvertible {
let total: Int
var description: String {
"total: \(total)"
}
}
fileprivate struct TestLinks: JSONAPI.Links {
let link: Link<String, NoMetadata>
}
typealias TestAPIDescription = APIDescription<NoMetadata>
fileprivate typealias SingleDocumentWithMetaAndLinks = JSONAPI.Document<SingleResourceBody<TestType>, TestMetadata, TestLinks, Include2<TestType, TestType2>, TestAPIDescription, BasicJSONAPIError<String>>
fileprivate typealias OptionalSingleDocument = JSONAPI.Document<SingleResourceBody<TestType?>, NoMetadata, NoLinks, Include2<TestType, TestType2>, NoAPIDescription, BasicJSONAPIError<String>>
fileprivate typealias ManyDocument = JSONAPI.Document<ManyResourceBody<TestType>, NoMetadata, NoLinks, Include2<TestType, TestType2>, NoAPIDescription, BasicJSONAPIError<String>>
@@ -220,3 +281,35 @@ fileprivate let d10 = SingleDocument(
meta: .none,
links: .none
)
fileprivate let d11 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "10", meta: .none),
body: .init(resourceObject: r2),
includes: .none,
meta: TestMetadata(total: 10),
links: TestLinks(link: .init(url: "http://google.com"))
)
fileprivate let d12 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "10", meta: .none),
body: .init(resourceObject: r2),
includes: .none,
meta: TestMetadata(total: 10000),
links: TestLinks(link: .init(url: "http://google.com"))
)
fileprivate let d13 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "10", meta: .none),
body: .init(resourceObject: r2),
includes: .none,
meta: TestMetadata(total: 10),
links: TestLinks(link: .init(url: "http://yahoo.com"))
)
fileprivate let d14 = SingleDocumentWithMetaAndLinks(
apiDescription: TestAPIDescription(version: "1", meta: .none),
body: .init(resourceObject: r2),
includes: .none,
meta: TestMetadata(total: 10),
links: TestLinks(link: .init(url: "http://google.com"))
)
@@ -1,5 +1,5 @@
//
// File.swift
// RelationshipCompareTests.swift
//
//
// Created by Mathew Polzin on 11/5/19.
@@ -10,5 +10,176 @@ import JSONAPI
import JSONAPITesting
final class RelationshipsCompareTests: XCTestCase {
// TODO: write tests
func test_same() {
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() {
let r1 = TestRelationships(
a: t1,
b: nil,
c: t3,
d: nil
)
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() {
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() {
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>
}
}
@@ -1,5 +1,5 @@
//
// File.swift
// ResourceObjectCompareTests.swift
//
//
// Created by Mathew Polzin on 11/3/19.
@@ -11,14 +11,72 @@ 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)
XCTAssertTrue(test1_differentId.compare(to: test1_differentId).differences.isEmpty)
XCTAssertTrue(test1_differentAttributes.compare(to: test1_differentAttributes).differences.isEmpty)
}
func test_different() {
// TODO: write actual test
print(test1.compare(to: test2).differences.map { "\($0): \($1)" }.joined(separator: ", "))
func test_differentAttributes() {
XCTAssertEqual(test1.compare(to: test1_differentAttributes).differences, [
"'favoriteColor' attribute": #"Optional("red") ≠ nil"#,
"'name' attribute": "James ≠ Fred",
"'age' attribute": "12 ≠ 10"
])
}
func test_differentRelationships() {
XCTAssertEqual(test1.compare(to: test1_differentRelationships).differences, [
"'parents' relationship": "4, 5 ≠ 3",
"'bestFriend' relationship": "Optional(Id(3)) ≠ nil"
])
}
func test_differentIds() {
XCTAssertEqual(test1.compare(to: test1_differentId).differences, [
"id": "2 ≠ 3"
])
}
func test_differentMetadata() {
let test1 = TestType2(
id: "2",
attributes: .none,
relationships: .none,
meta: .init(total: 10),
links: .init(link: .init(url: "http://google.com"))
)
let test1_differentMeta = TestType2(
id: "2",
attributes: .none,
relationships: .none,
meta: .init(total: 12),
links: .init(link: .init(url: "http://google.com"))
)
XCTAssertEqual(test1.compare(to: test1_differentMeta).differences, [
"meta": "total: 10 ≠ total: 12"
])
}
func test_differentLinks() {
let test1 = TestType2(
id: "2",
attributes: .none,
relationships: .none,
meta: .init(total: 10),
links: .init(link: .init(url: "http://google.com"))
)
let test1_differentLinks = TestType2(
id: "2",
attributes: .none,
relationships: .none,
meta: .init(total: 10),
links: .init(link: .init(url: "http://yahoo.com"))
)
XCTAssertEqual(test1.compare(to: test1_differentLinks).differences, [
"links": "link: http://google.com ≠ link: http://yahoo.com"
])
}
fileprivate let test1 = TestType(
@@ -35,15 +93,43 @@ final class ResourceObjectCompareTests: XCTestCase {
links: .none
)
fileprivate let test2 = TestType(
fileprivate let test1_differentId = TestType(
id: "3",
attributes: .init(
name: "James",
age: 12,
favoriteColor: "red"),
relationships: .init(
bestFriend: "3",
parents: ["4", "5"]
),
meta: .none,
links: .none
)
fileprivate let test1_differentAttributes = TestType(
id: "2",
attributes: .init(
name: "Fred",
age: 10,
favoriteColor: .init(value: nil)),
relationships: .init(
bestFriend: "3",
parents: ["4", "5"]
),
meta: .none,
links: .none
)
fileprivate let test1_differentRelationships = TestType(
id: "2",
attributes: .init(
name: "James",
age: 12,
favoriteColor: "red"),
relationships: .init(
bestFriend: nil,
parents: ["1"]
parents: ["3"]
),
meta: .none,
links: .none
@@ -66,3 +152,29 @@ private enum TestDescription: JSONAPI.ResourceObjectDescription {
}
private typealias TestType = ResourceObject<TestDescription, NoMetadata, NoLinks, String>
private struct TestMetadata: JSONAPI.Meta, CustomStringConvertible {
let total: Int
var description: String {
"total: \(total)"
}
}
private struct TestLinks: JSONAPI.Links, CustomStringConvertible {
let link: Link<String, NoMetadata>
var description: String {
"link: \(link.url)"
}
}
private enum TestDescription2: JSONAPI.ResourceObjectDescription {
static let jsonType: String = "test_type2"
typealias Attributes = NoAttributes
typealias Relationships = NoRelationships
}
private typealias TestType2 = ResourceObject<TestDescription2, TestMetadata, TestLinks, String>

Some files were not shown because too many files have changed in this diff Show More