//
//  RKManagedObjectMappingOperationDataSourceTest.m
//  RestKit
//
//  Created by Blake Watters on 7/12/12.
//  Copyright (c) 2012 RestKit. All rights reserved.
//

#import "RKTestEnvironment.h"
#import "RKEntityCache.h"
#import "RKEntityByAttributeCache.h"
#import "RKHuman.h"
#import "RKManagedObjectMappingOperationDataSource.h"
#import "RKMappableObject.h"
#import "RKMappingErrors.h"
#import "RKCat.h"
#import "RKHuman.h"
#import "RKChild.h"
#import "RKParent.h"
#import "RKBenchmark.h"

@interface RKManagedObjectMappingOperationDataSourceTest : RKTestCase

@end

/**
 NOTE: You need to take care that you allow the operationQueue to finish before the next test begins execution, else the Core Data tear down can result in intermittent test crashes.
 */
@implementation RKManagedObjectMappingOperationDataSourceTest

- (void)setUp
{
    [RKTestFactory setUp];
}

- (void)tearDown
{
    [RKTestFactory tearDown];
}

- (void)testShouldCreateNewInstancesOfUnmanagedObjects
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKMappableObject class]];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:[NSDictionary dictionary] withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat([object class], is(equalTo([RKMappableObject class])));
}

- (void)testShouldCreateNewManagedObjectInstancesWhenThereIsNoPrimaryKeyInTheData
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    
    NSDictionary *data = [NSDictionary dictionary];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(instanceOf([RKHuman class])));
}

- (void)testShouldCreateNewManagedObjectInstancesWhenThereIsNoPrimaryKeyAttribute
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    
    NSDictionary *data = [NSDictionary dictionary];    
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(instanceOf([RKHuman class])));
}

- (void)testShouldCreateANewManagedObjectWhenThePrimaryKeyValueIsNSNull
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"id" toKeyPath:@"railsID"]];
    
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"id"];    
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(instanceOf([RKHuman class])));
}

#pragma mark - Fetched Results Cache

- (void)testShouldFindExistingManagedObjectsByPrimaryKeyWithFetchedResultsCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"id" toKeyPath:@"railsID"]];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = [NSNumber numberWithInt:123];
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testShouldFindExistingManagedObjectsByPrimaryKeyPathWithFetchedResultsCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.id" toKeyPath:@"railsID"]];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = [NSNumber numberWithInt:123];
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];
    NSDictionary *nestedDictionary = [NSDictionary dictionaryWithObject:data forKey:@"monkey"];
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:nestedDictionary withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testUsingDateAsIdentifierAttributeWithFetchRequestCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"createdAt" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.created_at" toKeyPath:@"createdAt"]];
    
    NSString *createdAtString = @"2012-03-22T11:05:42Z";
    NSDate *createdAtDate = RKDateFromString(createdAtString);
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.createdAt = createdAtDate;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    NSDictionary *representation = @{ @"monkey": @{ @"created_at": createdAtString } };
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

#pragma mark - In Memory Cache

- (void)testShouldFindExistingManagedObjectsByPrimaryKeyWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"id" toKeyPath:@"railsID"]];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = [NSNumber numberWithInt:123];
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    // Check the count
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];    
    NSManagedObject *object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    expect([object managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext);
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testShouldFindExistingManagedObjectsByPrimaryKeyPathWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.id" toKeyPath:@"railsID"]];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = [NSNumber numberWithInt:123];
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    // Check the count
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    assertThat([NSNumber numberWithInteger:count], is(greaterThan([NSNumber numberWithInteger:0])));
    
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];
    NSDictionary *nestedDictionary = [NSDictionary dictionaryWithObject:data forKey:@"monkey"];
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:nestedDictionary withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(equalTo(human)));
}

- (void)testUsingDateAsIdentifierAttributeWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"createdAt" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.created_at" toKeyPath:@"createdAt"]];
    
    NSString *createdAtString = @"2012-03-22T11:05:42Z";
    NSDate *createdAtDate = RKDateFromString(createdAtString);
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.createdAt = createdAtDate;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    NSDictionary *representation = @{ @"monkey": @{ @"created_at": createdAtString } };
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testThatCreationOfNewObjectWithIncorrectTypeValueForPrimaryKeyAddsToCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.name" toKeyPath:@"name"]];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"monkey.railsID" toKeyPath:@"railsID"]];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Testing";
    human.railsID = [NSNumber numberWithInteger:12345];
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    // Check the count
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    assertThatInteger(count, is(greaterThan([NSNumber numberWithInteger:0])));
    
    NSDictionary *data = [NSDictionary dictionaryWithObject:@"12345" forKey:@"railsID"];
    NSDictionary *nestedDictionary = [NSDictionary dictionaryWithObject:data forKey:@"monkey"];
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    RKHuman *object = [dataSource mappingOperation:nil targetObjectForRepresentation:nestedDictionary withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(equalTo(human)));
    assertThatInteger([object.railsID integerValue], is(equalToInteger(12345)));
}

