// // RKManagedObjectRequestOperationTest.m // RestKit // // Created by Blake Watters on 10/17/12. // Copyright (c) 2012 RestKit. All rights reserved. // #import "RKTestEnvironment.h" #import "RKManagedObjectRequestOperation.h" #import "RKEntityMapping.h" #import "RKHuman.h" #import "RKTestUser.h" #import "RKMappingErrors.h" #import "RKMappableObject.h" #import "RKPost.h" @interface RKManagedObjectRequestOperation () - (NSSet *)localObjectsFromFetchRequestsMatchingRequestURL:(NSError **)error; @end NSSet *RKSetByRemovingSubkeypathsFromSet(NSSet *setOfKeyPaths); @interface RKTestDelegateManagedObjectRequestOperation : RKManagedObjectRequestOperation @end @implementation RKTestDelegateManagedObjectRequestOperation - (void)mapperWillStartMapping:(RKMapperOperation *)mapper { // For stubbing } @end @interface RKTestListOfLists : NSObject @property (nonatomic, strong) NSArray *listOfLists; @end @implementation RKTestListOfLists @end @interface RKManagedObjectRequestOperationTest : RKTestCase @end @implementation RKManagedObjectRequestOperationTest - (void)setUp { [RKTestFactory setUp]; } - (void)tearDown { [RKTestFactory tearDown]; } - (void)testThatInitializationWithRequestDefaultsToSavingToPersistentStore { RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/whatever" relativeToURL:manager.baseURL]]; RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; expect(operation.savesToPersistentStore).to.equal(YES); } - (void)testThatInitializationWithRequestOperationDefaultsToSavingToPersistentStore { RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/whatever" relativeToURL:manager.baseURL]]; RKHTTPRequestOperation *requestOperation = [[RKHTTPRequestOperation alloc] initWithRequest:request]; RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithHTTPRequestOperation:requestOperation responseDescriptors:@[]]; expect(operation.savesToPersistentStore).to.equal(YES); } - (void)testFetchRequestBlocksAreInvokedWithARelativeURL { NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"categories/1234" relativeToURL:baseURL]]; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:@{}]; RKObjectMapping *mapping = [RKObjectMapping requestMapping]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"categories/:categoryID" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; responseDescriptor.baseURL = baseURL; id mockRequestOperation = [OCMockObject niceMockForClass:[RKHTTPRequestOperation class]]; [[[mockRequestOperation stub] andReturn:request] request]; [[[mockRequestOperation stub] andReturn:response] response]; RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithHTTPRequestOperation:mockRequestOperation responseDescriptors:@[ responseDescriptor ]]; __block NSURL *blockURL = nil; RKFetchRequestBlock fetchRequesBlock = ^NSFetchRequest *(NSURL *URL) { blockURL = URL; return nil; }; operation.fetchRequestBlocks = @[fetchRequesBlock]; NSError *error; [operation localObjectsFromFetchRequestsMatchingRequestURL:&error]; expect(blockURL).notTo.beNil(); expect([blockURL.baseURL absoluteString]).to.equal(@"http://restkit.org/api/v1/"); expect(blockURL.relativePath).to.equal(@"categories/1234"); } - (void)testFetchRequestBlocksDoNotCrash { RKLogConfigureByName("RestKit/Network/CoreData", RKLogLevelTrace); NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"categories/1234" relativeToURL:baseURL]]; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:@{}]; RKObjectMapping *mapping = [RKObjectMapping requestMapping]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"categories/:categoryID" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; responseDescriptor.baseURL = baseURL; id mockRequestOperation = [OCMockObject niceMockForClass:[RKHTTPRequestOperation class]]; [[[mockRequestOperation stub] andReturn:request] request]; [[[mockRequestOperation stub] andReturn:response] response]; RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithHTTPRequestOperation:mockRequestOperation responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequesBlock = ^NSFetchRequest *(NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"RKHuman"]; }; operation.fetchRequestBlocks = @[fetchRequesBlock]; NSError *error; [operation localObjectsFromFetchRequestsMatchingRequestURL:&error]; } - (void)testThatFetchRequestBlocksInvokedWithRelativeURLAreInAgreementWithPathPattern { NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"library/" relativeToURL:baseURL]]; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:@{}]; expect([request.URL absoluteString]).to.equal(@"http://restkit.org/api/v1/library/"); RKObjectMapping *mapping = [RKObjectMapping requestMapping]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"library/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; responseDescriptor.baseURL = baseURL; id mockRequestOperation = [OCMockObject niceMockForClass:[RKHTTPRequestOperation class]]; [[[mockRequestOperation stub] andReturn:request] request]; [[[mockRequestOperation stub] andReturn:response] response]; RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithHTTPRequestOperation:mockRequestOperation responseDescriptors:@[ responseDescriptor ]]; __block NSURL *blockURL = nil; RKFetchRequestBlock fetchRequesBlock = ^NSFetchRequest *(NSURL *URL) { blockURL = URL; return nil; }; operation.fetchRequestBlocks = @[fetchRequesBlock]; NSError *error; [operation localObjectsFromFetchRequestsMatchingRequestURL:&error]; expect(blockURL).notTo.beNil(); expect([blockURL absoluteString]).to.equal(@"http://restkit.org/api/v1/library/"); expect([blockURL.baseURL absoluteString]).to.equal(@"http://restkit.org/api/v1/"); expect(blockURL.relativePath).to.equal(@"library"); expect(blockURL.relativeString).to.equal(@"library/"); } - (void)testThatMappingResultContainsObjectsFetchedFromManagedObjectContextTheOperationWasInitializedWith { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/all.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"name" toKeyPath:@"name"]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:humanMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.mappingResult).notTo.beNil(); NSArray *managedObjectContexts = [[managedObjectRequestOperation.mappingResult array] valueForKeyPath:@"@distinctUnionOfObjects.managedObjectContext"]; expect([managedObjectContexts count]).to.equal(1); expect(managedObjectContexts).to.equal([NSArray arrayWithObject:managedObjectStore.mainQueueManagedObjectContext]); } // 304 'Not Modified' - (void)testThatManagedObjectsAreFetchedWhenHandlingAResponseThatCanSkipMapping { RKFetchRequestBlock fetchRequestBlock = ^(NSURL *URL){ return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/humans/1" relativeToURL:[RKTestFactory baseURL]]]; // Store a cache entry indicating that the response has been previously mapped NSData *responseData = [@"{}" dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *headers = @{ @"Etag": @"\"2cdd0a2b329541d81e82ab20aff6281b\"", @"Content-Type": @"application/json" }; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[request URL] statusCode:200 HTTPVersion:@"1.1" headerFields:headers]; NSAssert(response, @"Failed to build cached response"); NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:responseData userInfo:@{RKResponseHasBeenMappedCacheUserInfoKey: @YES} storagePolicy:NSURLCacheStorageAllowed]; [[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:request]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"name" toKeyPath:@"name"]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:humanMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]]; managedObjectRequestOperation.fetchRequestBlocks = @[fetchRequestBlock]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.mappingResult).notTo.beNil(); NSArray *mappedObjects = [managedObjectRequestOperation.mappingResult array]; expect(mappedObjects).to.haveCountOf(1); expect([mappedObjects[0] objectID]).to.equal([human objectID]); } - (void)testThatCachedNotModifiedResponseIsNotUsedWhenMappingFailedToComplete { [[NSURLCache sharedURLCache] removeAllCachedResponses]; RKFetchRequestBlock fetchRequestBlock = ^(NSURL *URL){ return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/coredata/etag" relativeToURL:[RKTestFactory baseURL]]]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"name" toKeyPath:@"name"]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:humanMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKManagedObjectRequestOperation *initialManagedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]]; initialManagedObjectRequestOperation.fetchRequestBlocks = @[fetchRequestBlock]; initialManagedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; initialManagedObjectRequestOperation.managedObjectCache = managedObjectStore.managedObjectCache; // Send our first request in order to generate a cache entry, ensuring mapping is suspended and thus no objects are created in the store NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; [[RKObjectRequestOperation responseMappingQueue] setSuspended:YES]; [operationQueue addOperation:initialManagedObjectRequestOperation]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; [initialManagedObjectRequestOperation cancel]; expect(initialManagedObjectRequestOperation.isCancelled).to.beTruthy(); expect([[NSURLCache sharedURLCache] cachedResponseForRequest:request]).toNot.beNil(); expect(initialManagedObjectRequestOperation.mappingResult).to.beNil(); // Now setup and send our second request [[RKObjectRequestOperation responseMappingQueue] setSuspended:NO]; RKManagedObjectRequestOperation *secondManagedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]]; secondManagedObjectRequestOperation.fetchRequestBlocks = @[fetchRequestBlock]; secondManagedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; secondManagedObjectRequestOperation.managedObjectCache = managedObjectStore.managedObjectCache; [operationQueue addOperation:secondManagedObjectRequestOperation]; [operationQueue waitUntilAllOperationsAreFinished]; expect(secondManagedObjectRequestOperation.mappingResult).notTo.beNil(); NSArray *mappedObjects = [secondManagedObjectRequestOperation.mappingResult array]; expect(mappedObjects).to.haveCountOf(2); } - (void)testThatInvalidObjectFailingManagedObjectContextSaveFailsOperation { // NOTE: The model defines a maximum length of 15 for the 'name' attribute RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:nil withProperties:nil]; human.name = @"This Is An Invalid Name Because It Exceeds Fifteen Characters"; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/all.json" relativeToURL:[RKTestFactory baseURL]]]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"name" toKeyPath:@"name"]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:humanMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.error).notTo.beNil(); expect(managedObjectRequestOperation.mappingResult).to.beNil(); #if __IPHONE_OS_VERSION_MIN_REQUIRED expect([managedObjectRequestOperation.error localizedDescription]).to.equal(@"The operation couldn’t be completed. (Cocoa error 1660.)"); #else expect([managedObjectRequestOperation.error localizedDescription]).to.equal(@"name is too long."); #endif } #pragma mark - Deletion Response Tests // 204 application/json - (void)testDeletionOfObjectWith204ResponseTriggersObjectDeletion { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/204" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } // 200 application/json "" - (void)testDeletionOfObjectWithZeroLengthResponseTriggersObjectDeletion { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/empty" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } // 200 application/json "{}" - (void)testDeletionOfObjectWithEmptyDictionaryResponseTriggersObjectDeletion { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/1" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } // 200 application/json { "human": { "status": "OK" } } - (void)testDeletionOfObjectWithUnmappedResponseBody { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/success" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } // 200 application/json { "human": { "status": "OK" } } - (void)testDeletionOfObjectWithResponseDescriptorMappingResponseByURL { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"human.status" : @"name" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:@"/humans/success" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/success" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } // 200 application/json { "human": { "status": "OK" } } - (void)testDeletionOfObjectWithResponseDescriptorMappingResponseByKeyPath { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"status" : @"name" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/success" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } - (void)testThatDeletionOfObjectThatHasAlreadyBeenDeletedFromCoreDataDoesNotRaiseException { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/success" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"DELETE"; [managedObjectStore.persistentStoreManagedObjectContext saveToPersistentStore:nil]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.error).to.beNil(); expect([human hasBeenDeleted]).to.equal(YES); } - (void)testThatManagedObjectMappedAsTheRelationshipOfNonManagedObjectsAreRefetchedFromTheParentContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friends" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); RKTestUser *testUser = [managedObjectRequestOperation.mappingResult firstObject]; expect([[testUser.friends lastObject] managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testThatManagedObjectMappedAsTheRelationshipOfNonManagedObjectsWithADynamicMappingAreRefetchedFromTheParentContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friends" withMapping:entityMapping]]; RKDynamicMapping *dynamicMapping = [RKDynamicMapping new]; [dynamicMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:@"name" expectedValue:@"Blake Watters" objectMapping:userMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); RKTestUser *testUser = [managedObjectRequestOperation.mappingResult firstObject]; expect([[testUser.friends lastObject] managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testThatManagedObjectMappedFromRootKeyPathAreRefetchedFromTheParentContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"human.name": @"name" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); NSManagedObject *managedObject = [managedObjectRequestOperation.mappingResult firstObject]; expect([managedObject managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testThatManagedObjectMappedToNSSetRelationshipOfNonManagedObjectsAreRefetchedFromTheParentContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friendsSet" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); RKTestUser *testUser = [managedObjectRequestOperation.mappingResult firstObject]; expect([[testUser.friendsSet anyObject] managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testThatManagedObjectMappedToNSOrderedSetRelationshipOfNonManagedObjectsAreRefetchedFromTheParentContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friendsOrderedSet" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); RKTestUser *testUser = [managedObjectRequestOperation.mappingResult firstObject]; expect([[testUser.friendsOrderedSet firstObject] managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testMappingRespectsTargetObjectWhenMappingNonManagedThatIncludesChildManagedObject { RKTestUser *testUser = [RKTestUser new]; RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friendsOrderedSet" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.targetObject = testUser; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); RKTestUser *mappedTestUser = [managedObjectRequestOperation.mappingResult firstObject]; expect(mappedTestUser).to.equal(testUser); } - (void)testDeletionOfOrphanedManagedObjects { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *orphanedHuman = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); expect(orphanedHuman.managedObjectContext).to.beNil(); } - (void)testDeletionOfOrphanedObjectsMappedOnRelationships { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *orphanedHuman = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"friends" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); expect(orphanedHuman.managedObjectContext).to.beNil(); } - (void)testDeletionOfOrphanedTagsOfPosts { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; NSManagedObject *orphanedTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [orphanedTag setValue:@"orphaned" forKey:@"name"]; RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:@"Post" inManagedObjectStore:managedObjectStore]; [postMapping addAttributeMappingsFromArray:@[ @"title", @"body" ]]; RKEntityMapping *tagMapping = [RKEntityMapping mappingForEntityForName:@"Tag" inManagedObjectStore:managedObjectStore]; [tagMapping addAttributeMappingsFromArray:@[ @"name" ]]; [postMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"tags" toKeyPath:@"tags" withMapping:tagMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:@"posts" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/posts.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Tag"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); expect(orphanedTag.managedObjectContext).to.beNil(); // Create 3 tags. Update the post entity so it only points to 2 tags. Tag should be deleted. // Create 3 tags. Create another post pointing to one of the tags. Update the post entity so it only points to 2 tags. Tag should be deleted. } - (void)testThatDeletionOfOrphanedObjectsCanBeSuppressedByPredicate { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; NSManagedObject *tagOnDiferentObject = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [tagOnDiferentObject setValue:@"orphaned" forKey:@"name"]; NSManagedObject *otherPost = [NSEntityDescription insertNewObjectForEntityForName:@"Post" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [otherPost setValue:@"Title" forKey:@"title"]; [otherPost setValue:[NSSet setWithObject:tagOnDiferentObject] forKey:@"tags"]; RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:@"Post" inManagedObjectStore:managedObjectStore]; [postMapping addAttributeMappingsFromArray:@[ @"title", @"body" ]]; RKEntityMapping *tagMapping = [RKEntityMapping mappingForEntityForName:@"Tag" inManagedObjectStore:managedObjectStore]; [tagMapping addAttributeMappingsFromArray:@[ @"name" ]]; [postMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"tags" toKeyPath:@"tags" withMapping:tagMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:@"posts" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/posts.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Tag"]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"posts.@count == 0"]; return fetchRequest; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); expect(tagOnDiferentObject.managedObjectContext).notTo.beNil(); } - (void)testThatObjectsOrphanedByRequestOperationAreDeletedAppropriately { // create tags: development, restkit, orphaned RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; __block NSManagedObject *post = nil; __block NSManagedObject *orphanedTag; __block NSManagedObject *anotherTag; [managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{ NSManagedObject *developmentTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [developmentTag setValue:@"development" forKey:@"name"]; NSManagedObject *restkitTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [restkitTag setValue:@"restkit" forKey:@"name"]; orphanedTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [orphanedTag setValue:@"orphaned" forKey:@"name"]; post = [NSEntityDescription insertNewObjectForEntityForName:@"Post" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [post setValue:@"Post Title" forKey:@"title"]; [post setValue:[NSSet setWithObjects:developmentTag, restkitTag, orphanedTag, nil] forKey:@"tags"]; anotherTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [anotherTag setValue:@"another" forKey:@"name"]; NSManagedObject *anotherPost = [NSEntityDescription insertNewObjectForEntityForName:@"Post" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [anotherPost setValue:@"Another Post" forKey:@"title"]; [anotherPost setValue:[NSSet setWithObject:anotherTag] forKey:@"tags"]; [managedObjectStore.persistentStoreManagedObjectContext save:nil]; }]; RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:@"Post" inManagedObjectStore:managedObjectStore]; postMapping.identificationAttributes = @[ @"title" ]; [postMapping addAttributeMappingsFromArray:@[ @"title", @"body" ]]; RKEntityMapping *tagMapping = [RKEntityMapping mappingForEntityForName:@"Tag" inManagedObjectStore:managedObjectStore]; tagMapping.identificationAttributes = @[ @"name" ]; [tagMapping addAttributeMappingsFromArray:@[ @"name" ]]; [postMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"tags" toKeyPath:@"tags" withMapping:tagMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:@"posts" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/posts.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Tag"]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"posts.@count == 0"]; return fetchRequest; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); NSSet *tagNames = [post valueForKeyPath:@"tags.name"]; NSSet *expectedTagNames = [NSSet setWithObjects:@"development", @"restkit", nil ]; expect(tagNames).to.equal(expectedTagNames); expect([orphanedTag hasBeenDeleted]).to.equal(YES); expect([anotherTag hasBeenDeleted]).to.equal(NO); } - (void)testPruningOfSubkeypathsFromSet { NSSet *keyPaths = [NSSet setWithObjects:@"posts", @"posts.tags", @"another", @"something.else.entirely", @"another.this.that", @"somewhere.out.there", @"some.posts", nil]; NSSet *prunedSet = RKSetByRemovingSubkeypathsFromSet(keyPaths); NSSet *expectedSet = [NSSet setWithObjects:@"posts", @"another", @"something.else.entirely", @"somewhere.out.there", @"some.posts", nil]; expect(prunedSet).to.equal(expectedSet); } - (void)testPathVisitationDoesNotRecurseInfinitelyForSelfReferentialMappings { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *orphanedHuman = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [entityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"friends" toKeyPath:@"friends" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); expect(orphanedHuman.managedObjectContext).to.beNil(); } - (void)testDeletionOfObjectsMappedFindsObjectsMappedBySelfReferentialMappings { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; entityMapping.identificationAttributes = @[ @"railsID" ]; [entityMapping addAttributeMappingsFromDictionary:@{ @"name": @"name", @"id": @"railsID" }]; [entityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"friends" toKeyPath:@"friends" withMapping:entityMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; // Create Blake, Sarah, Colin, Monkey & Orphan NSManagedObjectContext *context = managedObjectStore.persistentStoreManagedObjectContext; NSUInteger count = [context countForEntityForName:@"Human" predicate:nil error:nil]; expect(count).to.equal(0); RKHuman *blake = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(1), @"name": @"Blake" }]; RKHuman *sarah = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(2), @"name": @"Sarah" }]; RKHuman *monkey = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(3), @"name": @"Monkey" }]; RKHuman *colin = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(4), @"name": @"Colin" }]; RKHuman *orphan = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(5), @"name": @"Orphan" }]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/self_referential.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock fetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ fetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); // Verify that orphan was deleted count = [context countForEntityForName:@"Human" predicate:nil error:nil]; expect(count).to.equal(4); expect(blake.managedObjectContext).notTo.beNil(); expect(sarah.managedObjectContext).notTo.beNil(); expect(monkey.managedObjectContext).notTo.beNil(); expect(colin.managedObjectContext).notTo.beNil(); expect(orphan.managedObjectContext).to.beNil(); } - (void)testDeletionOfObjectsMappedFindsObjectsMappedByNestedSelfReferentialMappings { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *houseMapping = [RKEntityMapping mappingForEntityForName:@"House" inManagedObjectStore:managedObjectStore]; [houseMapping addAttributeMappingsFromDictionary:@{ @"houseID": @"railsID" }]; [houseMapping addAttributeMappingsFromArray:@[ @"city", @"state" ]]; houseMapping.identificationAttributes = @[ @"railsID" ]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; humanMapping.identificationAttributes = @[ @"railsID" ]; [humanMapping addAttributeMappingsFromDictionary:@{ @"name": @"name", @"humanID": @"railsID" }]; [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"roommates" toKeyPath:@"friends" withMapping:humanMapping]]; [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"landlord" toKeyPath:@"landlord" withMapping:humanMapping]]; [houseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"owner" toKeyPath:@"owner" withMapping:humanMapping]]; [houseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"occupants" toKeyPath:@"occupants" withMapping:humanMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:houseMapping pathPattern:nil keyPath:@"houses" statusCodes:[NSIndexSet indexSetWithIndex:200]]; // Create Blake, Sarah, Colin, Monkey & Orphan NSManagedObjectContext *context = managedObjectStore.persistentStoreManagedObjectContext; RKHuman *orphan = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(5), @"name": @"Orphan" }]; RKHuman *edward = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:context withProperties:@{ @"railsID": @(4), @"name": @"Edward" }]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/nested_self_referential.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock humanFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; RKFetchRequestBlock houseFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"House"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ humanFetchRequestBlock, houseFetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.deletesOrphanedObjects = YES; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); NSUInteger count = [context countForEntityForName:@"Human" predicate:nil error:nil]; expect(count).to.equal(4); count = [context countForEntityForName:@"House" predicate:nil error:nil]; expect(count).to.equal(1); expect(edward.managedObjectContext).notTo.beNil(); expect(orphan.managedObjectContext).to.beNil(); } - (void)testDeletionOfOrphanedObjectsMappedWithCyclicRelationshipThatIsNotSelfReferential { NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel new]; NSEntityDescription *listEntity = [NSEntityDescription new]; [listEntity setName:@"List"]; NSAttributeDescription *listNameAttribute = [NSAttributeDescription new]; [listNameAttribute setName:@"name"]; [listNameAttribute setAttributeType:NSStringAttributeType]; NSEntityDescription *authorEntity = [NSEntityDescription new]; [authorEntity setName:@"Author"]; NSAttributeDescription *authorNameAttribute = [NSAttributeDescription new]; [authorNameAttribute setName:@"name"]; [authorNameAttribute setAttributeType:NSStringAttributeType]; [authorEntity setProperties:@[ authorNameAttribute ]]; NSEntityDescription *entryEntity = [NSEntityDescription new]; [entryEntity setName:@"Entry"]; NSAttributeDescription *entryNameAttribute = [NSAttributeDescription new]; [entryNameAttribute setName:@"name"]; [entryNameAttribute setAttributeType:NSStringAttributeType]; NSAttributeDescription *entryValueAttribute = [NSAttributeDescription new]; [entryValueAttribute setName:@"value"]; [entryValueAttribute setAttributeType:NSStringAttributeType]; // Create relationships NSRelationshipDescription *listHasOneAuthorRelationship = [NSRelationshipDescription new]; [listHasOneAuthorRelationship setName:@"author"]; [listHasOneAuthorRelationship setDestinationEntity:authorEntity]; [listHasOneAuthorRelationship setMaxCount:1]; // To One NSRelationshipDescription *authorHasManyListsRelationship = [NSRelationshipDescription new]; [authorHasManyListsRelationship setName:@"lists"]; [authorHasManyListsRelationship setDestinationEntity:listEntity]; [authorHasManyListsRelationship setMaxCount:0]; // To Many [authorHasManyListsRelationship setInverseRelationship:listHasOneAuthorRelationship]; [listHasOneAuthorRelationship setInverseRelationship:authorHasManyListsRelationship]; NSRelationshipDescription *listHasManyValuesRelationship = [NSRelationshipDescription new]; [listHasManyValuesRelationship setName:@"entries"]; [listHasManyValuesRelationship setDestinationEntity:entryEntity]; [listHasManyValuesRelationship setMaxCount:0]; // To Many NSRelationshipDescription *entryBelongsToListRelationship = [NSRelationshipDescription new]; [entryBelongsToListRelationship setName:@"list"]; [entryBelongsToListRelationship setDestinationEntity:listEntity]; [entryBelongsToListRelationship setMaxCount:1]; // To One [entryBelongsToListRelationship setInverseRelationship:listHasManyValuesRelationship]; [listHasManyValuesRelationship setInverseRelationship:entryBelongsToListRelationship]; NSRelationshipDescription *entryRelatesToOtherListRelationship = [NSRelationshipDescription new]; [entryRelatesToOtherListRelationship setName:@"relatedList"]; [entryRelatesToOtherListRelationship setDestinationEntity:listEntity]; [entryRelatesToOtherListRelationship setMaxCount:1]; // To One [entryRelatesToOtherListRelationship setInverseRelationship:listHasManyValuesRelationship]; // Set the properties [authorEntity setProperties:@[ authorNameAttribute, authorHasManyListsRelationship ]]; [listEntity setProperties:@[ listNameAttribute, listHasOneAuthorRelationship, listHasManyValuesRelationship ]]; [entryEntity setProperties:@[ entryNameAttribute, entryValueAttribute, entryBelongsToListRelationship, entryRelatesToOtherListRelationship ]]; [managedObjectModel setEntities:@[ listEntity, authorEntity, entryEntity ]]; // Configure a Core Data stack for this model NSError *error = nil; RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel]; [managedObjectStore createPersistentStoreCoordinator]; [managedObjectStore addInMemoryPersistentStore:&error]; [managedObjectStore createManagedObjectContexts]; // Create mappings RKEntityMapping *listMapping = [[RKEntityMapping alloc] initWithEntity:listEntity]; listMapping.identificationAttributes = @[ @"name" ]; [listMapping addAttributeMappingsFromArray:@[ @"name" ]]; RKEntityMapping *authorMapping = [[RKEntityMapping alloc] initWithEntity:authorEntity]; authorMapping.identificationAttributes = @[ @"name" ]; [authorMapping addAttributeMappingsFromArray:@[ @"name" ]]; RKEntityMapping *entryMapping = [[RKEntityMapping alloc] initWithEntity:entryEntity]; entryMapping.identificationAttributes = @[ @"name" ]; [entryMapping addAttributeMappingsFromArray:@[ @"name" ]]; // NOTE: This cyclic mapping should trigger an explosion... [entryMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"list" toKeyPath:@"list" withMapping:listMapping]]; [entryMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"relatedList" toKeyPath:@"relatedList" withMapping:listMapping]]; [listMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"author" toKeyPath:@"author" withMapping:authorMapping]]; RKRelationshipMapping *entriesRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:@"entries" toKeyPath:@"entries" withMapping:entryMapping]; entriesRelationship.assignmentPolicy = RKUnionAssignmentPolicy; [listMapping addPropertyMapping:entriesRelationship]; RKObjectMapping *listsOfListsMapping = [RKObjectMapping mappingForClass:[RKTestListOfLists class]]; [listsOfListsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"lists" toKeyPath:@"listOfLists" withMapping:listMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:listsOfListsMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; // Create an orphaned Author and List NSManagedObject *orphanedAuthor = [[NSManagedObject alloc] initWithEntity:authorEntity insertIntoManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [orphanedAuthor setValue:@"Orphaned Author" forKey:@"name"]; NSManagedObject *orphanedList = [[NSManagedObject alloc] initWithEntity:listEntity insertIntoManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [orphanedList setValue:@"Orphaned List" forKey:@"name"]; // Create an existing List and Entry to be Unioned onto the result set NSManagedObject *existingList = [[NSManagedObject alloc] initWithEntity:listEntity insertIntoManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [existingList setValue:@"Second List" forKey:@"name"]; NSManagedObject *existingEntry = [[NSManagedObject alloc] initWithEntity:entryEntity insertIntoManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; [existingEntry setValue:@"Existing Entry" forKey:@"name"]; [existingList setValue:[NSSet setWithObject:existingEntry] forKey:@"entries"]; BOOL success = [managedObjectStore.persistentStoreManagedObjectContext saveToPersistentStore:&error]; expect(success).to.equal(YES); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/lists.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock listFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"List"]; }; RKFetchRequestBlock authorFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Author"]; }; RKFetchRequestBlock entryFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Entry"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ listFetchRequestBlock, authorFetchRequestBlock, entryFetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.deletesOrphanedObjects = YES; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); NSManagedObjectContext *managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; NSUInteger count = [managedObjectContext countForEntityForName:@"List" predicate:nil error:nil]; expect(count).to.equal(3); count = [managedObjectContext countForEntityForName:@"Entry" predicate:nil error:nil]; expect(count).to.equal(4); count = [managedObjectContext countForEntityForName:@"Author" predicate:nil error:nil]; expect(count).to.equal(2); // Orphans get deleted expect(orphanedAuthor.managedObjectContext).to.beNil(); expect(orphanedList.managedObjectContext).to.beNil(); // Objects in the results do not expect(existingList.managedObjectContext).notTo.beNil(); expect(existingEntry.managedObjectContext).notTo.beNil(); } - (void)testMappingWithDynamicMappingContainingIncompatibleEntityMappingsAtSameKeyPath { RKDynamicMapping *dynamicMapping = [RKDynamicMapping new]; RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; humanMapping.identificationAttributes = @[ @"railsID" ]; [humanMapping addAttributeMappingsFromDictionary:@{ @"name": @"name", @"humanID": @"railsID" }]; [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"roommates" toKeyPath:@"friends" withMapping:humanMapping]]; RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore]; RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore]; parentMapping.identificationAttributes = @[ @"railsID" ]; [parentMapping addAttributeMappingsFromDictionary:@{ @"name": @"name", @"humanID": @"railsID" }]; [parentMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"children" toKeyPath:@"children" withMapping:childMapping]]; [dynamicMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:@"invalid" expectedValue:@"whatever" objectMapping:parentMapping]]; [dynamicMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:@"name" expectedValue:@"Blake" objectMapping:humanMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/self_referential.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; RKFetchRequestBlock humanFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Human"]; }; RKFetchRequestBlock parentFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Parent"]; }; RKFetchRequestBlock childFetchRequestBlock = ^NSFetchRequest * (NSURL *URL) { return [NSFetchRequest fetchRequestWithEntityName:@"Child"]; }; managedObjectRequestOperation.fetchRequestBlocks = @[ humanFetchRequestBlock, parentFetchRequestBlock, childFetchRequestBlock ]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.deletesOrphanedObjects = YES; RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new]; managedObjectRequestOperation.managedObjectCache = managedObjectCache; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); } - (void)testThatMappingObjectsWithTheSameIdentificationAttributesAcrossTwoObjectRequestOperationConcurrentlyDoesNotCreateDuplicateObjects { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKInMemoryManagedObjectCache *inMemoryCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; managedObjectStore.managedObjectCache = inMemoryCache; RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; mapping.identificationAttributes = @[ @"railsID" ]; [mapping addAttributeMappingsFromArray:@[ @"name" ]]; [mapping addAttributeMappingsFromDictionary:@{ @"id": @"railsID" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:@"human" statusCodes:nil]; NSURL *URL = [NSURL URLWithString:@"humans/1" relativeToURL:[RKTestFactory baseURL]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; RKManagedObjectRequestOperation *firstOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; firstOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; firstOperation.managedObjectCache = inMemoryCache; RKManagedObjectRequestOperation *secondOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; secondOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; secondOperation.managedObjectCache = inMemoryCache; NSOperationQueue *operationQueue = [NSOperationQueue new]; [operationQueue setMaxConcurrentOperationCount:2]; [operationQueue setSuspended:YES]; [operationQueue addOperation:firstOperation]; [operationQueue addOperation:secondOperation]; // Start both operations [operationQueue setSuspended:NO]; [operationQueue waitUntilAllOperationsAreFinished]; // Now pull the count back from the parent context NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"railsID == 1"]; NSArray *fetchedObjects = [managedObjectStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:nil]; expect(fetchedObjects).to.haveCountOf(1); } - (void)testManagedObjectRequestOperationCompletesAndIgnoresInvalidObjectsWhenDiscardsInvalidObjectsOnInsertIsYES { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:@"Post" inManagedObjectStore:managedObjectStore]; postMapping.discardsInvalidObjectsOnInsert = YES; [postMapping addAttributeMappingsFromArray:@[ @"title", @"body" ]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:@"posts" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/posts_with_invalid.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect([managedObjectRequestOperation.mappingResult array]).to.haveCountOf(1); } - (void)testManagedObjectRequestOperationFailsWithValidationErrorWhenDiscardsInvalidObjectsOnInsertIsNO { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:@"Post" inManagedObjectStore:managedObjectStore]; postMapping.discardsInvalidObjectsOnInsert = NO; [postMapping addAttributeMappingsFromArray:@[ @"title", @"body" ]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:@"posts" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/posts_with_invalid.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).notTo.beNil(); expect([managedObjectRequestOperation.error code]).to.equal(NSValidationMissingMandatoryPropertyError); #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED expect([managedObjectRequestOperation.error localizedDescription]).to.equal(@"title is a required value."); #else expect([managedObjectRequestOperation.error localizedDescription]).to.equal(@"The operation couldn’t be completed. (Cocoa error 1570.)"); #endif } - (void)testThatSuccessfulCompletionSavesManagedObjectIfTargetObjectIsUnsavedEvenIfNoMappingWasPerformed { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext withProperties:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/204" relativeToURL:[RKTestFactory baseURL]]]; request.HTTPMethod = @"POST"; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.targetObject = human; [managedObjectRequestOperation start]; [managedObjectRequestOperation waitUntilFinished]; expect(managedObjectRequestOperation.error).to.beNil(); [managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{ expect([human isNew]).to.equal(NO); }]; } - (void)testThatMapperOperationDelegateIsPassedThroughToUnderlyingMapperOperation { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKTestDelegateManagedObjectRequestOperation *managedObjectRequestOperation = [[RKTestDelegateManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; id mockOperation = [OCMockObject partialMockForObject:managedObjectRequestOperation]; [[mockOperation expect] mapperWillStartMapping:OCMOCK_ANY]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect([managedObjectRequestOperation isFinished]).will.beTruthy(); [mockOperation verify]; } - (void)testThatRefetchingOfNestedNonManagedAndManagedObjectsWorksWithHasOneRelations { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKObjectMapping *itemMapping = [RKObjectMapping mappingForClass:[RKMappableObject class]]; RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; [userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"bestFriend" withMapping:entityMapping]]; [itemMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"items.human" toKeyPath:@"hasMany" withMapping:userMapping]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:itemMapping pathPattern:nil keyPath:@"result" statusCodes:[NSIndexSet indexSetWithIndex:200]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/has_many_with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); RKMappableObject *result = [managedObjectRequestOperation.mappingResult.array lastObject]; RKTestUser *user = (RKTestUser *)result.hasMany.anyObject; NSLog(@"Examining result = %@, with result.hasMany = %@, user = %@ and user.bestFriend(%@) = %@", result, result.hasMany, user, [user.bestFriend class], user.bestFriend); expect(user.bestFriend).to.beInstanceOf([RKHuman class]); expect([user.bestFriend managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext); } - (void)testThatAnEmptyResultHasTheProperManagedObjectContext { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromArray:@[ @"name" ]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/empty_human.json" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); RKHuman *result = [managedObjectRequestOperation.mappingResult.array lastObject]; expect(managedObjectRequestOperation.managedObjectContext).to.equal(result.managedObjectContext); } - (void)testMappingMetadataConfiguredOnTheOperation { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"@metadata.nickName": @"nickName" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/1" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; managedObjectRequestOperation.mappingMetadata = @{ @"nickName": @"Big Sleezy" }; [managedObjectRequestOperation start]; expect(managedObjectRequestOperation.error).to.beNil(); expect(managedObjectRequestOperation.mappingResult).notTo.beNil(); RKHuman *human = [managedObjectRequestOperation.mappingResult firstObject]; expect(human.nickName).to.equal(@"Big Sleezy"); } - (void)testCopyingOperation { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"@metadata.nickName": @"nickName" }]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans/1" relativeToURL:[RKTestFactory baseURL]]]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext; RKManagedObjectRequestOperation *copiedOperation = [managedObjectRequestOperation copy]; copiedOperation.mappingMetadata = @{ @"nickName": @"Big Sleezy" }; [copiedOperation start]; expect(copiedOperation.error).to.beNil(); expect(copiedOperation.mappingResult).notTo.beNil(); RKHuman *human = [copiedOperation.mappingResult firstObject]; expect(human.nickName).to.equal(@"Big Sleezy"); } - (void)testThatManuallyCreatedObjectsAreNotDuplicatedWhenMappedWithInMemoryManagedObjectCache { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore]; [entityMapping addAttributeMappingsFromDictionary:@{ @"id": @"railsID", @"name": @"name" }]; entityMapping.identificationAttributes = @[ @"railsID" ]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"human" statusCodes:nil]; RKInMemoryManagedObjectCache *managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext]; RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext]; human.railsID = @1; [managedObjectStore.mainQueueManagedObjectContext saveToPersistentStore:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"/humans" relativeToURL:[RKTestFactory baseURL]]]; [request setHTTPMethod:@"POST"]; RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]]; managedObjectRequestOperation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext; managedObjectRequestOperation.managedObjectCache = managedObjectCache; NSOperationQueue *operationQueue = [NSOperationQueue new]; [operationQueue addOperation:managedObjectRequestOperation]; expect(managedObjectRequestOperation.isFinished).will.equal(YES); RKHuman *mappedHuman = [managedObjectRequestOperation.mappingResult firstObject]; expect(mappedHuman).to.equal(human); } @end