mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
Add more errors for the resource object type property
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "'type' (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 "'type' (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:
|
||||
|
||||
@@ -11,7 +11,6 @@ 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)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ final class ResourceObjectDecodingErrorTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
"'required' relationship is not nullable but null."
|
||||
"'required' relationship is not nullable but null was found."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ final class ResourceObjectDecodingErrorTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
"'required' relationship is not nullable but null."
|
||||
"'required' relationship is not nullable but null was found."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,6 @@ final class ResourceObjectDecodingErrorTests: XCTestCase {
|
||||
TestEntity.self,
|
||||
from: entity_relationship_is_wrong_type
|
||||
)) { error in
|
||||
print(error)
|
||||
XCTAssertEqual(
|
||||
error as? ResourceObjectDecodingError,
|
||||
ResourceObjectDecodingError(
|
||||
@@ -218,7 +217,7 @@ extension ResourceObjectDecodingErrorTests {
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
"'required' attribute is not nullable but null."
|
||||
"'required' attribute is not nullable but null was found."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -285,11 +284,44 @@ extension ResourceObjectDecodingErrorTests {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func test_transformed_attribute() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_attribute_is_wrong_type4
|
||||
)) { error in
|
||||
XCTAssertEqual(
|
||||
error as? ResourceObjectDecodingError,
|
||||
ResourceObjectDecodingError(
|
||||
subjectName: "transformed",
|
||||
cause: .typeMismatch(expectedTypeName: String(describing: Int.self)),
|
||||
location: .attributes
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
"'transformed' attribute is not a Int as expected."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func test_transformed_attribute2() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_attribute_always_fails
|
||||
)) { error in
|
||||
XCTAssertEqual(
|
||||
String(describing: error),
|
||||
"Error: Always Fails"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - JSON:API Type
|
||||
extension ResourceObjectDecodingErrorTests {
|
||||
func test_wrongType() {
|
||||
func test_wrongJSONAPIType() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_is_wrong_type
|
||||
@@ -309,6 +341,69 @@ extension ResourceObjectDecodingErrorTests {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func test_wrongDecodedType() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_type_is_wrong_type
|
||||
)) { error in
|
||||
XCTAssertEqual(
|
||||
error as? ResourceObjectDecodingError,
|
||||
ResourceObjectDecodingError(
|
||||
subjectName: "type",
|
||||
cause: .typeMismatch(expectedTypeName: String(describing: String.self)),
|
||||
location: .type
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
#"'type' (a.k.a. the JSON:API type name) is not a String as expected."#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func test_type_missing() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_type_is_missing
|
||||
)) { error in
|
||||
XCTAssertEqual(
|
||||
error as? ResourceObjectDecodingError,
|
||||
ResourceObjectDecodingError(
|
||||
subjectName: "type",
|
||||
cause: .keyNotFound,
|
||||
location: .type
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
#"'type' (a.k.a. JSON:API type name) is required and missing."#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func test_type_null() {
|
||||
XCTAssertThrowsError(try testDecoder.decode(
|
||||
TestEntity2.self,
|
||||
from: entity_type_is_null
|
||||
)) { error in
|
||||
XCTAssertEqual(
|
||||
error as? ResourceObjectDecodingError,
|
||||
ResourceObjectDecodingError(
|
||||
subjectName: "type",
|
||||
cause: .valueNotFound,
|
||||
location: .type
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
(error as? ResourceObjectDecodingError)?.description,
|
||||
#"'type' (a.k.a. JSON:API type name) is not nullable but null was found."#
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Test Types
|
||||
@@ -335,10 +430,30 @@ extension ResourceObjectDecodingErrorTests {
|
||||
let required: Attribute<String>
|
||||
let other: Attribute<Int>?
|
||||
let yetAnother: Attribute<Bool?>?
|
||||
let transformed: TransformedAttribute<Int, IntToString>?
|
||||
let transformed2: TransformedAttribute<String, AlwaysFails>?
|
||||
}
|
||||
|
||||
typealias Relationships = NoRelationships
|
||||
}
|
||||
|
||||
typealias TestEntity2 = BasicEntity<TestEntityType2>
|
||||
|
||||
enum IntToString: Transformer {
|
||||
static func transform(_ value: Int) throws -> String {
|
||||
return "\(value)"
|
||||
}
|
||||
typealias From = Int
|
||||
typealias To = String
|
||||
}
|
||||
|
||||
enum AlwaysFails: Transformer {
|
||||
static func transform(_ value: String) throws -> String {
|
||||
throw Error()
|
||||
}
|
||||
|
||||
struct Error: Swift.Error, CustomStringConvertible {
|
||||
let description: String = "Error: Always Fails"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +533,35 @@ let entity_attribute_is_wrong_type3 = """
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_attribute_is_wrong_type4 = """
|
||||
{
|
||||
"id": "1",
|
||||
"type": "fourteenth_test_entities",
|
||||
"attributes": {
|
||||
"required": "hello",
|
||||
"transformed": "world"
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_attribute_always_fails = """
|
||||
{
|
||||
"id": "1",
|
||||
"type": "fourteenth_test_entities",
|
||||
"attributes": {
|
||||
"required": "hello",
|
||||
"transformed2": "world"
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_attributes_entirely_missing = """
|
||||
{
|
||||
"id": "1",
|
||||
"type": "fourteenth_test_entities"
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_is_wrong_type = """
|
||||
{
|
||||
"id": "1",
|
||||
@@ -544,10 +573,32 @@ let entity_is_wrong_type = """
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_attributes_entirely_missing = """
|
||||
let entity_type_is_wrong_type = """
|
||||
{
|
||||
"id": "1",
|
||||
"type": "fourteenth_test_entities"
|
||||
"type": 10,
|
||||
"attributes": {
|
||||
"required": "hello"
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_type_is_missing = """
|
||||
{
|
||||
"id": "1",
|
||||
"attributes": {
|
||||
"required": "hello"
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let entity_type_is_null = """
|
||||
{
|
||||
"id": "1",
|
||||
"type": null,
|
||||
"attributes": {
|
||||
"required": "hello"
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ class SparseFieldEncoderTests: XCTestCase {
|
||||
do {
|
||||
let _ = try encoder.encode(Wrapper())
|
||||
} catch let err as Wrapper.OuterFail.FailError {
|
||||
print(err.path)
|
||||
XCTAssertEqual(err.path.first as? Wrapper.OuterFail.CodingKeys, Wrapper.OuterFail.CodingKeys.inner)
|
||||
} catch {
|
||||
XCTFail("received unexpected error during test")
|
||||
|
||||
Reference in New Issue
Block a user