- (void)testThatMappingAnEntityMappingContainingAConnectionMappingWithANilManagedObjectCacheTriggersError
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
    RKConnectionDescription *connection = [[RKConnectionDescription alloc] initWithRelationship:entity.relationshipsByName[@"favoriteCat"] attributes:@{ @"railsID": @"railsID" }];
    [mapping addConnection:connection];
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:nil];
    id mockOperation = [OCMockObject mockForClass:[RKMappingOperation class]];
    [[mockOperation stub] destinationObject];
    [[[mockOperation stub] andReturn:mapping] objectMapping];
    NSError *error = nil;
    BOOL success = [dataSource commitChangesForMappingOperation:mockOperation error:&error];
    expect(success).to.beFalsy();
    expect([error code]).to.equal(RKMappingErrorNilManagedObjectCache);
    expect([error localizedDescription]).to.equal(@"Cannot map an entity mapping that contains connection mappings with a data source whose managed object cache is nil.");
}

#pragma mark - Rearrange Me

- (void)testCompoundEntityIdentifierWithFetchRequestCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID", @"name", @"createdAt" ];
    [mapping addAttributeMappingsFromDictionary:@{ @"monkey.id": @"railsID", @"monkey.name": @"name", @"monkey.created_at": @"createdAt" }];
    
    NSString *createdAtString = @"2012-03-22T11:05:42Z";
    NSDate *createdAtDate = RKDateFromString(createdAtString);
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = @(12345);
    human.createdAt = createdAtDate;
    human.name = @"Reginald";
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    // Check the count
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    assertThat([NSNumber numberWithInteger:count], is(greaterThan([NSNumber numberWithInteger:0])));
    
    NSDictionary *representation = @{ @"monkey": @{ @"id": @"12345", @"name": @"Reginald", @"created_at": createdAtString } };
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(equalTo(human)));
}

- (void)testCompoundEntityIdentifierWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID", @"name", @"createdAt" ];
    [mapping addAttributeMappingsFromDictionary:@{ @"monkey.id": @"railsID", @"monkey.name": @"name", @"monkey.created_at": @"createdAt" }];
    
    NSString *createdAtString = @"2012-03-22T11:05:42Z";
    NSDate *createdAtDate = RKDateFromString(createdAtString);
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.railsID = @(12345);
    human.createdAt = createdAtDate;
    human.name = @"Reginald";
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    // Check the count
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    assertThat([NSNumber numberWithInteger:count], is(greaterThan([NSNumber numberWithInteger:0])));
    
    NSDictionary *representation = @{ @"monkey": @{ @"id": @"12345", @"name": @"Reginald", @"created_at": createdAtString } };
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    assertThat(object, isNot(nilValue()));
    assertThat(object, is(equalTo(human)));
}

- (void)testRetrievalOfTargetObjectInWhichIdentifierAttributeIsDynamicNestingKey
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"name" ];
    [mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"name"];
    [mapping addAttributeMappingsFromDictionary:@{ @"(name).id": @"railsID" }];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Blake";
    human.railsID = @(12345);
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *representation = @{ @"Blake": @{ @"id": @"12345" } };
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testRetrievalOfTargetObjectWithDynamicNestingKeyMappingInWhichIdentifierAttributeIsNotTheDynamicNestingKey
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"name"];
    [mapping addAttributeMappingsFromDictionary:@{ @"(name).id": @"railsID" }];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Blake";
    human.railsID = @(12345);
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *representation = @{ @"Blake": @{ @"id": @"12345" } };
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testRetrievalOfTargetObjectInWhichIdentifierAttributeIsCompoundAndOneAttributeIsTheDynamicNestingKey
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"name", @"railsID" ];
    [mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"name"];
    [mapping addAttributeMappingsFromDictionary:@{ @"(name).id": @"railsID" }];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Blake";
    human.railsID = @(12345);
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *representation = @{ @"Blake": @{ @"id": @"12345" } };
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testRetrievalOfTargetObjectInWhichIdentifierAttributeIsCompoundAndOneAttributeIsTheDynamicNestingKeyAndItIsNotTheFirstIdentifierAttribute
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID", @"name" ];
    [mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"name"];
    [mapping addAttributeMappingsFromDictionary:@{ @"(name).id": @"railsID" }];
    
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Blake";
    human.railsID = @(12345);
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *representation = @{ @"Blake": @{ @"id": @"12345" } };
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human);
}

- (void)testEntityIdentifierWithPredicate
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    managedObjectStore.managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    mapping.identificationPredicate = [NSPredicate predicateWithFormat:@"age < 30"];
    [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"id" toKeyPath:@"railsID"]];
    
    // Create two humans matching the identifier, but differ in matching the 
    RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human1.name = @"Colin";
    human1.railsID = [NSNumber numberWithInt:123];
    human1.age = @28;
    
    RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human2.name = @"Blake";
    human2.railsID = [NSNumber numberWithInt:123];
    human2.age = @30;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];
    
    NSError *error = nil;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    NSUInteger count = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    expect(count).to.beGreaterThan(0);
    
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:managedObjectStore.managedObjectCache];
    NSDictionary *data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];
    id object = [dataSource mappingOperation:nil targetObjectForRepresentation:data withMapping:mapping inRelationship:nil];
    expect(object).notTo.beNil();
    expect(object).to.equal(human1);
}

