diff --git a/Sources/JSONAPI/Document/Document.swift b/Sources/JSONAPI/Document/Document.swift index 34c32a4..1343559 100644 --- a/Sources/JSONAPI/Document/Document.swift +++ b/Sources/JSONAPI/Document/Document.swift @@ -494,6 +494,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 +538,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 +549,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 +557,29 @@ extension Document { /// /// `nil` if the Document is an error document. Otherwise, /// zero or more includes. - var includes: Includes? { + public var includes: Includes? { 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 + } } } diff --git a/Sources/JSONAPI/Meta/Links.swift b/Sources/JSONAPI/Meta/Links.swift index 876995f..ab2b7e1 100644 --- a/Sources/JSONAPI/Meta/Links.swift +++ b/Sources/JSONAPI/Meta/Links.swift @@ -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 diff --git a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift index c61e9be..a79f5a1 100644 --- a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift +++ b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift @@ -109,11 +109,11 @@ extension ResourceObjectDecodingError: CustomStringConvertible { case .keyNotFound: return "'\(subjectName)' \(location.singular) is required and missing." case .valueNotFound where location == .type: - return "'type' (a.k.a. JSON:API type name) is not nullable but null was found." + 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 was found." case .typeMismatch(expectedTypeName: let expected) where location == .type: - return "'type' (a.k.a. the JSON:API type name) is not a \(expected) as expected." + 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: diff --git a/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift b/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift index a1f7e5c..f93ab62 100644 --- a/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/ArrayCompare.swift @@ -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 diff --git a/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift b/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift index d56c84d..4cee720 100644 --- a/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/DocumentCompare.swift @@ -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(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 ) ) } diff --git a/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift index 3bfa7d6..0c24386 100644 --- a/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/RelationshipsCompare.swift @@ -1,5 +1,5 @@ // -// File.swift +// RelationshipsCompare.swift // // // Created by Mathew Polzin on 11/3/19. diff --git a/Tests/JSONAPITestingTests/Comparisons/ArrayCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/ArrayCompareTests.swift new file mode 100644 index 0000000..fccf84c --- /dev/null +++ b/Tests/JSONAPITestingTests/Comparisons/ArrayCompareTests.swift @@ -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") + } +} diff --git a/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift index e7a7dca..e54031c 100644 --- a/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/AttributesCompareTests.swift @@ -10,13 +10,14 @@ 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) ) let attr2 = attr1 @@ -25,24 +26,27 @@ final class AttributesCompareTests: XCTestCase { "int": .same, "bool": .same, "double": .same, - "struct": .same + "struct": .same, + "transformed": .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) ) 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) ) XCTAssertEqual(attr1.compare(to: attr2), [ @@ -50,7 +54,8 @@ 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") ]) } } @@ -61,6 +66,7 @@ private struct TestAttributes: JSONAPI.Attributes { let bool: Attribute let double: Attribute let `struct`: Attribute + let transformed: TransformedAttribute struct Struct: Equatable, Codable, CustomStringConvertible { let string: String @@ -72,3 +78,9 @@ private struct TestAttributes: JSONAPI.Attributes { var description: String { return "string: \(string)" } } } + +private enum TestTransformer: Transformer { + static func transform(_ value: Int) throws -> String { + return "\(value)" + } +} diff --git a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift index 0d19a5c..9cbf93f 100644 --- a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift @@ -34,8 +34,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.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 +85,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(url: "http://google.com", meta: No Metadata)) ≠ TestLinks(link: JSONAPI.Link(url: "http://yahoo.com", meta: No Metadata)))"## + ]) + } + + func test_differentAPIDescription() { + XCTAssertEqual(d11.compare(to: d14).differences, [ + "API Description": ##"APIDescription(version: "10", meta: No Metadata) ≠ APIDescription(version: "1", meta: No Metadata)"## + ]) + } } fileprivate enum TestDescription: JSONAPI.ResourceObjectDescription { @@ -98,6 +141,22 @@ fileprivate typealias TestType2 = ResourceObject, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> +fileprivate struct TestMetadata: JSONAPI.Meta, CustomStringConvertible { + let total: Int + + var description: String { + "total: \(total)" + } +} + +fileprivate struct TestLinks: JSONAPI.Links { + let link: Link +} + +typealias TestAPIDescription = APIDescription + +fileprivate typealias SingleDocumentWithMetaAndLinks = JSONAPI.Document, TestMetadata, TestLinks, Include2, TestAPIDescription, BasicJSONAPIError> + fileprivate typealias OptionalSingleDocument = JSONAPI.Document, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> fileprivate typealias ManyDocument = JSONAPI.Document, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> @@ -220,3 +279,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")) +) diff --git a/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift index cdb7fda..b2dc83e 100644 --- a/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/RelationshipsCompareTests.swift @@ -10,5 +10,23 @@ import JSONAPI import JSONAPITesting final class RelationshipsCompareTests: XCTestCase { - // TODO: write tests + func test_same() { + // TODO: write test + } + + func test_differentIds() { + // TODO: write test + } + + func test_differentTypes() { + // TODO: write test + } + + func test_differentMetadata() { + // TODO: write test + } + + func test_differentLinks() { + // TODO: write tests + } } diff --git a/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift index fd43103..d62f9e2 100644 --- a/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/ResourceObjectCompareTests.swift @@ -1,5 +1,5 @@ // -// File.swift +// ResourceObjectCompareTests.swift // // // Created by Mathew Polzin on 11/3/19. @@ -15,11 +15,31 @@ final class ResourceObjectCompareTests: XCTestCase { XCTAssertTrue(test2.compare(to: test2).differences.isEmpty) } - func test_different() { + func test_differentAttributes() { // TODO: write actual test print(test1.compare(to: test2).differences.map { "\($0): \($1)" }.joined(separator: ", ")) } + func test_differentRelationships() { + // TODO: write test + } + + func test_differentTypes() { + // TODO: write test + } + + func test_differentIds() { + // TODO: write test + } + + func test_differentMetadata() { + // TODO: write test + } + + func test_differentLinks() { + // TODO: write test + } + fileprivate let test1 = TestType( id: "2", attributes: .init( diff --git a/Tests/JSONAPITestingTests/Test Helpers/EntityTestTypes.swift b/Tests/JSONAPITestingTests/Test Helpers/EntityTestTypes.swift index af99a71..1729dbc 100644 --- a/Tests/JSONAPITestingTests/Test Helpers/EntityTestTypes.swift +++ b/Tests/JSONAPITestingTests/Test Helpers/EntityTestTypes.swift @@ -12,3 +12,5 @@ public typealias Entity = Entity public typealias NewEntity = JSONAPI.ResourceObject + +extension String: JSONAPI.JSONAPIURL {} diff --git a/Tests/JSONAPITests/Attribute/AttributeTests.swift b/Tests/JSONAPITests/Attribute/AttributeTests.swift index f68831c..6ffdd22 100644 --- a/Tests/JSONAPITests/Attribute/AttributeTests.swift +++ b/Tests/JSONAPITests/Attribute/AttributeTests.swift @@ -14,6 +14,10 @@ class AttributeTests: XCTestCase { XCTAssertEqual(Attribute(value: "hello").value, "hello") } + func test_AttributeRawType() { + XCTAssert(Attribute(value: "hello").rawValueType == String.self) + } + func test_TransformedAttributeNoThrow() { XCTAssertNoThrow(try TransformedAttribute(rawValue: "10")) } @@ -26,6 +30,10 @@ class AttributeTests: XCTestCase { XCTAssertNoThrow(try TransformedAttribute(transformedValue: 10)) } + func test_TransformedAttributeRawType() throws { + try XCTAssert(TransformedAttribute(rawValue: "10").rawValueType == String.self) + } + func test_EncodedPrimitives() { testEncodedPrimitive(attribute: Attribute(value: 10)) testEncodedPrimitive(attribute: Attribute(value: false)) diff --git a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift index 30e2740..6fa2fb4 100644 --- a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift +++ b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift @@ -120,6 +120,28 @@ found JSON:API type "not_an_author" but expected "authors" ) } } + + func test_wantSuccess_foundError() { + XCTAssertThrowsError( + try testDecoder.decode( + Document, NoMetadata, NoLinks, Include1, NoAPIDescription, UnknownJSONAPIError>.SuccessDocument.self, + from: error_document_no_metadata + ) + ) { error in + XCTAssertEqual(String(describing: error), #"Expected a success document with a 'data' property but found an error document."#) + } + } + + func test_wantError_foundSuccess() { + XCTAssertThrowsError( + try testDecoder.decode( + Document, NoMetadata, NoLinks, Include1, NoAPIDescription, UnknownJSONAPIError>.ErrorDocument.self, + from: single_document_some_includes_with_metadata_with_api_description + ) + ) { error in + XCTAssertEqual(String(describing: error), #"Expected an error document but found a success document with a 'data' property."#) + } + } } // MARK: - Test Types diff --git a/Tests/JSONAPITests/Document/DocumentTests.swift b/Tests/JSONAPITests/Document/DocumentTests.swift index d6f6351..5911a0d 100644 --- a/Tests/JSONAPITests/Document/DocumentTests.swift +++ b/Tests/JSONAPITests/Document/DocumentTests.swift @@ -1554,5 +1554,3 @@ extension DocumentTests { } } } - -extension String: JSONAPI.JSONAPIURL {} diff --git a/Tests/JSONAPITests/Document/SuccessAndErrorDocumentTests.swift b/Tests/JSONAPITests/Document/SuccessAndErrorDocumentTests.swift new file mode 100644 index 0000000..9fa1144 --- /dev/null +++ b/Tests/JSONAPITests/Document/SuccessAndErrorDocumentTests.swift @@ -0,0 +1,139 @@ +// +// SuccessAndErrorDocumentTests.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/12/19. +// + +import XCTest +import JSONAPI + +final class SuccessAndErrorDocumentTests: XCTestCase { + func test_errorAccessors() { + let apiDescription = TestErrorDocument.APIDescription( + version: "1.0", + meta: .none + ) + let errors = [ + BasicJSONAPIError.error(.init(status: "500")) + ] + let meta = TestMeta(hello: "world") + let links = TestLinks(testLink: .init(url: "http://google.com")) + let errorDoc = TestErrorDocument( + apiDescription: apiDescription, + errors: errors, + meta: meta, + links: links + ) + + guard case let .errors(testErrors, meta: testMeta, links: testLinks) = errorDoc.body else { + XCTFail("Expected an error body") + return + } + + XCTAssertEqual(testErrors, errors) + XCTAssertEqual(testMeta, meta) + XCTAssertEqual(testLinks, links) + + XCTAssertEqual(errorDoc.apiDescription, apiDescription) + XCTAssertEqual(errorDoc.errors, errors) + XCTAssertEqual(errorDoc.meta, meta) + XCTAssertEqual(errorDoc.links, links) + + let equivalentDocument = TestDocument( + apiDescription: apiDescription, + errors: errors, + meta: meta, + links: links + ) + + XCTAssert(equivalentDocument == errorDoc) + XCTAssert(errorDoc == equivalentDocument) + } + + func test_successAccessors() { + let apiDescription = TestErrorDocument.APIDescription( + version: "1.0", + meta: .none + ) + let primaryResource = TestType( + id: "123", + attributes: .none, + relationships: .none, + meta: .none, + links: .none + ) + let resourceBody = SingleResourceBody(resourceObject: primaryResource) + let includedResource = TestType( + id: "456", + attributes: .none, + relationships: .none, + meta: .none, + links: .none + ) + let includes = Includes(values: [.init(includedResource)]) + let meta = TestMeta(hello: "world") + let links = TestLinks(testLink: .init(url: "http://google.com")) + let successDoc = TestSuccessDocument( + apiDescription: apiDescription, + body: resourceBody, + includes: includes, + meta: meta, + links: links + ) + + guard case let .data(data) = successDoc.body else { + XCTFail("Expected an data body") + return + } + + XCTAssertEqual(data.primary, resourceBody) + XCTAssertEqual(data.includes, includes) + XCTAssertEqual(data.meta, meta) + XCTAssertEqual(data.links, links) + + XCTAssertEqual(successDoc.data, data) + XCTAssertEqual(successDoc.apiDescription, apiDescription) + XCTAssertEqual(successDoc.primaryResource, resourceBody) + XCTAssertEqual(successDoc.includes, includes) + XCTAssertEqual(successDoc.meta, meta) + XCTAssertEqual(successDoc.links, links) + + let equivalentDocument = TestDocument( + apiDescription: apiDescription, + body: resourceBody, + includes: includes, + meta: meta, + links: links + ) + + XCTAssert(equivalentDocument == successDoc) + XCTAssert(successDoc == equivalentDocument) + } +} + +// MARK: - Test Type +extension SuccessAndErrorDocumentTests { + enum TestTypeDescription: ResourceObjectDescription { + static let jsonType: String = "tests" + + typealias Attributes = NoAttributes + + typealias Relationships = NoRelationships + } + + struct TestMeta: JSONAPI.Meta { + let hello: String + } + + struct TestLinks: JSONAPI.Links { + let testLink: Link + } + + typealias TestType = ResourceObject + + typealias TestDocument = Document, TestMeta, TestLinks, Include1, APIDescription, BasicJSONAPIError> + + typealias TestSuccessDocument = TestDocument.SuccessDocument + typealias TestErrorDocument = TestDocument.ErrorDocument +} diff --git a/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift b/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift index 21172ed..a3c3552 100644 --- a/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift +++ b/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift @@ -64,6 +64,7 @@ final class GenericJSONAPIErrorTests: XCTestCase { let error = decoded(type: TestGenericJSONAPIError.self, data: data) XCTAssertEqual(error, .unknown) + XCTAssertEqual(String(describing: error), "unknown error") } func test_encode() { diff --git a/Tests/JSONAPITests/Includes/IncludesDecodingErrorTests.swift b/Tests/JSONAPITests/Includes/IncludesDecodingErrorTests.swift new file mode 100644 index 0000000..2db4f75 --- /dev/null +++ b/Tests/JSONAPITests/Includes/IncludesDecodingErrorTests.swift @@ -0,0 +1,109 @@ +// +// IncludesDecodingErrorTests.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/14/19. +// + +import XCTest +import JSONAPI + +final class IncludesDecodingErrorTests: XCTestCase { + func test_unexpectedIncludeType() { + var error1: Error! + XCTAssertThrowsError(try testDecoder.decode(Includes>.self, from: three_different_type_includes)) { (error: Error) -> Void in + XCTAssertEqual( + (error as? IncludesDecodingError)?.idx, + 2 + ) + + XCTAssertEqual( + (error as? IncludesDecodingError).map(String.init(describing:)), +""" +Include 3 failed to parse: \nCould not have been Include Type 1 because: +found JSON:API type "test_entity4" but expected "test_entity1" + +Could not have been Include Type 2 because: +found JSON:API type "test_entity4" but expected "test_entity2" +""" + ) + + error1 = error + } + + // now test that we get the same error from a different test stub + XCTAssertThrowsError(try testDecoder.decode(Includes>.self, from: four_different_type_includes)) { (error2: Error) -> Void in + XCTAssertEqual( + error1 as? IncludesDecodingError, + error2 as? IncludesDecodingError + ) + } + } +} + +// MARK: - Test Types +extension IncludesDecodingErrorTests { + enum TestEntityType: ResourceObjectDescription { + + typealias Relationships = NoRelationships + + public static var jsonType: String { return "test_entity1" } + + public struct Attributes: JSONAPI.SparsableAttributes { + let foo: Attribute + let bar: Attribute + + public enum CodingKeys: String, Equatable, CodingKey { + case foo + case bar + } + } + } + + typealias TestEntity = BasicEntity + + enum TestEntityType2: ResourceObjectDescription { + + public static var jsonType: String { return "test_entity2" } + + public struct Relationships: JSONAPI.Relationships { + let entity1: ToOneRelationship + } + + public struct Attributes: JSONAPI.SparsableAttributes { + let foo: Attribute + let bar: Attribute + + public enum CodingKeys: String, Equatable, CodingKey { + case foo + case bar + } + } + } + + typealias TestEntity2 = BasicEntity + + enum TestEntityType4: ResourceObjectDescription { + + typealias Attributes = NoAttributes + + typealias Relationships = NoRelationships + + public static var jsonType: String { return "test_entity4" } + } + + typealias TestEntity4 = BasicEntity + + enum TestEntityType6: ResourceObjectDescription { + + typealias Attributes = NoAttributes + + public static var jsonType: String { return "test_entity6" } + + struct Relationships: JSONAPI.Relationships { + let entity4: ToOneRelationship + } + } + + typealias TestEntity6 = BasicEntity +} diff --git a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift index 5e5f593..e53b144 100644 --- a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift +++ b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift @@ -129,6 +129,10 @@ let four_different_type_includes = """ } } }, + { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + }, { "type": "test_entity6", "id": "11113B69-4DF1-467F-B52E-B0C9E44FC444", @@ -140,10 +144,6 @@ let four_different_type_includes = """ } } } - }, - { - "type": "test_entity4", - "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" } ] """.data(using: .utf8)! diff --git a/Tests/JSONAPITests/Poly/PolyProxyTests.swift b/Tests/JSONAPITests/Poly/PolyProxyTests.swift index 582a2fc..efae216 100644 --- a/Tests/JSONAPITests/Poly/PolyProxyTests.swift +++ b/Tests/JSONAPITests/Poly/PolyProxyTests.swift @@ -15,6 +15,12 @@ public class PolyProxyTests: XCTestCase { XCTAssertEqual(User.jsonType, "users") } + func test_CannotEncodeOrDecodePoly0() { + XCTAssertThrowsError(try testDecoder.decode(Poly0.self, from: poly_user_stub_1)) + + XCTAssertThrowsError(try testEncoder.encode(Poly0())) + } + func test_UserADecode() { let polyUserA = decoded(type: User.self, data: poly_user_stub_1) let userA = decoded(type: UserA.self, data: poly_user_stub_1) diff --git a/Tests/JSONAPITests/Test Helpers/EncodeDecode.swift b/Tests/JSONAPITests/Test Helpers/EncodeDecode.swift index d0b1c56..a588b71 100644 --- a/Tests/JSONAPITests/Test Helpers/EncodeDecode.swift +++ b/Tests/JSONAPITests/Test Helpers/EncodeDecode.swift @@ -9,13 +9,14 @@ import Foundation import XCTest let testDecoder = JSONDecoder() +let testEncoder = JSONEncoder() func decoded(type: T.Type, data: Data) -> T { return try! testDecoder.decode(T.self, from: data) } func encoded(value: T) -> Data { - return try! JSONEncoder().encode(value) + return try! testEncoder.encode(value) } /// A helper function that tests that decode() == decode().encode().decode(). diff --git a/Tests/JSONAPITests/Test Helpers/EncodedAttributeTest.swift b/Tests/JSONAPITests/Test Helpers/EncodedAttributeTest.swift index 56070a0..2a340d1 100644 --- a/Tests/JSONAPITests/Test Helpers/EncodedAttributeTest.swift +++ b/Tests/JSONAPITests/Test Helpers/EncodedAttributeTest.swift @@ -33,12 +33,9 @@ func testEncodedPrimitive(at let wrapperObject = try! JSONSerialization.jsonObject(with: encodedAttributeData, options: []) as! [String: Any] let jsonObject = wrapperObject["x"] - guard let jsonAttribute = jsonObject as? Transform.From else { - XCTFail("Attribute did not encode to the correct type") - return - } + XCTAssert(jsonObject is Transform.From) - XCTAssertEqual(attribute.rawValue, jsonAttribute) + XCTAssertEqual(attribute.rawValue, jsonObject as? Transform.From) } /// This function attempts to just cast to the type, so it only works @@ -48,10 +45,7 @@ func testEncodedPrimitive(attribute: Attribute = Entity public typealias NewEntity = JSONAPI.ResourceObject + +extension String: JSONAPI.JSONAPIURL {}