// // BUYPropertyDescriptionAdditionsTests.m // Mobile Buy SDK // // Created by Shopify. // Copyright (c) 2015 Shopify Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #import <XCTest/XCTest.h> #import <CoreData/CoreData.h> #import "BUYFlatCollectionTransformer.h" #import "BUYDateTransformer.h" #import "BUYIdentityTransformer.h" #import "NSEntityDescription+BUYAdditions.h" #import "NSPropertyDescription+BUYAdditions.h" #import "TestModel.h" static NSString * const BirdEntity = @"Bird"; static NSString * const BranchEntity = @"Branch"; static NSString * const LeafEntity = @"Leaf"; static NSString * const NestEntity = @"Nest"; static NSString * const ResearchEntity = @"Researcher"; static NSString * const RootEntity = @"Root"; @interface NSIndexSet (BUYTestAdditions) + (instancetype)buy_indexSetWithIndexes:(NSArray *)indexes; @end @interface BUYCoreDataModelAdditionsTests : XCTestCase @property (nonatomic) NSManagedObjectModel *model; @property (nonatomic) TestModelManager *modelManager; @end @implementation BUYCoreDataModelAdditionsTests + (void)initialize { if (self == [BUYCoreDataModelAdditionsTests class]) { [NSValueTransformer setValueTransformer:[BUYFlatCollectionTransformer arrayTransformer] forName:@"Array"]; [NSValueTransformer setValueTransformer:[BUYFlatCollectionTransformer setTransformer] forName:@"Set"]; } } - (instancetype)initWithInvocation:(NSInvocation *)invocation { self = [super initWithInvocation:invocation]; if (self) { self.modelManager = [[TestModelManager alloc] init]; self.model = self.modelManager.model; } return self; } - (NSEntityDescription *)entityForName:(NSString *)entityName { return self.model.entitiesByName[entityName]; } - (NSAttributeDescription *)attributeWithName:(NSString *)attributeName forEntity:(NSString *)entityName { return [[self entityForName:entityName] attributesByName][attributeName]; } - (NSRelationshipDescription *)relationshipWithName:(NSString *)propertyName forEntity:(NSString *)entityName { return [[self entityForName:entityName] relationshipsByName][propertyName]; } - (void)testJSONTransformerName { XCTAssertEqualObjects(BUYDateTransformerName, [self attributeWithName:@"date" forEntity:LeafEntity].JSONValueTransformerName); XCTAssertEqualObjects(BUYIdentityTransformerName, [self attributeWithName:@"identifier" forEntity:RootEntity].JSONValueTransformerName); } - (void)testJSONPropertyKey { XCTAssertEqualObjects(@"createDate", [self attributeWithName:@"date" forEntity:LeafEntity].JSONPropertyKey); } - (void)testJSONValueTransformer { XCTAssertEqualObjects([BUYFlatCollectionTransformer class], [[self attributeWithName:@"tags" forEntity:LeafEntity].JSONValueTransformer class]); } - (void)testNilAttribute { NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity]; XCTAssertNil([idAttribute buy_JSONForValue:nil]); XCTAssertEqualObjects([idAttribute buy_valueForJSON:nil object:nil], [NSNull null]); } - (void)testNullAttribute { NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity]; XCTAssertEqualObjects([idAttribute buy_JSONForValue:[NSNull null]], [NSNull null]); XCTAssertEqualObjects([idAttribute buy_valueForJSON:[NSNull null] object:nil], [NSNull null]); } - (void)testInteger { NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity]; NSNumber *identifier = @10001; XCTAssertEqualObjects(identifier, [idAttribute buy_JSONForValue:identifier]); XCTAssertEqualObjects(identifier, [idAttribute buy_valueForJSON:identifier object:nil]); } - (void)testString { NSAttributeDescription *stringAttribute = [self attributeWithName:@"name" forEntity:RootEntity]; NSString *name = @"MyRoot"; XCTAssertEqualObjects(name, [stringAttribute buy_JSONForValue:name]); XCTAssertEqualObjects(name, [stringAttribute buy_valueForJSON:name object:nil]); } - (void)testDecimalNumber { NSAttributeDescription *decimalAttribute = [self attributeWithName:@"age" forEntity:RootEntity]; NSString *ageString = @"145"; NSDecimalNumber *age = [NSDecimalNumber decimalNumberWithString:ageString]; XCTAssertEqualObjects(ageString, [decimalAttribute buy_JSONForValue:age]); XCTAssertEqualObjects(age, [decimalAttribute buy_valueForJSON:ageString object:nil]); } - (void)testDate { NSAttributeDescription *dateAttribute = [self attributeWithName:@"date" forEntity:LeafEntity]; NSString *dateString = @"1970-01-01T01:17:59+0000"; NSDate *date = [NSDate dateWithTimeIntervalSince1970:4679.0]; XCTAssertEqualObjects(dateString, [dateAttribute buy_JSONForValue:date]); XCTAssertEqualObjects(date, [dateAttribute buy_valueForJSON:dateString object:nil]); } - (void)testURL { // the Root.url attribute declares "attributeValueClass = NSURL; JSONTransformerName = BUYURL" NSAttributeDescription *urlAttribute = [self attributeWithName:@"url" forEntity:RootEntity]; NSString *urlString = @"https://www.example.com/api/model.json?id=100"; NSURL *url = [NSURL URLWithString:urlString]; XCTAssertEqualObjects(urlString, [urlAttribute buy_JSONForValue:url]); XCTAssertEqualObjects(url, [urlAttribute buy_valueForJSON:urlString object:nil]); } - (void)testFlatArray { NSAttributeDescription *arrayDescription = [self attributeWithName:@"ornaments" forEntity:BranchEntity]; NSString *ornamentsString = @"one two three two one"; NSArray *ornaments = @[@"one", @"two", @"three", @"two", @"one"]; XCTAssertEqualObjects(ornamentsString, [arrayDescription buy_JSONForValue:ornaments]); XCTAssertEqualObjects(ornaments, [arrayDescription buy_valueForJSON:ornamentsString object:nil]); } - (void)testFlatSet { NSAttributeDescription *setDescription = [self attributeWithName:@"tags" forEntity:LeafEntity]; NSString *tagsString = @"blue green red"; NSSet *tags = [NSSet setWithArray:@[@"red", @"green", @"blue"]]; XCTAssertEqualObjects(tags, [setDescription buy_valueForJSON:tagsString object:nil]); NSString *jsonString = [setDescription buy_JSONForValue:tags]; NSSet *actual = [NSSet setWithArray:[jsonString componentsSeparatedByString:@" "]]; XCTAssertEqualObjects(actual, tags); } - (void)testNilRelationship { Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil]; NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity]; XCTAssertNil([nestRelationship buy_valueForJSON:nil object:branch]); } - (void)testNullRelationship { Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil]; NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity]; XCTAssertNil([nestRelationship buy_valueForJSON:[NSNull null] object:branch]); } - (void)testRelationship { Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil]; NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity]; NSDictionary *expected = @{ @"egg_count" : @2 }; Nest *nest = [nestRelationship buy_valueForJSON:expected object:branch]; XCTAssertEqual(branch, [nest branch]); NSDictionary *actual = [nestRelationship buy_JSONForValue:nest]; XCTAssertEqualObjects(actual, expected); } - (void)testRecursiveRelationship { Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil]; NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity]; NSDictionary *json = @{ @"bird_id" : @501 }; Nest *nest = [nestRelationship buy_valueForJSON:json object:branch]; XCTAssertEqualObjects(@501, [[nest bird] identifier]); id actual = [nestRelationship buy_JSONForValue:nest]; XCTAssertEqualObjects(actual, json); } - (void)testToManyRelationship { Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil]; NSRelationshipDescription *leafRelationship = [self relationshipWithName:@"leaves" forEntity:BranchEntity]; // Semi-random leaf objects branch.leaves = [NSSet setWithArray:@[[self leafWithDate:[self dateWithComponents:[self november4_1605]] tags:[self tagsWithIndexes:@[@1, @5, @11]]], [self leafWithDate:[self dateWithComponents:[self june21_1970]] tags:[self tagsWithIndexes:@[@9]]], [self leafWithDate:[self dateWithComponents:[self jan1_2000]] tags:[self tagsWithIndexes:@[@12, @0, @8, @4]]]]]; id json = [leafRelationship buy_JSONForValue:branch.leaves]; NSSet<Leaf *> *actual = [leafRelationship buy_valueForJSON:json object:branch]; for (Leaf *leaf in actual) { XCTAssertEqualObjects(leaf.branch, branch); } XCTAssertEqualObjects(actual, branch.leaves); } - (void)testManyToManyRelationship { Researcher *researcher = [self.modelManager buy_objectWithEntityName:ResearchEntity JSONDictionary:nil]; NSRelationshipDescription *researchersRelationship = [self relationshipWithName:@"researchers" forEntity:BirdEntity]; XCTAssertNil([researchersRelationship buy_JSONForValue:[NSSet setWithObject:researcher]]); } - (void)testEntityIsPrivate { NSEntityDescription *forestEntity = [self entityForName:[Forest entityName]]; XCTAssertTrue([forestEntity buy_isPrivate]); } - (void)testFetchedProperty { NSFetchedPropertyDescription *fetchedProperty = [[NSFetchedPropertyDescription alloc] init]; XCTAssertNil([fetchedProperty buy_valueForJSON:nil object:nil]); XCTAssertNil([fetchedProperty buy_JSONForValue:nil]); } - (Leaf *)leafWithDate:(NSDate *)date tags:(NSSet *)tags { Leaf *leaf = [self.modelManager buy_objectWithEntityName:[Leaf entityName] JSONDictionary:nil]; leaf.date = date; leaf.tags = tags; return leaf; } - (NSSet *)tagsWithIndexes:(NSArray *)indexes { static NSArray *tags; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ tags = @[@"one", @"two", @"three", @"hot", @"urgent", @"important", @"red", @"green", @"blue", @"animal", @"vegetable", @"mineral", @"fungus"]; }); return [NSSet setWithArray:[tags objectsAtIndexes:[NSIndexSet buy_indexSetWithIndexes:indexes]]]; } - (NSDate *)dateWithComponents:(NSDateComponents *)components { return [[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian] dateFromComponents:components]; } - (NSDateComponents *)november4_1605 { NSDateComponents *components = [[NSDateComponents alloc] init]; components.year = 1605; components.month = 11; components.day = 4; return components; } - (NSDateComponents *)june21_1970 { NSDateComponents *components = [[NSDateComponents alloc] init]; components.year = 1970; components.month = 6; components.day = 21; return components; } - (NSDateComponents *)jan1_2000 { NSDateComponents *components = [[NSDateComponents alloc] init]; components.year = 2000; components.month = 1; components.day = 1; components.second = 1; return components; } @end @implementation NSIndexSet (BUYTestAdditions) + (instancetype)buy_indexSetWithIndexes:(NSArray *)indexes { NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; for (NSNumber *index in indexes) { [indexSet addIndex:index.unsignedIntegerValue]; } return indexSet; } @end