- (void)testMappingInPrivateQueue
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    managedObjectContext.parentContext = managedObjectStore.persistentStoreManagedObjectContext;
    managedObjectContext.mergePolicy  = NSMergeByPropertyStoreTrumpMergePolicy;
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectContext
                                                                                                                                                      cache:managedObjectCache];

    __block BOOL success;
    __block NSError *error;
    NSDictionary *sourceObject = @{ @"name" : @"Blake Watters" };
    [managedObjectContext performBlockAndWait:^{
        RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
        humanMapping.identificationAttributes = @[ @"railsID" ];
        [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectContext];
        RKHuman *human = [[RKHuman alloc] initWithEntity:entity insertIntoManagedObjectContext:managedObjectContext];
        RKMappingOperation *mappingOperation = [[RKMappingOperation alloc] initWithSourceObject:sourceObject destinationObject:human mapping:humanMapping];
        mappingOperation.dataSource = mappingOperationDataSource;
        success = [mappingOperation performMapping:&error];

        assertThatBool(success, is(equalToBool(YES)));
        assertThat(human.name, is(equalTo(@"Blake Watters")));
    }];
}

- (void)testShouldConnectRelationshipsByPrimaryKey
{
    /* Connect a new human to a cat */
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [humanMapping addConnectionForRelationship:@"favoriteCat" connectedBy:@{ @"favoriteCatID": @"railsID" }];

    // Create a cat to connect
    RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    cat.name = @"Asia";
    cat.railsID = @31337;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSDictionary *mappableData = @{ @"name": @"Blake", @"favoriteCatID": @31337 };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(human.favoriteCat, isNot(nilValue()));
    assertThat(human.favoriteCat.name, is(equalTo(@"Asia")));
    
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
}

- (void)testShouldConnectRelationshipsByPrimaryKeyReverse
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSManagedObjectContext *managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext;

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name", @"railsID"]];
    [catMapping addConnectionForRelationship:@"favoriteOfHumans" connectedBy:@{ @"railsID": @"favoriteCatID" }];

    // Create some humans to connect
    RKHuman *blake = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    blake.name = @"Blake";
    blake.favoriteCatID = [NSNumber numberWithInt:31340];

    RKHuman *jeremy = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    jeremy.name = @"Jeremy";
    jeremy.favoriteCatID = [NSNumber numberWithInt:31340];

    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSDictionary *mappableData = @{ @"name": @"Asia", @"railsID": @31340 };
    RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    NSOperationQueue *operationQueue = [NSOperationQueue new];
    [operationQueue setSuspended:YES];
    mappingOperationDataSource.operationQueue = operationQueue;
    __block BOOL success;
    [managedObjectContext performBlockAndWait:^{
        RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:cat mapping:catMapping];
        operation.dataSource = mappingOperationDataSource;
        NSError *error = nil;
        success = [operation performMapping:&error];
    }];

    [operationQueue setSuspended:NO];
    [operationQueue waitUntilAllOperationsAreFinished];

    assertThatBool(success, is(equalToBool(YES)));
    assertThat(cat.favoriteOfHumans, isNot(nilValue()));
    assertThat([cat.favoriteOfHumans valueForKeyPath:@"name"], containsInAnyOrder(blake.name, jeremy.name, nil));
}

- (void)testConnectionOfHasManyRelationshipsByPrimaryKey
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [humanMapping addConnectionForRelationship:@"favoriteCat" connectedBy:@{ @"favoriteCatID": @"railsID" }];

    // Create a cat to connect
    RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    cat.name = @"Asia";
    cat.railsID = @31337;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSDictionary *mappableData = @{ @"name": @"Blake", @"favoriteCatID": @31337 };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    assertThat(human.favoriteCat, isNot(nilValue()));
    assertThat(human.favoriteCat.name, is(equalTo(@"Asia")));
}

- (void)testConnectingARelationshipFromASourceAttributeWhoseValueIsACollectionWithFetchRequestCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"catIDs"]];
    [humanMapping addConnectionForRelationship:@"cats" connectedBy:@{ @"catIDs": @"railsID" }];

    // Create a couple of cats to connect
    RKCat *asia = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    asia.name = @"Asia";
    asia.railsID = @31337;

    RKCat *roy = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    roy.name = @"Reginald Royford Williams III";
    roy.railsID = [NSNumber numberWithInt:31338];

    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSArray *catIDs = [NSArray arrayWithObjects:@31337, [NSNumber numberWithInt:31338], nil];
    NSDictionary *mappableData = @{ @"name": @"Blake", @"catIDs": catIDs };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(human.cats, isNot(nilValue()));
    assertThat([human.cats valueForKeyPath:@"name"], containsInAnyOrder(@"Asia", @"Reginald Royford Williams III", nil));
}

