From f38399a1d633ea1ea65dca92b4d3a61bfeb94348 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 21 Dec 2018 07:50:32 -0800 Subject: [PATCH] Add a bit of entity structure testing --- Sources/JSONAPI/Resource/Entity.swift | 6 ++ Tests/JSONAPITests/Entity/EntityTests.swift | 50 +++++++++++++- .../EncodedEntityPropertyTest.swift | 68 +++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 Tests/JSONAPITests/Test Helpers/EncodedEntityPropertyTest.swift diff --git a/Sources/JSONAPI/Resource/Entity.swift b/Sources/JSONAPI/Resource/Entity.swift index a7196c3..c7c1b98 100644 --- a/Sources/JSONAPI/Resource/Entity.swift +++ b/Sources/JSONAPI/Resource/Entity.swift @@ -76,6 +76,8 @@ extension EntityProxy { /// protocol lets other types accept any Entity as a generic /// specialization. public protocol EntityType: EntityProxy, PrimaryResource { + associatedtype Meta: JSONAPI.Meta + associatedtype Links: JSONAPI.Links } public protocol IdentifiableEntityType: EntityType, Relatable where EntityRawIdType: JSONAPI.RawIdType {} @@ -85,6 +87,10 @@ public protocol IdentifiableEntityType: EntityType, Relatable where EntityRawIdT /// "Resource Object." /// See https://jsonapi.org/format/#document-resource-objects public struct Entity: EntityType { + + public typealias Meta = MetaType + public typealias Links = LinksType + /// The `Entity`'s Id. This can be of type `Unidentified` if /// the entity is being created clientside and the /// server is being asked to create a unique Id. Otherwise, diff --git a/Tests/JSONAPITests/Entity/EntityTests.swift b/Tests/JSONAPITests/Entity/EntityTests.swift index b75b5e1..a682b26 100644 --- a/Tests/JSONAPITests/Entity/EntityTests.swift +++ b/Tests/JSONAPITests/Entity/EntityTests.swift @@ -110,6 +110,8 @@ extension EntityTests { XCTAssert(type(of: entity.relationships) == NoRelationships.self) XCTAssert(type(of: entity.attributes) == NoAttributes.self) XCTAssertNoThrow(try TestEntity1.check(entity)) + + testEncoded(entity: entity) } func test_EntityNoRelationshipsNoAttributes_encode() { @@ -125,6 +127,8 @@ extension EntityTests { XCTAssertEqual(entity[\.floater], 123.321) XCTAssertNoThrow(try TestEntity5.check(entity)) + + testEncoded(entity: entity) } func test_EntityNoRelationshipsSomeAttributes_encode() { @@ -140,6 +144,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.others).map { $0.rawValue }, ["364B3B69-4DF1-467F-B52E-B0C9E44F666E"]) XCTAssertNoThrow(try TestEntity3.check(entity)) + + testEncoded(entity: entity) } func test_EntitySomeRelationshipsNoAttributes_encode() { @@ -155,6 +161,8 @@ extension EntityTests { XCTAssertEqual(entity[\.number], 992299) XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertNoThrow(try TestEntity4.check(entity)) + + testEncoded(entity: entity) } func test_EntitySomeRelationshipsSomeAttributes_encode() { @@ -174,6 +182,8 @@ extension EntityTests { XCTAssertNil(entity[\.maybeHere]) XCTAssertEqual(entity[\.maybeNull], "World") XCTAssertNoThrow(try TestEntity6.check(entity)) + + testEncoded(entity: entity) } func test_entityOneOmittedAttribute_encode() { @@ -189,6 +199,8 @@ extension EntityTests { XCTAssertEqual(entity[\.maybeHere], "World") XCTAssertNil(entity[\.maybeNull]) XCTAssertNoThrow(try TestEntity6.check(entity)) + + testEncoded(entity: entity) } func test_entityOneNullAttribute_encode() { @@ -204,6 +216,8 @@ extension EntityTests { XCTAssertEqual(entity[\.maybeHere], "World") XCTAssertEqual(entity[\.maybeNull], "!") XCTAssertNoThrow(try TestEntity6.check(entity)) + + testEncoded(entity: entity) } func test_entityAllAttribute_encode() { @@ -220,7 +234,7 @@ extension EntityTests { XCTAssertNil(entity[\.maybeNull]) XCTAssertNoThrow(try TestEntity6.check(entity)) - print(encodable: entity) + testEncoded(entity: entity) } func test_entityOneNullAndOneOmittedAttribute_encode() { @@ -241,7 +255,7 @@ extension EntityTests { XCTAssertNil(entity[\.maybeHereMaybeNull]) XCTAssertNoThrow(try TestEntity7.check(entity)) - print(encodable: entity) + testEncoded(entity: entity) } func test_NullOptionalNullableAttribute_encode() { @@ -256,6 +270,8 @@ extension EntityTests { XCTAssertEqual(entity[\.here], "Hello") XCTAssertEqual(entity[\.maybeHereMaybeNull], "World") XCTAssertNoThrow(try TestEntity7.check(entity)) + + testEncoded(entity: entity) } func test_NonNullOptionalNullableAttribute_encode() { @@ -277,6 +293,8 @@ extension EntityTests { XCTAssertEqual(entity[\.doubleFromInt], 22.0) XCTAssertEqual(entity[\.nullToString], "nil") XCTAssertNoThrow(try TestEntity8.check(entity)) + + testEncoded(entity: entity) } func test_IntToString_encode() { @@ -311,6 +329,8 @@ extension EntityTests { XCTAssertNil(entity ~> \.optionalOne) XCTAssertEqual((entity ~> \.optionalNullableOne)?.rawValue, "1229") XCTAssertNoThrow(try TestEntity9.check(entity)) + + testEncoded(entity: entity) } func test_nullableRelationshipNotNullOrOmitted_encode() { @@ -326,6 +346,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.one).rawValue, "4459") XCTAssertNil(entity ~> \.optionalNullableOne) XCTAssertNoThrow(try TestEntity9.check(entity)) + + testEncoded(entity: entity) } func test_nullableRelationshipNotNull_encode() { @@ -341,6 +363,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.one).rawValue, "4459") XCTAssertNil(entity ~> \.optionalNullableOne) XCTAssertNoThrow(try TestEntity9.check(entity)) + + testEncoded(entity: entity) } func test_optionalNullableRelationshipNulled_encode() { @@ -356,6 +380,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.one).rawValue, "4452") XCTAssertNil(entity ~> \.optionalNullableOne) XCTAssertNoThrow(try TestEntity9.check(entity)) + + testEncoded(entity: entity) } func test_nullableRelationshipIsNull_encode() { @@ -372,6 +398,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.optionalMany)?[0].rawValue, "332223") XCTAssertNil(entity ~> \.optionalNullableOne) XCTAssertNoThrow(try TestEntity9.check(entity)) + + testEncoded(entity: entity) } func test_optionalToManyIsNotOmitted_encode() { @@ -389,6 +417,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.selfRef).rawValue, "1") XCTAssertNoThrow(try TestEntity10.check(entity)) + + testEncoded(entity: entity) } func test_RleationshipsOfSameType_encode() { @@ -407,6 +437,8 @@ extension EntityTests { XCTAssertNil(entity[\.me]) XCTAssertEqual(entity.id, .unidentified) XCTAssertNoThrow(try UnidentifiedTestEntity.check(entity)) + + testEncoded(entity: entity) } func test_UnidentifiedEntity_encode() { @@ -421,6 +453,8 @@ extension EntityTests { XCTAssertEqual(entity[\.me], "unknown") XCTAssertEqual(entity.id, .unidentified) XCTAssertNoThrow(try UnidentifiedTestEntity.check(entity)) + + testEncoded(entity: entity) } func test_UnidentifiedEntityWithAttributes_encode() { @@ -441,6 +475,8 @@ extension EntityTests { XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) XCTAssertNoThrow(try UnidentifiedTestEntityWithMeta.check(entity)) + + testEncoded(entity: entity) } func test_UnidentifiedEntityWithAttributesAndMeta_encode() { @@ -456,6 +492,8 @@ extension EntityTests { XCTAssertEqual(entity.id, .unidentified) XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try UnidentifiedTestEntityWithLinks.check(entity)) + + testEncoded(entity: entity) } func test_UnidentifiedEntityWithAttributesAndLinks_encode() { @@ -473,6 +511,8 @@ extension EntityTests { XCTAssertEqual(entity.meta.y, 5) XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try UnidentifiedTestEntityWithMetaAndLinks.check(entity)) + + testEncoded(entity: entity) } func test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode() { @@ -490,6 +530,8 @@ extension EntityTests { XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) XCTAssertNoThrow(try TestEntity4WithMeta.check(entity)) + + testEncoded(entity: entity) } func test_EntitySomeRelationshipsSomeAttributesWithMeta_encode() { @@ -506,6 +548,8 @@ extension EntityTests { XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try TestEntity4WithLinks.check(entity)) + + testEncoded(entity: entity) } func test_EntitySomeRelationshipsSomeAttributesWithLinks_encode() { @@ -524,6 +568,8 @@ extension EntityTests { XCTAssertEqual(entity.meta.y, 5) XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try TestEntity4WithMetaAndLinks.check(entity)) + + testEncoded(entity: entity) } func test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode() { diff --git a/Tests/JSONAPITests/Test Helpers/EncodedEntityPropertyTest.swift b/Tests/JSONAPITests/Test Helpers/EncodedEntityPropertyTest.swift new file mode 100644 index 0000000..22752a9 --- /dev/null +++ b/Tests/JSONAPITests/Test Helpers/EncodedEntityPropertyTest.swift @@ -0,0 +1,68 @@ +// +// EncodedEntityPropertyTest.swift +// JSONAPITests +// +// Created by Mathew Polzin on 12/21/18. +// + +import Foundation +import XCTest +import JSONAPI +import JSONAPITestLib + +func testEncoded(entity: E) { + let encodedEntityData = encoded(value: entity) + let jsonObject = try! JSONSerialization.jsonObject(with: encodedEntityData, options: []) + let jsonDict = jsonObject as? [String: Any] + + XCTAssertNotNil(jsonDict) + + let jsonAttributes = jsonDict?["attributes"] as? [String: Any] + + if E.Attributes.self == NoAttributes.self { + XCTAssertNil(jsonAttributes) + } else { + XCTAssertNotNil(jsonAttributes) + } + + let jsonRelationships = jsonDict?["relationships"] as? [String: Any] + + if E.Relationships.self == NoRelationships.self { + XCTAssertNil(jsonRelationships) + } else { + XCTAssertNotNil(jsonRelationships) + } + + let jsonMeta = jsonDict?["meta"] as? [String: Any] + + if E.Meta.self == NoMetadata.self { + XCTAssertNil(jsonMeta) + } else { + XCTAssertNotNil(jsonMeta) + } + + let jsonLinks = jsonDict?["links"] as? [String: Any] + + if E.Links.self == NoLinks.self { + XCTAssertNil(jsonLinks) + } else { + XCTAssertNotNil(jsonLinks) + } +} + +// MARK: - Extensions to help with identifying structure of Mirror +private protocol OptionalAttributeType {} +extension Optional: OptionalAttributeType where Wrapped: AttributeType {} + +private protocol OptionalArray {} +extension Optional: OptionalArray where Wrapped: ArrayType {} + +private protocol AttributeTypeWithOptionalArray {} +extension TransformedAttribute: AttributeTypeWithOptionalArray where RawValue: OptionalArray {} + +private protocol OptionalRelationshipType {} +extension Optional: OptionalRelationshipType where Wrapped: RelationshipType {} + +private protocol _RelationshipType {} +extension ToOneRelationship: _RelationshipType {} +extension ToManyRelationship: _RelationshipType {}