<commit_before>import XCTest
import DATAStack
import Sync
import CoreData

class SyncTests: XCTestCase {
  // MARK: - Camelcase
  func testAutomaticCamelcaseMapping() {
    let dataStack = Helper.dataStackWithModelName("Camelcase")
    let objects = Helper.objectsFromJSON("camelcase.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "NormalUser", dataStack: dataStack, completion: nil)

    let result = Helper.fetchEntity("NormalUser", inContext: dataStack.mainContext)
    XCTAssertEqual(result.count, 1)

    let first = result.first!
    XCTAssertEqual(first.valueForKey("etternavn") as? String, "Nuñez")
    XCTAssertEqual(first.valueForKey("firstName") as? String, "Elvis")
    XCTAssertEqual(first.valueForKey("fullName") as? String, "<NAME>")
    XCTAssertEqual(first.valueForKey("numberOfChildren") as? Int, 1)
    XCTAssertEqual(first.valueForKey("remoteID") as? String, "1")

    try! dataStack.drop()
  }

  // MARK: - Contacts

  func testLoadAndUpdateUsers() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("users_a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext: dataStack.mainContext), 8)

    let objectsB = Helper.objectsFromJSON("users_b.json") as! [[String : AnyObject]]
    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 6)

    let result = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 7)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext: dataStack.mainContext).first!
    XCTAssertEqual(result.valueForKey("email") as? String, "<EMAIL>")

    let dateFormat = NSDateFormatter()
    dateFormat.dateFormat = "yyyy-MM-dd"
    dateFormat.timeZone = NSTimeZone(name: "GMT")

    let createdDate = dateFormat.dateFromString("2014-02-14")
    XCTAssertEqual(result.valueForKey("createdAt") as? NSDate, createdDate);

    let updatedDate = dateFormat.dateFromString("2014-02-17")
    XCTAssertEqual(result.valueForKey("updatedAt") as? NSDate, updatedDate)

    try! dataStack.drop()
  }

  func testUsersAndCompanies() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objects = Helper.objectsFromJSON("users_company.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "User", dataStack: dataStack, completion: nil)

    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 5);
    let user = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 0)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext:dataStack.mainContext).first!
    XCTAssertEqual((user.valueForKey("company")! as? NSManagedObject)!.valueForKey("name") as? String, "Apple")

    XCTAssertEqual(Helper.countForEntity("Company", inContext:dataStack.mainContext), 2)
    let company = Helper.fetchEntity("Company", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 1)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext:dataStack.mainContext).first!
    XCTAssertEqual(company.valueForKey("name") as? String, "Facebook")

    try! dataStack.drop()
  }

  func testCustomMappingAndCustomPrimaryKey() {
    let dataStack = Helper.dataStackWithModelName("Contacts")
    let objects = Helper.objectsFromJSON("images.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "Image", dataStack: dataStack, completion: nil)

    let array = Helper.fetchEntity("Image", sortDescriptors: [NSSortDescriptor(key: "url", ascending: true)], inContext: dataStack.mainContext)
    XCTAssertEqual(array.count, 3)
    let image = array.first
    XCTAssertEqual(image!.valueForKey("url") as? String, "http://sample.com/sample0.png")

    try! dataStack.drop()
  }

  func testRelationshipsB() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objects = Helper.objectsFromJSON("users_c.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 4);

    let users = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 6)), inContext:dataStack.mainContext)
    let user = users.first!
    XCTAssertEqual(user.valueForKey("name") as? String, "<NAME>")

    let location = user.valueForKey("location") as! NSManagedObject
    XCTAssertEqual(location.valueForKey("city") as? String, "New York")
    XCTAssertEqual(location.valueForKey("street") as? String, "Broadway")
    XCTAssertEqual(location.valueForKey("zipCode") as? NSNumber, NSNumber(int: 10012))

    let profilePicturesCount = Helper.countForEntity("Image", predicate: NSPredicate(format: "user = %@", user), inContext:dataStack.mainContext)
    XCTAssertEqual(profilePicturesCount, 3);

    try! dataStack.drop()
  }

  // If all operations where enabled in the first sync, 2 users would be inserted, in the second sync 1 user would be updated
  // and one user deleted. In this test we try only inserting user, no update, no insert, so the second sync should leave us with
  // 2 users with no changes and 1 inserted user.
  func testSyncingWithOnlyInsertOperationType() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("operation-types-users-a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, operations: [.All], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext: dataStack.mainContext), 2)

    let objectsB = Helper.objectsFromJSON("operation-types-users-b.json") as! [[String : AnyObject]]
    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, operations: [.Insert], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 3)

    let result = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 0)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext: dataStack.mainContext).first!
    XCTAssertEqual(result.valueForKey("email") as? String, "<EMAIL>")

    try! dataStack.drop()
  }

  // If all operations where enabled in the first sync, 2 users would be inserted, in the second sync 1 user would be updated
  // and one user deleted. In this test we try only updating users, no insert, no delete, so the second sync should leave us with
  // one updated user and one inserted user, the third user will be discarded.
  func testSyncingWithOnlyUpdateOperationType() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("operation-types-users-a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, operations: [.All], completion: nil)<commit_msg>Add unit test for calling insert multiple times for the same data source<commit_after>import XCTest
import DATAStack
import Sync
import CoreData

class SyncTests: XCTestCase {
  // MARK: - Camelcase
  func testAutomaticCamelcaseMapping() {
    let dataStack = Helper.dataStackWithModelName("Camelcase")
    let objects = Helper.objectsFromJSON("camelcase.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "NormalUser", dataStack: dataStack, completion: nil)

    let result = Helper.fetchEntity("NormalUser", inContext: dataStack.mainContext)
    XCTAssertEqual(result.count, 1)

    let first = result.first!
    XCTAssertEqual(first.valueForKey("etternavn") as? String, "Nuñez")
    XCTAssertEqual(first.valueForKey("firstName") as? String, "Elvis")
    XCTAssertEqual(first.valueForKey("fullName") as? String, "<NAME>")
    XCTAssertEqual(first.valueForKey("numberOfChildren") as? Int, 1)
    XCTAssertEqual(first.valueForKey("remoteID") as? String, "1")

    try! dataStack.drop()
  }

  // MARK: - Contacts

  func testLoadAndUpdateUsers() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("users_a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext: dataStack.mainContext), 8)

    let objectsB = Helper.objectsFromJSON("users_b.json") as! [[String : AnyObject]]
    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 6)

    let result = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 7)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext: dataStack.mainContext).first!
    XCTAssertEqual(result.valueForKey("email") as? String, "<EMAIL>")

    let dateFormat = NSDateFormatter()
    dateFormat.dateFormat = "yyyy-MM-dd"
    dateFormat.timeZone = NSTimeZone(name: "GMT")

    let createdDate = dateFormat.dateFromString("2014-02-14")
    XCTAssertEqual(result.valueForKey("createdAt") as? NSDate, createdDate);

    let updatedDate = dateFormat.dateFromString("2014-02-17")
    XCTAssertEqual(result.valueForKey("updatedAt") as? NSDate, updatedDate)

    try! dataStack.drop()
  }

  func testUsersAndCompanies() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objects = Helper.objectsFromJSON("users_company.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "User", dataStack: dataStack, completion: nil)

    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 5);
    let user = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 0)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext:dataStack.mainContext).first!
    XCTAssertEqual((user.valueForKey("company")! as? NSManagedObject)!.valueForKey("name") as? String, "Apple")

    XCTAssertEqual(Helper.countForEntity("Company", inContext:dataStack.mainContext), 2)
    let company = Helper.fetchEntity("Company", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 1)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext:dataStack.mainContext).first!
    XCTAssertEqual(company.valueForKey("name") as? String, "Facebook")

    try! dataStack.drop()
  }

  func testCustomMappingAndCustomPrimaryKey() {
    let dataStack = Helper.dataStackWithModelName("Contacts")
    let objects = Helper.objectsFromJSON("images.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "Image", dataStack: dataStack, completion: nil)

    let array = Helper.fetchEntity("Image", sortDescriptors: [NSSortDescriptor(key: "url", ascending: true)], inContext: dataStack.mainContext)
    XCTAssertEqual(array.count, 3)
    let image = array.first
    XCTAssertEqual(image!.valueForKey("url") as? String, "http://sample.com/sample0.png")

    try! dataStack.drop()
  }

  func testRelationshipsB() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objects = Helper.objectsFromJSON("users_c.json") as! [[String : AnyObject]]
    Sync.changes(objects, inEntityNamed: "User", dataStack: dataStack, completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 4);

    let users = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 6)), inContext:dataStack.mainContext)
    let user = users.first!
    XCTAssertEqual(user.valueForKey("name") as? String, "<NAME>")

    let location = user.valueForKey("location") as! NSManagedObject
    XCTAssertEqual(location.valueForKey("city") as? String, "New York")
    XCTAssertEqual(location.valueForKey("street") as? String, "Broadway")
    XCTAssertEqual(location.valueForKey("zipCode") as? NSNumber, NSNumber(int: 10012))

    let profilePicturesCount = Helper.countForEntity("Image", predicate: NSPredicate(format: "user = %@", user), inContext:dataStack.mainContext)
    XCTAssertEqual(profilePicturesCount, 3);

    try! dataStack.drop()
  }

  // If all operations where enabled in the first sync, 2 users would be inserted, in the second sync 1 user would be updated
  // and one user deleted. In this test we try only inserting user, no update, no insert, so the second sync should leave us with
  // 2 users with no changes and 1 inserted user.
  func testSyncingWithOnlyInsertOperationType() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("operation-types-users-a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, operations: [.All], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext: dataStack.mainContext), 2)

    let objectsB = Helper.objectsFromJSON("operation-types-users-b.json") as! [[String : AnyObject]]
    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, operations: [.Insert], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 3)

    let result = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 0)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext: dataStack.mainContext).first!
    XCTAssertEqual(result.valueForKey("email") as? String, "<EMAIL>")

    try! dataStack.drop()
  }

  // If all operations where enabled in the first sync, 2 users would be inserted, in the second sync 1 user would be updated
  // and one user deleted. In this test we try only inserting user, no update, no insert, so the second sync should leave us with
  // 2 users with no changes and 1 inserted user. After this is done, we'll try inserting again, this shouldn't make any changes.
  func testSyncingWithMultipleInsertOperationTypes() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("operation-types-users-a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, operations: [.All], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext: dataStack.mainContext), 2)

    let objectsB = Helper.objectsFromJSON("operation-types-users-b.json") as! [[String : AnyObject]]
    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, operations: [.Insert], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 3)

    let result = Helper.fetchEntity("User", predicate: NSPredicate(format: "remoteID = %@", NSNumber(int: 0)), sortDescriptors: [NSSortDescriptor(key: "remoteID", ascending: true)], inContext: dataStack.mainContext).first!
    XCTAssertEqual(result.valueForKey("email") as? String, "<EMAIL>")

    Sync.changes(objectsB, inEntityNamed: "User", dataStack: dataStack, operations: [.Insert], completion: nil)
    XCTAssertEqual(Helper.countForEntity("User", inContext:dataStack.mainContext), 3)

    try! dataStack.drop()
  }

  // If all operations where enabled in the first sync, 2 users would be inserted, in the second sync 1 user would be updated
  // and one user deleted. In this test we try only updating users, no insert, no delete, so the second sync should leave us with
  // one updated user and one inserted user, the third user will be discarded.
  func testSyncingWithOnlyUpdateOperationType() {
    let dataStack = Helper.dataStackWithModelName("Contacts")

    let objectsA = Helper.objectsFromJSON("operation-types-users-a.json") as! [[String : AnyObject]]
    Sync.changes(objectsA, inEntityNamed: "User", dataStack: dataStack, operations: [.All], completion: nil)
{'fraction_non_alphanumeric': 0.12736231522484875, 'fraction_numerical': 0.006112393189047589, 'mean_word_length': 5.43160850381067, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4699459', 'n_tokens_mistral': 5006, 'n_tokens_neox': 4784, 'n_words': 1187}