- (void)testConnectingARelationshipFromASourceAttributeWhoseValueIsACollectionWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"catIDs"]];
    [humanMapping addConnectionForRelationship:@"cats" connectedBy:@{ @"catIDs": @"railsID" }];

    // Create a couple of cats to connect
    RKCat *asia = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    asia.name = @"Asia";
    asia.railsID = @31337;

    RKCat *roy = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    roy.name = @"Reginald Royford Williams III";
    roy.railsID = [NSNumber numberWithInt:31338];

    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSArray *catIDs = [NSArray arrayWithObjects:@31337, [NSNumber numberWithInt:31338], nil];
    NSDictionary *mappableData = @{ @"name": @"Blake", @"catIDs": catIDs };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

    RKInMemoryManagedObjectCache *managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(human.cats, isNot(nilValue()));
    assertThat([human.cats valueForKeyPath:@"name"], containsInAnyOrder(@"Asia", @"Reginald Royford Williams III", nil));
}

- (void)testShouldConnectRelationshipsByPrimaryKeyWithDifferentSourceAndDestinationKeyPathsReverse
{
    /* Connect a new cat to a human */
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"railsID"]];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name", @"humanId"]];
    [catMapping addConnectionForRelationship:@"human" connectedBy:@{ @"humanId": @"railsID" }];

    // Create a human to connect
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.name = @"Blake";
    human.railsID = @31337;
    [managedObjectStore.persistentStoreManagedObjectContext save:nil];

    NSDictionary *mappableData = @{ @"name": @"Asia", @"humanId": @31337 };
    RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation* operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:cat mapping:catMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(cat.human, isNot(nilValue()));
    assertThat(cat.human.name, is(equalTo(@"Blake")));
}

- (void)testShouldLoadNestedHasManyRelationship
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [humanMapping addRelationshipMappingWithSourceKeyPath:@"cats" mapping:catMapping];

    NSArray *catsData = [NSArray arrayWithObject:[NSDictionary dictionaryWithObject:@"Asia" forKey:@"name"]];
    NSDictionary *mappableData = @{ @"name": @"Blake", @"favoriteCatID": @31337, @"cats": catsData };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
}

- (void)testShouldLoadOrderedHasManyRelationship
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    humanMapping.identificationAttributes = @[ @"railsID" ];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"cats" toKeyPath:@"catsInOrderByAge" withMapping:catMapping]];;

    NSArray *catsData = [NSArray arrayWithObject:[NSDictionary dictionaryWithObject:@"Asia" forKey:@"name"]];
    NSDictionary *mappableData = @{ @"name": @"Blake", @"favoriteCatID": @31337, @"cats": catsData };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat([human catsInOrderByAge], isNot(empty()));
}

- (void)testShouldMapNullToAHasManyRelationship
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    [catMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    [humanMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [humanMapping addRelationshipMappingWithSourceKeyPath:@"cats" mapping:catMapping];

    NSDictionary *mappableData = @{ @"name": @"Blake", @"cats": [NSNull null] };
    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(human.cats, is(empty()));
}

- (void)testShouldLoadNestedHasManyRelationshipWithoutABackingClass
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKEntityMapping *cloudMapping = [RKEntityMapping mappingForEntityForName:@"Cloud" inManagedObjectStore:managedObjectStore];
    [cloudMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *stormMapping = [RKEntityMapping mappingForEntityForName:@"Storm" inManagedObjectStore:managedObjectStore];
    [stormMapping addAttributeMappingsFromArray:@[@"name", @"favoriteCatID"]];
    [stormMapping addRelationshipMappingWithSourceKeyPath:@"clouds" mapping:cloudMapping];

    NSArray *cloudsData = [NSArray arrayWithObject:[NSDictionary dictionaryWithObject:@"Nimbus" forKey:@"name"]];
    NSDictionary *mappableData = @{ @"name": @"Hurricane", @"clouds": cloudsData };
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Storm" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    NSManagedObject *storm = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:storm mapping:stormMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
}

- (void)testShouldConnectManyToManyRelationships
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    childMapping.identificationAttributes = @[ @"name" ];
    [childMapping addAttributeMappingsFromArray:@[@"name"]];

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    parentMapping.identificationAttributes = @[ @"railsID" ];
    [parentMapping addAttributeMappingsFromArray:@[@"name", @"age"]];
    [parentMapping addRelationshipMappingWithSourceKeyPath:@"children" mapping:childMapping];

    NSArray *childMappableData = @[ @{ @"name": @"Maya" }, @{ @"name": @"Brady" } ];
    NSDictionary *parentMappableData = @{ @"name": @"Win", @"age": @34, @"children": childMappableData };
    RKParent *parent = [NSEntityDescription insertNewObjectForEntityForName:@"Parent" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:parentMappableData destinationObject:parent mapping:parentMapping];
    operation.dataSource = mappingOperationDataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
    assertThat(parent.children, isNot(nilValue()));
    assertThatUnsignedInteger([parent.children count], is(equalToInt(2)));
    assertThat([[parent.children anyObject] parents], isNot(nilValue()));
    assertThatBool([[[parent.children anyObject] parents] containsObject:parent], is(equalToBool(YES)));
    assertThatUnsignedInteger([[[parent.children anyObject] parents] count], is(equalToInt(1)));
    
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
}

- (void)testShouldConnectRelationshipsByPrimaryKeyRegardlessOfOrder
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSManagedObjectContext *managedObjectContext = managedObjectStore.persistentStoreManagedObjectContext;

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    [parentMapping addAttributeMappingsFromArray:@[@"parentID"]];
    parentMapping.identificationAttributes = @[ @"parentID" ];

    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    [childMapping addAttributeMappingsFromArray:@[@"fatherID"]];
    [childMapping addConnectionForRelationship:@"father" connectedBy:@{ @"fatherID": @"parentID" }];

    NSDictionary *mappingsDictionary = @{ @"parents": parentMapping, @"children": childMapping };
    NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"ConnectingParents.json"];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    NSOperationQueue *operationQueue = [NSOperationQueue new];
    [operationQueue setSuspended:YES];
    mappingOperationDataSource.operationQueue = operationQueue;
    [managedObjectContext performBlockAndWait:^{
        RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:JSON mappingsDictionary:mappingsDictionary];
        mapper.mappingOperationDataSource = mappingOperationDataSource;
        [mapper start];
    }];

    [operationQueue setSuspended:NO];
    [operationQueue waitUntilAllOperationsAreFinished];

    [managedObjectContext performBlockAndWait:^{
        NSError *error;
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Parent"];
        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"parentID = %@", @1];
        fetchRequest.fetchLimit = 1;
        NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
        RKParent *parent = [results lastObject];
        assertThat(parent, is(notNilValue()));
        NSSet *children = [parent fatheredChildren];
        assertThat(children, hasCountOf(1));
        RKChild *child = [children anyObject];
        assertThat(child.father, is(notNilValue()));
    }];
}

- (void)testMappingAPayloadContainingRepeatedObjectsDoesNotYieldDuplicatesWithFetchRequestMappingCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    managedObjectStore.managedObjectCache = managedObjectCache;

    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    childMapping.identificationAttributes = @[ @"childID" ];
    [childMapping addAttributeMappingsFromArray:@[@"name", @"childID"]];

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    [parentMapping addAttributeMappingsFromArray:@[@"parentID", @"name"]];
    parentMapping.identificationAttributes = @[ @"parentID" ];
    [parentMapping addRelationshipMappingWithSourceKeyPath:@"children" mapping:childMapping];

    NSDictionary *mappingsDictionary = @{ @"parents": parentMapping };

    NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"parents_and_children.json"];
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:JSON mappingsDictionary:mappingsDictionary];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    [mapper start];

    NSUInteger parentCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Parent" predicate:nil error:nil];
    NSUInteger childrenCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Child" predicate:nil error:nil];
    assertThatInteger(parentCount, is(equalToInteger(2)));
    assertThatInteger(childrenCount, is(equalToInteger(4)));
}

- (void)testMappingAPayloadContainingRepeatedObjectsDoesNotYieldDuplicatesWithInMemoryMappingCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKInMemoryManagedObjectCache *managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    managedObjectStore.managedObjectCache = managedObjectCache;

    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    childMapping.identificationAttributes = @[ @"childID" ];
    [childMapping addAttributeMappingsFromArray:@[@"name", @"childID"]];

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    [parentMapping addAttributeMappingsFromArray:@[@"parentID", @"name"]];
    parentMapping.identificationAttributes = @[ @"parentID" ];
    [parentMapping addRelationshipMappingWithSourceKeyPath:@"children" mapping:childMapping];

    // NOTE: This may be fragile. Reverse order seems to trigger them to be mapped parent first. NSDictionary
    // keys are not guaranteed to return in any particular order
    NSDictionary *mappingsDictionary = @{ @"parents": parentMapping };

    NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"parents_and_children.json"];
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:JSON mappingsDictionary:mappingsDictionary];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    [mapper start];

    NSError *error = nil;
    BOOL success = [managedObjectStore.persistentStoreManagedObjectContext save:&error];
    assertThatBool(success, is(equalToBool(YES)));
    NSLog(@"Failed to save MOC: %@", error);
    assertThat(error, is(nilValue()));

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Parent"];
    NSUInteger parentCount = [managedObjectStore.persistentStoreManagedObjectContext countForFetchRequest:fetchRequest error:&error];
    NSUInteger childrenCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Child" predicate:nil error:nil];
    assertThatInteger(parentCount, is(equalToInteger(2)));
    assertThatInteger(childrenCount, is(equalToInteger(4)));
}

- (void)testThatMappingObjectsWithTheSameIdentificationAttributesAcrossTwoContextsDoesNotCreateDuplicateObjects
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKInMemoryManagedObjectCache *inMemoryCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    managedObjectStore.managedObjectCache = inMemoryCache;
    NSEntityDescription *humanEntity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    mapping.identificationAttributes = @[ @"railsID" ];
    [mapping addAttributeMappingsFromArray:@[ @"name", @"railsID" ]];
    
    // Create two contexts with common parent
    NSManagedObjectContext *firstContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType];
    NSManagedObjectContext *secondContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType];        
    
    // Map into the first context
    NSDictionary *objectRepresentation = @{ @"name": @"Blake", @"railsID": @(31337) };
    
    // Check that the cache contains a value for our identification attributes
    __block BOOL success;
    __block NSError *error;
    [firstContext performBlockAndWait:^{
        RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:firstContext
                                                                                                                                          cache:inMemoryCache];
        RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:objectRepresentation mappingsDictionary:@{ [NSNull null]: mapping }];
        mapperOperation.mappingOperationDataSource = dataSource;
        success = [mapperOperation execute:&error];
        expect(success).to.equal(YES);
        expect([mapperOperation.mappingResult count]).to.equal(1);
        
        [firstContext save:nil];
    }];
    
    // Check that there is an entry in the cache
    NSSet *objects = [inMemoryCache managedObjectsWithEntity:humanEntity attributeValues:@{ @"railsID": @(31337) } inManagedObjectContext:firstContext];
    expect(objects).to.haveCountOf(1);
    
    // Map into the second context
    [secondContext performBlockAndWait:^{
        RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:secondContext
                                                                                                                                          cache:inMemoryCache];
        RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:objectRepresentation mappingsDictionary:@{ [NSNull null]: mapping }];
        mapperOperation.mappingOperationDataSource = dataSource;
        success = [mapperOperation execute:&error];
        expect(success).to.equal(YES);
        expect([mapperOperation.mappingResult count]).to.equal(1);
        
        [secondContext save:nil];
    }];
    
    // Now check the count
    objects = [inMemoryCache managedObjectsWithEntity:humanEntity attributeValues:@{ @"railsID": @(31337) } inManagedObjectContext:secondContext];
    expect(objects).to.haveCountOf(1);
    
    // Now pull the count back from the parent context
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"railsID == 31337"];
    NSArray *fetchedObjects = [managedObjectStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:nil];
    expect(fetchedObjects).to.haveCountOf(1);
}

- (void)testConnectingToSubentitiesByFetchRequestCache
{
    
}

- (void)testConnectingToSubentitiesByInMemoryCache
{
    
}

- (void)testDeletionOfTombstoneRecords
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
    [mapping addAttributeMappingsFromArray:@[ @"name" ]];
    mapping.deletionPredicate = [NSPredicate predicateWithFormat:@"sex = %@", @"female"];

    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.sex = @"female";

    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:nil];
    NSDictionary *representation = @{ @"name": @"Whatever" };
    RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:representation destinationObject:human mapping:mapping];
    operation.dataSource = dataSource;
    NSError *error = nil;
    BOOL success = [operation performMapping:&error];
    assertThatBool(success, is(equalToBool(YES)));
    expect([human isDeleted]).to.equal(YES);
}

- (void)testDeletionOfTombstoneRecordsInMapperOperation
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
    [mapping addAttributeMappingsFromArray:@[ @"name" ]];
    mapping.deletionPredicate = [NSPredicate predicateWithFormat:@"sex = %@", @"female"];

    RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    human.sex = @"female";

    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                      cache:nil];
    NSDictionary *representation = @{ @"name": @"Whatever" };
    NSError *error = nil;
    RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ [NSNull null]: mapping }];
    mapperOperation.mappingOperationDataSource = dataSource;
    BOOL success = [mapperOperation execute:&error];
    assertThatBool(success, is(equalToBool(YES)));
    expect([human isDeleted]).to.equal(YES);
}

- (void)testMappingAPayloadContainingRepeatedObjectsPerformsAcceptablyWithFetchRequestMappingCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    managedObjectStore.managedObjectCache = managedObjectCache;

    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    childMapping.identificationAttributes = @[ @"childID" ];
    [childMapping addAttributeMappingsFromArray:@[@"name", @"childID"]];

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    [parentMapping addAttributeMappingsFromArray:@[@"parentID", @"name"]];
    parentMapping.identificationAttributes = @[ @"parentID" ];
    [parentMapping addRelationshipMappingWithSourceKeyPath:@"children" mapping:childMapping];


    NSDictionary *mappingsDictionary = @{ @"parents": parentMapping };
    NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"benchmark_parents_and_children.json"];
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:JSON mappingsDictionary:mappingsDictionary];
    mapper.mappingOperationDataSource = mappingOperationDataSource;

    RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelOff);
    RKLogConfigureByName("RestKit/CoreData", RKLogLevelOff);

    [RKBenchmark report:@"Mapping with Fetch Request Cache" executionBlock:^{
        for (NSUInteger i = 0; i < 50; i++) {
            [mapper start];
        }
    }];
    NSUInteger parentCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Parent" predicate:nil error:nil];
    NSUInteger childrenCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Child" predicate:nil error:nil];
    assertThatInteger(parentCount, is(equalToInteger(25)));
    assertThatInteger(childrenCount, is(equalToInteger(51)));
}

- (void)testMappingAPayloadContainingRepeatedObjectsPerformsAcceptablyWithInMemoryMappingCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKInMemoryManagedObjectCache *managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    managedObjectStore.managedObjectCache = managedObjectCache;

    RKEntityMapping *childMapping = [RKEntityMapping mappingForEntityForName:@"Child" inManagedObjectStore:managedObjectStore];
    childMapping.identificationAttributes = @[ @"childID" ];
    [childMapping addAttributeMappingsFromArray:@[@"name", @"childID"]];

    RKEntityMapping *parentMapping = [RKEntityMapping mappingForEntityForName:@"Parent" inManagedObjectStore:managedObjectStore];
    [parentMapping addAttributeMappingsFromArray:@[@"parentID", @"name"]];
    parentMapping.identificationAttributes = @[ @"parentID" ];
    [parentMapping addRelationshipMappingWithSourceKeyPath:@"children" mapping:childMapping];

    NSDictionary *mappingsDictionary = @{ @"parents": parentMapping };
    NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"benchmark_parents_and_children.json"];
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:JSON mappingsDictionary:mappingsDictionary];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelOff);
    RKLogConfigureByName("RestKit/CoreData", RKLogLevelOff);

    [RKBenchmark report:@"Mapping with In Memory Cache" executionBlock:^{
        for (NSUInteger i = 0; i < 50; i++) {
            [mapper start];
        }
    }];
    NSUInteger parentCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Parent" predicate:nil error:nil];
    NSUInteger childrenCount = [managedObjectStore.persistentStoreManagedObjectContext countForEntityForName:@"Child" predicate:nil error:nil];
    assertThatInteger(parentCount, is(equalToInteger(25)));
    assertThatInteger(childrenCount, is(equalToInteger(51)));
}

- (void)testMappingIdentificationAttributesFromElementsOnAnArray
{
    NSDictionary *representation = @{
        @"userSessions": @{
            @"name": @"Mr. User",
            @"identification": @54321,
            @"catIDs": @[ @"418", @"419", @"431",@"441", @"457", @"486", @"504" ]
        }
    };
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"railsID"]];
     
    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"identification" toKeyPath:@"railsID"]];
    [humanMapping setIdentificationAttributes:@[ @"railsID" ]];
    [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"catIDs" toKeyPath:@"cats" withMapping:catMapping]];
    
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ @"userSessions": humanMapping }];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    [mapper start];
    
    RKHuman *human = [mapper.mappingResult firstObject];
    expect(human).notTo.beNil();
    expect(human.railsID).to.equal(@54321);
    expect(human.cats).to.haveCountOf(7);
    NSSet *expectedIDs = [NSSet setWithArray:@[ @418, @419, @431, @441, @457, @486, @504 ]];
    expect([human.cats valueForKey:@"railsID"]).to.equal(expectedIDs);
}

- (void)testMappingIdentificationAttributesFromElementsOnAnArrayDoesNotDuplicateManagedObjects
{
    NSDictionary *representation = @{ @"userSessions": @{ @"name": @"Mr. User",
                                                          @"identification": @54321,
                                                          @"catIDs": @[ @"418", @"419", @"431",@"441", @"457", @"486", @"504" ] } };
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];

    NSManagedObject *cat1 = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    [cat1 setValue:@(418) forKey:@"railsID"];
    NSManagedObject *cat2 = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    [cat2 setValue:@(419) forKey:@"railsID"];

    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"railsID"]];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
    [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"identification" toKeyPath:@"railsID"]];
    [humanMapping setIdentificationAttributes:@[ @"railsID" ]];
    [humanMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"catIDs" toKeyPath:@"cats" withMapping:catMapping]];

    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ @"userSessions": humanMapping }];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    [mapper start];

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Cat"];
    [fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"railsID" ascending:YES]]];

    NSPredicate *catOnePredicate  = [NSPredicate predicateWithFormat:@"railsID == 418"];
    [fetchRequest setPredicate:catOnePredicate];
    NSArray *catsWithID418 = [managedObjectStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:NULL];

    NSPredicate *catTwoPredicate = [NSPredicate predicateWithFormat:@"railsID == 419"];
    [fetchRequest setPredicate:catTwoPredicate];
    NSArray *catsWithID419 = [managedObjectStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:NULL];

    expect(catsWithID418).to.haveCountOf(1);
    expect(catsWithID419).to.haveCountOf(1);
}

- (void)testManagedObjectsMappedWithRequiredRelationshipsThatAreSetByConnectionsAreNotPrematurelyDeleted
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];
    
    NSManagedObject *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    [cat setValue:@(12345) forKey:@"railsID"];
    
    NSDictionary *representation = @{ @"name": @"Blake Watters", @"requiredCatID": @(12345) };
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    
    RKEntityMapping *strictHumanMapping = [RKEntityMapping mappingForEntityForName:@"StrictHuman" inManagedObjectStore:managedObjectStore];
    [strictHumanMapping addAttributeMappingsFromDictionary:@{ @"name": @"name" }];
    [strictHumanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"requiredCatID" toKeyPath:@"favoriteCatID"]];
    [strictHumanMapping addConnectionForRelationship:@"requiredCat" connectedBy:@{ @"favoriteCatID": @"railsID" }];
    
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ [NSNull null]: strictHumanMapping }];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    [mapper start];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];
    
    RKHuman *blake = [mapper.mappingResult firstObject];
    expect(blake.name).to.equal(@"Blake Watters");
    expect(blake.managedObjectContext).notTo.beNil();
    expect([blake isDeleted]).to.beFalsy();
    expect([blake valueForKey:@"requiredCat"]).to.equal(cat);
}

- (void)testManagedObjectsMappedWithRelationshipsThatAreSetByConnectionsWithInMemoryCache
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKInMemoryManagedObjectCache *managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];

    NSDictionary *representation = @{ @"human": @{ @"name": @"Blake Watters", @"favoriteCatID": @(12345) }, @"cat": @{ @"railsID": @(12345) } };
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat"
                                                      inManagedObjectStore:managedObjectStore];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catMapping addAttributeMappingsFromDictionary:@{ @"railsID": @"railsID" }];

    RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human"
                                                        inManagedObjectStore:managedObjectStore];
    [humanMapping addAttributeMappingsFromDictionary:@{ @"name": @"name" }];
    [humanMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"favoriteCatID" toKeyPath:@"favoriteCatID"]];
    [humanMapping addConnectionForRelationship:@"favoriteCat" connectedBy:@{ @"favoriteCatID": @"railsID" }];
    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:representation
                                                               mappingsDictionary:@{ @"human": humanMapping , @"cat": catMapping }];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    mappingOperationDataSource.parentOperation = mapper;
    [mapper start];

    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];

    RKCat *cat = [mapper.mappingResult.dictionary objectForKey:@"cat"];
    expect(cat.railsID).to.equal(12345);
    expect(cat.managedObjectContext).notTo.beNil();

    RKHuman *blake = [mapper.mappingResult.dictionary objectForKey:@"human"];
    expect(blake.name).to.equal(@"Blake Watters");
    expect(blake.managedObjectContext).notTo.beNil();
    expect([blake isDeleted]).to.beFalsy();
    expect([blake valueForKey:@"favoriteCat"]).notTo.beNil();
    expect([blake valueForKey:@"favoriteCat"]).to.equal([mapper.mappingResult.dictionary objectForKey:@"cat"]);
}

- (void)testDeletionOperationAfterManagedObjectContextIsDeallocated
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    NSManagedObjectContext *managedObjectContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType];
    RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectContext cache:nil];
    
    
    NSDictionary *representation = @{ @"human": @{ @"name": @"Blake Watters", @"favoriteCatID": @(12345) }, @"cat": @{ @"railsID": @(12345) } };
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat"
                                                      inManagedObjectStore:managedObjectStore];
    catMapping.discardsInvalidObjectsOnInsert = YES;
    RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectContext];
    RKMappingOperation *mappingOperation = [[RKMappingOperation alloc] initWithSourceObject:representation destinationObject:cat mapping:catMapping];
    NSOperationQueue *operationQueue = [NSOperationQueue new];
    [operationQueue setSuspended:YES];
    dataSource.operationQueue = operationQueue;
    
    id mockOperation = [OCMockObject partialMockForObject:mappingOperation];
    [[[mockOperation stub] andReturn:catMapping] objectMapping];
    [dataSource commitChangesForMappingOperation:mockOperation error:nil];
    
    expect([operationQueue operationCount]).to.equal(1);
    dataSource = nil;
    managedObjectContext = nil;
    [operationQueue setSuspended:NO];
    
    [operationQueue waitUntilAllOperationsAreFinished];
    // Create a operation queue
    // Create data source
    
}

- (void)testThatMappingRequiredHasManyRelationshipDoesNotCrash
{
    RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
    RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
    RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
                                                                                                                                                      cache:managedObjectCache];
    mappingOperationDataSource.operationQueue = [NSOperationQueue new];

    NSDictionary *representation = @{ @"name": @"Blake Watters", @"railsID": @123, @"hoardedCats": @[ @{ @"name": @"Asia", @"railsID": @12345 }] };
    RKEntityMapping *catHoarderMapping = [RKEntityMapping mappingForEntityForName:@"CatHoarder" inManagedObjectStore:managedObjectStore];
    [catHoarderMapping addAttributeMappingsFromArray:@[ @"name", @"railsID" ]];
    RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"HoardedCat" inManagedObjectStore:managedObjectStore];
    [catMapping addAttributeMappingsFromArray:@[ @"name", @"railsID" ]];
    catMapping.identificationAttributes = @[ @"railsID" ];
    [catHoarderMapping addRelationshipMappingWithSourceKeyPath:@"hoardedCats" mapping:catMapping];

    RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ [NSNull null]: catHoarderMapping }];
    mapper.mappingOperationDataSource = mappingOperationDataSource;
    mappingOperationDataSource.parentOperation = mapper;
    [mapper start];
    [mappingOperationDataSource.operationQueue waitUntilAllOperationsAreFinished];

    NSManagedObject *catHoarder = [mapper.mappingResult firstObject];
    expect(catHoarder).notTo.beNil();
    expect([catHoarder valueForKeyPath:@"hoardedCats"]).to.haveCountOf(1);
}

@end
