text: (function (testFns) {
      var breeze = testFns.breeze;
      var core = breeze.core;
      
  
      var Enum = core.Enum;
  
      var MetadataStore = breeze.MetadataStore;
      var EntityManager = breeze.EntityManager;
      var AutoGeneratedKeyType = breeze.AutoGeneratedKeyType;
      var SaveOptions = breeze.SaveOptions;
      var EntityQuery = breeze.EntityQuery;
      var EntityKey = breeze.EntityKey;
      var EntityState = breeze.EntityState;
      var FilterQueryOp = breeze.FilterQueryOp;
  
      var newEm = testFns.newEm;
  
      var wellKnownData = testFns.wellKnownData;
  
      module("save", {
          setup: function () {
              testFns.setup({
                  metadataFn: function() {
                      var regionType = testFns.metadataStore.getEntityType("Region");
                      regionType.setProperties({ autoGeneratedKeyType: AutoGeneratedKeyType.KeyGenerator });
                      var territoryType = testFns.metadataStore.getEntityType("Territory");
                      territoryType.setProperties({ autoGeneratedKeyType: AutoGeneratedKeyType.KeyGenerator });
                  }
              });
          },
          teardown: function () { }
      });
  
      test("delete without query", function () {
          var em = newEm();
          var em2 = newEm();
          var similarEmp;
          var similarAspect;
          var emp = em.createEntity("Employee");
          emp.setProperty("firstName", "Test fn");
          emp.setProperty("lastName", "Test ln");
          emp.setProperty("fullName", "foo");
          em.addEntity(emp);
          stop();
          em.saveChanges().then(function (sr) {
              var savedEnts = sr.entities;
              ok(savedEnts.length === 1, "should have saved 1 entity");
              ok(emp === savedEnts[0], "should be same emp");
              var empKeyValue = emp.getProperty(testFns.employeeKeyName);
              var empKey = emp.entityAspect.getKey();
              similarEmp = em2.createEntity("Employee");
              similarEmp.setProperty(testFns.employeeKeyName, empKeyValue);
              similarAspect = similarEmp.entityAspect;
              similarAspect.setUnchanged();
              similarAspect.setDeleted();
              ok(similarAspect.entityState.isDeleted(), "should be deleted");
              return em2.saveChanges();
          }).then(function (sr) {
              var savedEnts = sr.entities;
              ok(savedEnts.length === 1, "should have saved 1 entity");
              ok(savedEnts[0] === similarEmp, "should be the same similarEmp");
              ok(similarAspect.entityState.isDetached(), "should be detached");
          }).fail(testFns.handleFail).fin(start);
  
  
      });
  
      test("pk update", function () {
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "TODO for Mongo - needs to be written specifically for Mongo - should succeed in Mongo");
              return;
          }
  
          var em = newEm();
  
          var q = new EntityQuery("Territories").orderBy("territoryID desc").take(1);
          stop();
          var order;
          var freight;
          q.using(em).execute().then(function (data) {
              ok(data.results.length === 1, "should be one result");
              var terr = data.results[0];
              var id = terr.getProperty("territoryID");
              terr.setProperty("territoryID", id + 1);
  
              return em.saveChanges();
          }).then(function (sr) {
              ok(false, "should not get here");
              
  
          }).fail(function (e) {
              var isOk = e.message.indexOf("part of the entity's key") > 0;
              ok(isOk, "error message should mention the entity key");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("exceptions thrown on server", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
  
          var em = newEm();
  
          var q = new EntityQuery("Orders").take(1);
          stop();
          var order;
          var freight;
          q.using(em).execute().then(function (data) {
              order = data.results[0];
              freight = order.getProperty("freight") + .5;
              order.setProperty("freight", freight);
  
              var so = new SaveOptions({ resourceName: "SaveAndThrow", tag: "SaveAndThrow" });
  
              return em.saveChanges(null, so);
          }).then(function (sr) {
              ok("should not get here");
  
          }).fail(function (e) {
              ok(e);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete entity with Int32 property set to null", function () {
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - employeeId is not an integer on Mongo");
              return;
          }
          var em = newEm();
          var c1 = em.createEntity("Order", { employeeID: 1 });
          stop();
          //save entity with non-null value on Int32 field
          em.saveChanges().then(function (sr) {
              var order = sr.entities[0];
              var empID0 = order.getProperty(testFns.employeeKeyName);
              ok(empID0 != null, "empID0 should not be null");
  
              //set the Int32 field to null
              order.setProperty(testFns.employeeKeyName, null);
              //resave entity
              return em.saveChanges();
          }).then(function (sr) {
              var order = sr.entities[0];
              var empID1 = order.getProperty(testFns.employeeKeyName);
              ok(empID1 === null, "value should be null");
  
              //mark entity as deleted
              order.entityAspect.setDeleted();
              //resave (i.e. delete) entity - the error should occur past this point
              return em.saveChanges();
          }).then(function (sr) {
              ok(true);
          }).fail(testFns.handleFail).fin(start);
      });
  
      // Breeze does not YET support an option to requery after save. 
      // When it does let's resurrect this test.
      //test("check if save requeried saved entities", function () {
      //    var em = newEm();
      //    var c1 = em.createEntity("TimeGroup", { comment: "trigger" });
      //    stop();
      //    em.saveChanges().then(function (sr) {
      //        var comment = sr.entities[0].comment;
      //        ok(comment === "happy", "should have requeried the value updated by trigger");
      //    }).fail(testFns.handleFail).fin(start);
      //});
  
      test("check unmapped property on server", function () {
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - Have not yet added SaveCheck code for Mongo");
              return;
          }
          
          var em = newEm(MetadataStore.importMetadata(testFns.metadataStore.exportMetadata()));
          var customerType = em.metadataStore.getEntityType("Customer");
  
          var Customer = function () {
              this.myUnmappedProperty = "anything22";
          };
          em.metadataStore.registerEntityTypeCtor("Customer", Customer);
  
  
          var cust = customerType.createEntity();
          cust.setProperty("companyName", "Test_compName");
          em.addEntity(cust);
  
          var entitiesToSave = new Array(cust);
          var saveOptions = new SaveOptions({ resourceName: "SaveCheckUnmappedProperty" });
          stop();
  
          em.saveChanges(entitiesToSave, saveOptions).then(function (sr) {
              ok(true);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("test unmapped property serialization on server", function () {
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - Have not yet added SaveCheck code for Mongo");
              return;
          }
  
          var em = newEm(MetadataStore.importMetadata(testFns.metadataStore.exportMetadata()));
          var customerType = em.metadataStore.getEntityType("Customer");
          customerType.setProperties({
              serializerFn: function (dp, value) {
                  if (typeof (value) === "string") return value.toUpperCase();
                  if (dp.isUnmapped) {
                      if (dp.name == "anotherOne") {
                          value.extra = 666;
                      }
                  }
                  return value;
              }
          })
          var Customer = function () {
              this.myUnmappedProperty = "anything22";
              var x = {
                  x: "22",
                  y: "test",
                  z: ["a1", 3, true, null, undefined, { foo: 4 }, function (x, y, z) { return 666 }],
                  testFn: function(a, b) { return a+b;}
              }
              x.recursive = { ok: true, notOk: x }; // notOk should not get serialized.
              this.anotherOne = x;
          };
          em.metadataStore.registerEntityTypeCtor("Customer", Customer);
  
  
          var cust = customerType.createEntity();
          cust.setProperty("companyName", "Test_compName");
          em.addEntity(cust);
  
          var entitiesToSave = new Array(cust);
          var saveOptions = new SaveOptions({ resourceName: "SaveCheckUnmappedPropertySerialized" });
          stop();
  
          em.saveChanges(entitiesToSave, saveOptions).then(function (sr) {
              ok(true);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("test unmapped property suppression", function () {
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - Have not yet added SaveCheck code for Mongo");
              return;
          }
  
          var em = newEm(MetadataStore.importMetadata(testFns.metadataStore.exportMetadata()));
          var customerType = em.metadataStore.getEntityType("Customer");
          customerType.setProperties({
              serializerFn: function (dp, value) {
                  if (dp.isUnmapped) return undefined;
                  return value;
              }
          })
          var Customer = function () {
              this.myUnmappedProperty = "anything22";
              var x = {
                  x: "22",
                  y: "test",
                  z: ["a1", 3, true, null, undefined, { foo: 4 }]
              }
              x.recursive = { ok: true, notOk: x }; // notOk should not get serialized.
              this.anotherOne = x;
          };
          em.metadataStore.registerEntityTypeCtor("Customer", Customer);
  
  
          var cust = customerType.createEntity();
          cust.setProperty("companyName", "Test_compName");
          em.addEntity(cust);
  
          var entitiesToSave = new Array(cust);
          var saveOptions = new SaveOptions({ resourceName: "SaveCheckUnmappedPropertySuppressed" });
          stop();
  
          em.saveChanges(entitiesToSave, saveOptions).then(function (sr) {
              ok(true);
          }).fail(testFns.handleFail).fin(start);
      });
  
  
      test("check initializer is hit for entities added/saved on server", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - Have not yet added SaveCheck code for Mongo");
              return;
          }
  
          var em = newEm(MetadataStore.importMetadata(testFns.metadataStore.exportMetadata()));
          var ordInitializer = function (ord) {
              ord.setProperty("shipCountry", "Brazil");
          };
  
          em.metadataStore.registerEntityTypeCtor("Order", null, ordInitializer);
  
          var emp = em.createEntity("Employee");
          emp.setProperty("firstName", "Test fn");
          emp.setProperty("lastName", "Test ln");
          emp.setProperty("fullName", "foo");
          em.addEntity(emp);
          var so = new SaveOptions({ resourceName: "SaveCheckInitializer" });
          stop();
          em.saveChanges(null, so).then(function (sr) {
              var ents = sr.entities;
              ok(ents.length === 2, "since an Order was created/saved in the interceptor, length should be 2");
              ok(ents[1].getProperty("shipCountry") === "Brazil", "initializer was not 'hit'");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("entities modified on server being saved as new entities", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - test not yet implemented - requires server side async call");
              return;
          }
          var em = newEm();
  
          var q = EntityQuery.from("Categories").where("categoryName", "startsWith", "Beverage");
          stop();
          em.executeQuery(q).then(function (data) {
              var category = data.results[0];
              testFns.morphStringProp(category, "categoryName");
  
              var entitiesToSave = new Array(category);
              var saveOptions = new SaveOptions({ tag: "increaseProductPrice" });
              stop();
              em.saveChanges(entitiesToSave, saveOptions).then(function (sr) {
                  ok(sr.entities.length === 13, "13 records should have been saved - 1 category + 12 products");
                  // TODO: we should now requery and check that the 12 products actually have increased in price.
              }).fail(testFns.handleFail).fin(start);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save data with with additional entity added on server", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          var em = newEm();
          
          var supplier = em.createEntity("Supplier", { companyName: "CompName" });
          var entitiesToSave = new Array(supplier);
          var saveOptions = new SaveOptions({ tag: "addProdOnServer" });
          stop();
          em.saveChanges(entitiesToSave, saveOptions).then(function (sr) {
              var addedProducts = em.getEntities(["Product"], EntityState.Added);
  
              ok(addedProducts.length === 0, "There should be no Added Products");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("can save a Northwind Order & InternationalOrder", function () {
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for Mongo - primary keys cannot be shared between collections");
              return;
          }
          // Create and initialize entity to save
          var em = newEm();
  
          var order = em.createEntity('Order', {
              customerID: wellKnownData.alfredsID,
              employeeID: wellKnownData.nancyID,
              shipName: "Test " + new Date().toISOString()
          });
  
          var internationalOrder = em.createEntity('InternationalOrder', {
              // I thought Jay fixed this?
              order: order, // sets OrderID and pulls it into the order's manager
              // orderID: order.getProperty("orderID"),
              customsDescription: "rare, exotic birds"
          });
          stop();
          em.saveChanges().then(function(data) {
  
              var orderId = order.getProperty("orderID");
              var internationalOrderID = internationalOrder.getProperty("orderID");
  
              equal(internationalOrderID, orderId,
                  "the new internationalOrder should have the same OrderID as its new parent Order, " + orderId);
              ok(orderId > 0, "the OrderID is positive, indicating it is a permanent order");
  
          }).fail(testFns.handleFail).fin(start);
  
      });
  
      test("save data with alt resource and server side add", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
  
          var em = newEm();
  
          var q = new EntityQuery("Orders").take(1);
          stop();
          var order;
          var freight;
          q.using(em).execute().then(function (data) {
              order = data.results[0];
              freight = order.getProperty("freight") + .5;
              order.setProperty("freight", freight);
  
              var so = new SaveOptions({ resourceName: "SaveWithComment", tag: "SaveWithComment - order" });
  
              return em.saveChanges(null, so);
          }).then(function (sr) {
              ok(sr.entities.length == 2, "should have saved two entities");
              sr.entities.forEach(function(e) {
                  ok(e.entityAspect, "entities should have an entityAspect after save");
              });
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em).execute();
          }).then(function (data2) {
              var order2 = data2.results[0];
              var freight2 = order2.getProperty("freight");
              ok(freight2 == freight, "freight2=" + freight2 + " vs " + freight);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save with date as part of key", function () {
          var em = newEm();
          var dt = new Date();
          dt.setUTCMilliseconds(100);
          var c1 = em.createEntity("Comment", { createdOn: dt, seqNum: 1, comment1: "now is the time for" });
          var c2 = em.createEntity("Comment", { createdOn: dt, seqNum: 2, comment1: "and again" });
          stop();
          em.saveChanges().then(function (sr) {
              var comments = sr.entities;
              ok(comments.length === 2, "should have saved 2 comments");
              var em2 = newEm();
              var q = EntityQuery.from("Comments").where("createdOn", "==", dt);
              return em2.executeQuery(q);
          }).then(function (data) {
              var comments2 = data.results;
              ok(comments2.length === 2, "should have returned 2 comments");
          }).fail(testFns.handleFail).fin(start);
      });
  
  
  
      test("save computed update", function () {
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support computed properties");
              return;
          };
          var em = newEm();
          var q = EntityQuery.from("Employees").take(3);
          stop();
          em.executeQuery(q).then(function(data) {
              var emps = data.results;
              testFns.morphStringProp(emps[0], "lastName");
              return em.saveChanges();
          }).then(function(sr) {
              var ents = sr.entities;
              ok(ents.length === 1);
              var emp = ents[0];
              var fullName = emp.getProperty("lastName") + ", " + emp.getProperty("firstName");
              if (testFns.DEBUG_ODATA) {
                  ok(fullName !== emp.getProperty("fullName"), "fullNames will not match with ODATA because no records are returned after save");
              } else {
                  ok(fullName === emp.getProperty("fullName"), "fullNames do not match");
              }
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save computed update - mod computed", function () {
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support computed properties");
              return;
          };
          var em = newEm();
          var q = EntityQuery.from("Employees").take(3);
          stop();
          em.executeQuery(q).then(function (data) {
              var emps = data.results;
              testFns.morphStringProp(emps[0], "lastName");
              emps[0].setProperty("fullName", "xxx");
              return em.saveChanges();
          }).then(function (sr) {
              var ents = sr.entities;
              ok(ents.length === 1);
              var emp = ents[0];
              var fullName = emp.getProperty("lastName") + ", " + emp.getProperty("firstName");
              if (testFns.DEBUG_ODATA) {
                  ok(fullName !== emp.getProperty("fullName"), "fullNames will not match with ODATA because no records are returned after save");
              } else {
                  ok(fullName === emp.getProperty("fullName"), "fullNames do not match");
              }
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save computed insert" , function () {
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support computed properties");
              return;
          };
          var em = newEm();
          var emp = em.createEntity("Employee");
          emp.setProperty("firstName", "Test fn");
          emp.setProperty("lastName", "Test ln");
          emp.setProperty("fullName", "foo");
          em.addEntity(emp);
          stop();
          em.saveChanges().then(function (sr) {
              var ents = sr.entities;
              ok(ents.length === 1);
              ok(ents[0] === emp, "should be the same emp");
              var fullName = emp.getProperty("lastName") + ", " + emp.getProperty("firstName");
              ok(fullName === emp.getProperty("fullName"), "fullNames do not match");
              
              
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save update with unmapped changes", function() {
          var em1 = newEm(testFns.newMs());
          var Customer = testFns.models.CustomerWithMiscData();
          em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
          stop();
          var q = new EntityQuery("Customers").take(1);
          em1.executeQuery(q).then(function(data) {
              var custType = em1.metadataStore.getEntityType("Customer");
              var cust = data.results[0];
              var oldContactName = cust.getProperty("contactName");
              var oldMiscData = cust.getProperty("miscData");
              testFns.morphStringProp(cust, "contactName");
              testFns.morphStringProp(cust, "miscData");
              return em1.saveChanges();
          }).then(function (sr) {
              
              var e = sr.entities;
              ok(e.length === 1, "1 record should have been saved");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save update with ES5 props and unmapped changes", function () {
          var em1 = newEm(testFns.newMs());
          var Customer = testFns.models.CustomerWithES5Props();
          em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
          stop();
          var q = new EntityQuery("Customers").take(1);
          em1.executeQuery(q).then(function (data) {
              var custType = em1.metadataStore.getEntityType("Customer");
              var cust = data.results[0];
              var oldContactName = cust.getProperty("contactName");
              var oldMiscData = cust.getProperty("miscData");
              testFns.morphStringProp(cust, "contactName");
              testFns.morphStringProp(cust, "miscData");
              return em1.saveChanges();
          }).then(function (sr) {
  
              var e = sr.entities;
              ok(e.length === 1, "1 record should have been saved");
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save delete with unmapped changes", function () {
          var em1 = newEm(testFns.newMs());
          var Customer = testFns.models.CustomerWithMiscData();
          em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
          stop();
          var zzz;
          em1.fetchMetadata().then(function() {
              zzz = createParentAndChildren(em1);
              return em1.saveChanges();
          }).then(function (saveResult) {
              var cust = zzz.cust1;
              var oldContactName = cust.getProperty("contactName");
              var oldMiscData = cust.getProperty("miscData");
              testFns.morphStringProp(cust, "contactName");
              testFns.morphStringProp(cust, "miscData");
              zzz.cust1.entityAspect.setDeleted();
              
              return em1.saveChanges();
          }).then(function (sr) {
              var r = sr.entities;
              ok(zzz.cust1.entityAspect.entityState.isDetached());
              ok(r.length === 3, "3 child records should have been modified ( stranded)");
          }).fail(testFns.handleFail).fin(start);
          
  
      });
      
      test("save data with server reject", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception");
              return;
          };
  
  
          var em = newEm();
  
          var user = em.createEntity("Region");
          
          user.setProperty("regionDescription", "error here");
          em.addEntity(user);
          var hasChanges = em.hasChanges();
          ok(hasChanges, "should have some changes");
          stop();
          em.saveChanges().then(function (sr) {
              ok(sr.entities.length == 0, "should now have saved anything");
              hasChanges = em.hasChanges();
              ok(hasChanges, "should still have some changes because user should have been rejected on the server");
              // var q2 = EntityQuery.fromEntities(order);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save data with alt resource and server update", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          }
  
          var em = newEm();
  
          var q = new EntityQuery("Orders").take(1);
          stop();
          var order;
          var freight;
          q.using(em).execute().then(function(data) {
              order = data.results[0];
              freight = order.getProperty("freight") + .5;
              order.setProperty("freight", freight);
              
              var so = new SaveOptions({ resourceName: "SaveWithFreight2", tag: "freight update" });
              
              return em.saveChanges(null, so);
          }).then(function(sr) {
              ok(sr.entities.length == 1, "should have saved one entity");
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em).execute();
          }).then(function(data2) {
              var order2 = data2.results[0];
              var freight2 = order2.getProperty("freight");
              ok(freight2 == freight + 1, "freight2=" + freight2 + " vs " + (freight+1));
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save data with alt resource and server update - ForceUpdate", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
  
          var em = newEm();
  
          var q = new EntityQuery("Orders").where("shipCountry", "ne", null).take(1);
          stop();
          var order, freight, shipCountry;
          q.using(em).execute().then(function (data) {
              order = data.results[0];
              freight = order.getProperty("freight");
              shipCountry = testFns.morphString(order.getProperty("shipCountry"));
              order.setProperty("shipCountry", shipCountry);
              var so = new SaveOptions({ resourceName: "SaveWithFreight", tag: "freight update-force" });
              return em.saveChanges(null, so);
          }).then(function (sr) {
              ok(sr.entities.length == 1, "should have saved one entity");
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em).execute();
          }).then(function (data2) {
              var order2 = data2.results[0];
              var freight2 = order2.getProperty("freight");
              ok(freight2 == freight + 1, "freight2=" + freight2 + " vs " + (freight + 1));
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save data with server update - original values fixup", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception");
              return;
          };
  
          var em = newEm();
  
          var q = new EntityQuery("Orders").where("shipCountry", "ne", null).take(1);
          stop();
          var order, freight, shipCity;
          q.using(em).execute().then(function (data) {
              order = data.results[0];
              freight = order.getProperty("freight");
              shipCity = testFns.morphString(order.getProperty("shipCountry"));
              order.setProperty("shipCountry", shipCity);
              var so = new SaveOptions({ resourceName: "SaveWithFreight", tag: "freight update-ov" });
              return em.saveChanges(null, so);
          }).then(function (sr) {
              ok(sr.entities.length == 1, "should have saved one entity");
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em).execute();
          }).then(function (data2) {
              var order2 = data2.results[0];
              var freight2 = order2.getProperty("freight");
              ok(freight2 == freight + 1, "freight2=" + freight2 + " vs " + (freight + 1));
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save with saveOptions exit", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          var so = new SaveOptions({ resourceName: "SaveWithExit", tag: "exit" });
          stop();
          em.saveChanges(null, so).then(function(sr) {
              ok(sr.entities.length == 0);
          }).fail(testFns.handleFail).fin(start);
              
      });
  
      test("save/adds with EntityErrorsException", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support server side validation");
              return;
          };
  
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          var so = new SaveOptions({ resourceName: "SaveWithEntityErrorsException", tag: "entityErrorsException" });
          stop();
          em.saveChanges(null, so).then(function (sr) {
              ok(false, "should not get here");
  
          }).fail(function (e) {
              ok(e.entityErrors, "should have server errors");
              ok(e.entityErrors.length === 2, "2 order entities should have failed");
              ok(zzz.order1.entityAspect.getValidationErrors().length === 1);
              var order2Errs = zzz.order2.entityAspect.getValidationErrors();
              ok(order2Errs.length === 1, "should be 1 error for order2");
              ok(order2Errs[0].propertyName === "orderID", "errant property should have been 'orderID'");
              // now save it properly
              return em.saveChanges();
          }).then(function(sr) {
              ok(sr.entities.length === 4, "should have saved ok");
          }).fail(testFns.handleFail).fin(start);
  
      });
  
      test("save/mods with EntityErrorsException", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support server side validation");
              return;
          };
  
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          
          stop();
          em.saveChanges().then(function (sr) {
              zzz.cust1.setProperty("contactName", "foo");
              zzz.cust2.setProperty("contactName", "foo");
              zzz.order1.setProperty("freight", 888.11);
              zzz.order2.setProperty("freight", 888.11);
              ok(zzz.cust1.entityAspect.entityState.isModified(), "cust1 should be modified");
              ok(zzz.order1.entityAspect.entityState.isModified(), "order1 should be modified");
              var so = new SaveOptions({ resourceName: "SaveWithEntityErrorsException", tag: "entityErrorsException" });
              return em.saveChanges(null, so);
          }).then(function(sr2) {
              ok(false, "should not get here");
          }).fail(function (e) {
              ok(e.entityErrors, "should have server errors");
              ok(e.entityErrors.length === 2, "2 order entities should have failed");
              ok(zzz.order1.entityAspect.getValidationErrors().length === 1);
              var order2Errs = zzz.order2.entityAspect.getValidationErrors();
              ok(order2Errs.length === 1, "should be 1 error for order2");
              ok(order2Errs[0].propertyName === "orderID", "errant property should have been 'orderID'");
              // now save it properly
              return em.saveChanges();
          }).then(function (sr) {
              ok(sr.entities.length === 4, "should have saved ok");
          }).fail(testFns.handleFail).fin(start);
  
      });
  
      test("save with client side validation error", function () {
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          cust1.setProperty("companyName", null);
          stop();
          em.saveChanges().then(function (sr) {
              ok(false, "should not get here");
          }).fail(function (e) {
              ok(e.entityErrors, "should be a  entityError");
              ok(e.entityErrors.length === 1, "should be only one error");
              ok(!e.entityErrors[0].isServerError, "should NOT be a server error");
              var errors = cust1.entityAspect.getValidationErrors();
              ok(errors[0].errorMessage === errors[0].errorMessage, "error message should appear on the cust");
  
          }).fin(start);
      });
  
      test("save with server side entity level validation error", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support server side validation");
              return;
          };
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          cust1.setProperty("companyName", "error");
          stop();
          em.saveChanges().then(function(sr) {
              ok(false, "should not get here");
          }).fail(function (e) {
              ok(e.entityErrors, "should be a server error");
              ok(e.entityErrors.length === 1, "should be only one server error");
              var errors = cust1.entityAspect.getValidationErrors();
              ok(errors[0].errorMessage === e.entityErrors[0].errorMessage, "error message should appear on the cust");
          }).fin(start);
      });
  
      test("save with server side entity level validation error + repeat", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "Skipped test - Mongo does not YET support server side validation");
              return;
          };
  
          var em = newEm();
          var zzz = createParentAndChildren(em);
          var cust1 = zzz.cust1;
          cust1.setProperty("companyName", "error");
          stop();
          em.saveChanges().then(function (sr) {
              ok(false, "should not get here");
          }).fail(function (e) {
              ok(e.entityErrors, "should be a server error");
              ok(e.entityErrors.length === 1, "should be only one server error");
              var errors = cust1.entityAspect.getValidationErrors();
              ok(errors.length === 1, "should only be 1 error");
              ok(errors[0].errorMessage === e.entityErrors[0].errorMessage, "error message should appear on the cust");
              return em.saveChanges();
          }).fail(function(e2) {
              ok(e2.entityErrors, "should be a server error");
              ok(e2.entityErrors.length === 1, "should be only one server error");
             var errors = cust1.entityAspect.getValidationErrors();
             ok(errors.length === 1, "should only be 1 error");
             ok(errors[0].errorMessage === e2.entityErrors[0].errorMessage, "error message should appear on the cust");
          }).fin(start);
      });
  
      
      test("delete unsaved entity", function () {
          var realEm = newEm();
          ok(realEm.hasChanges() === false, "The entity manager must not have changes");
          var query = EntityQuery.from("Customers")
              .where(testFns.customerKeyName, "==", "729de505-ea6d-4cdf-89f6-0360ad37bde7");
          stop();
          var cust;
          realEm.executeQuery(query).then(function(data) {
              cust = data.results[0];
              return cust.entityAspect.loadNavigationProperty("orders");
          }).then(function(data2) {
              var newOrder = realEm.createEntity("Order", {}, breeze.EntityState.Detached);
              var orders = cust.getProperty("orders");
              orders.push(newOrder);
              ok(newOrder.entityAspect.entityState.isAdded() === true, "The entity is Added");
              newOrder.entityAspect.setDeleted();
              ok(realEm.hasChanges() === false, "The entity manager must not  have changes");
              return realEm.saveChanges();
          }).then(function (sr) {
              ok(realEm.hasChanges() === false, "The entity manager must not have changes");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("bigsave", function() {
          var em = newEm();
          var recentArgs;
          em.hasChangesChanged.subscribe(function(args) {
              recentArgs = args;
          });
          for (var i = 0; i < 20; i++) {
              var zzz = createParentAndChildren(em);    
          }
          ok(recentArgs.hasChanges === true);
          
          stop();
          var startMs = Date.now();
          var endMs;
          em.saveChanges().then(function(sr) {
              var r = sr.entities;
              ok(r.length == 80, "Length: "+ r.length);
              endMs = Date.now();
              var elapsed = (endMs - startMs) / 1000;
              ok(elapsed, "Elapsed time: " + elapsed);
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("bigsave many children", function () {
          var em = newEm();
          var recentArgs;
          em.hasChangesChanged.subscribe(function (args) {
              recentArgs = args;
          });
          for (var i = 0; i < 5; i++) {
              var zzz = createParentAndManyChildren(em);
          }
          ok(recentArgs.hasChanges === true);
  
          stop();
          var startMs = Date.now();
          var endMs;
          em.saveChanges().then(function (sr) {
              var r = sr.entities;
              ok(r.length > 100, "Length: " + r.length);
              endMs = Date.now();
              var elapsed = (endMs - startMs) / 1000;
              ok(elapsed, "Elapsed time: " + elapsed);
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("noop", function() {
          var em = newEm();
          var q = new EntityQuery("Customers");
          stop();
          q.using(em).execute().then(function(data) {
              return em.saveChanges();
          }).then(function(sr) {
              ok(Array.isArray(sr.entities));
              ok(sr.entities.length == 0);
              ok(!em.hasChanges());
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save data with millseconds - UTC time - IE bug", function() {
          var em = newEm();
          var dt = new Date(Date.parse("2012-12-17T13:35:15.690Z"));
          var offset = dt.getTimezoneOffset() * 60000;
          var dt1 = new Date(dt.getTime() - offset);
          var dt2 = new Date(dt.getTime() + offset);
          var ms = dt.getUTCMilliseconds();
          ok(ms === 690);
          var q = new EntityQuery("Orders").where("shippedDate", "!=", dt).take(1);
          stop();
          var order;
          q.using(em).execute().then(function(data) {
              order = data.results[0];
              order.setProperty("shippedDate", dt);
              return em.saveChanges();
          }).then(function(sr) {
              ok(sr.entities.length == 1, "should have saved one entity");
              var sameOrder = sr.entities[0];
              ok(order === sameOrder, "should be the sameOrder");
              var sameDt = sameOrder.getProperty("shippedDate");
              ok(dt.getTime() === sameDt.getTime(), "should be the same date");
              var em2 = newEm();
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em2).execute();
          }).then(function (data2) {
              var order2 = data2.results[0];
              var sameDt2 = order2.getProperty("shippedDate");
              ok(dt.getTime() === sameDt2.getTime(), "should be the same date: " +dt.toString() + " != " + sameDt2.toString());
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("save data with millseconds - local time", function () {
          var em = newEm();
          // Date.parse("2012-12-17T13:35:15.690");
          var dt = new Date(2012, 11, 17, 13, 35, 15, 690); // local time
          var ms = dt.getMilliseconds();
          ok(ms === 690);
          var q = new EntityQuery("Orders").take(1);
          stop();
          var order;
          q.using(em).execute().then(function (data) {
              order = data.results[0];
              order.setProperty("shippedDate", dt);
              return em.saveChanges();
          }).then(function (sr) {
              ok(sr.entities.length == 1, "should have saved one entity");
              var sameOrder = sr.entities[0];
              ok(order === sameOrder, "should be the sameOrder");
              var sameDt = sameOrder.getProperty("shippedDate");
              ok(dt.getTime() === sameDt.getTime(), "should be the same date");
              var em2 = newEm();
              var q2 = EntityQuery.fromEntities(order);
              return q2.using(em2).execute();
          }).then(function (data2) {
              var order2 = data2.results[0];
              var sameDt2 = order2.getProperty("shippedDate");
              ok(dt.getTime() === sameDt2.getTime(), "should be the same date: " + dt.toString() + " != " + sameDt2.toString());
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save custom data annotation validation", function () {
  
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server interception or alt resources");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "NA for Mongo - server side 'test' logic not yet implemented");
              return;
          }
          // This test will fail currently with the DATABASEFIRST_OLD define. 
          // This is because ObjectContext.SaveChanges() does not automatically validate 
          // entities. It must be done manually.
          var em = newEm();
          var q = new EntityQuery("Customers").skip(20).take(1).orderBy("contactName");
          stop();
          var cust1;
          q.using(em).execute().then(function(data) {
              ok(data.results.length === 1);
              cust1 = data.results[0];
              var region = cust1.getProperty("contactName");
              var newRegion = region == "Error" ? "Error again" : "Error";
              cust1.setProperty("contactName", newRegion);
              return em.saveChanges();
          }).then(function(sr) {
              ok(false, "shouldn't get here - except with DATABASEFIRST_OLD");
          }).fail(function (error) {
              ok(error.entityErrors, "should be some server errors");
              ok(error.entityErrors.length === 1, "should be 1 server error");
              ok(error.entityErrors[0].errorMessage.indexOf("the word 'Error'") > 0, "incorrect error message");
              var custErrors = cust1.entityAspect.getValidationErrors();
              ok(error.entityErrors[0].errorMessage === custErrors[0].errorMessage);
              // ok(error.message.indexOf("the word 'Error'") > 0, "incorrect error message");
          }).fin(start);
  
  
      });
      
      test("save date", function () {
          var em = newEm();
          var q = new EntityQuery("Orders").where("orderDate", '!=', null).take(10);
          stop();
          var order, orderDate, newOrderDate;
          q.using(em).execute().then(function (data) {
              var r = data.results;
              ok(r.length > 0, "should be some results");
              order = r[0];
              orderDate = order.getProperty("orderDate");
              ok(core.isDate(orderDate),"is not a date");
              var day = orderDate.getDate();
              day = day < 31 ? day + 1 : 1;
              newOrderDate = new Date(orderDate.getTime());
              newOrderDate.setDate(day);
              ok(core.isDate(newOrderDate), "is not a date");
              order.setProperty("orderDate", newOrderDate);
              return em.saveChanges();
          }).then(function (sr) {
              ok(Array.isArray(sr.entities));
              ok(sr.entities.length == 1);
              ok(!em.hasChanges());
              ok(sr.entities[0] === order, "should be same order");
              var newOrderDate2 = order.getProperty("orderDate");
              ok(core.isDate(newOrderDate2), "is not a date");
              ok(newOrderDate.getTime() == newOrderDate2.getTime());
              ok(orderDate != newOrderDate2);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("unmapped save", function() {
  
          // use a different metadata store for this em - so we don't polute other tests
          var em1 = newEm();
          var Customer = testFns.models.CustomerWithMiscData();
          em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
          stop();
          var q = new EntityQuery("Customers")
              .where("companyName", "startsWith", "C");
          q.using(em1).execute().then(function(data) {
              var customers = data.results;
              customers.every(function(c) {
                  ok(c.getProperty("miscData") == "asdf", "miscData should == 'asdf'");
  
              });
              var cust = customers[0];
              cust.setProperty("miscData", "xxx");
              ok(cust.entityAspect.entityState == EntityState.Unchanged);
              ok(!em1.hasChanges(), "should not have changes");
              return em1.saveChanges();
          }).then(function(sr) {
              var saved = sr.entities;
              ok(saved.length === 0);
              ok(!em1.hasChanges());
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("unmapped save with ES5 props", function () {
  
          // use a different metadata store for this em - so we don't polute other tests
          var em1 = newEm(testFns.newMs());
          var Customer = testFns.models.CustomerWithES5Props();
  
          em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
          stop();
          var q = new EntityQuery("Customers")
              .where("companyName", "startsWith", "C");
          q.using(em1).execute().then(function (data) {
              var customers = data.results;
              customers.every(function (c) {
                  ok(c.getProperty("miscData") == "asdf", "miscData should == 'asdf'");
  
              });
              var cust = customers[0];
              cust.setProperty("miscData", "xxx");
              ok(cust.entityAspect.entityState == EntityState.Unchanged);
              ok(!em1.hasChanges(), "should not have changes");
              return em1.saveChanges();
          }).then(function (sr) {
              var saved = sr.entities;
              ok(saved.length === 0);
              ok(!em1.hasChanges());
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("add parent and children", function () {
          var em = newEm();
          var recentArgs;
          em.hasChangesChanged.subscribe(function(args) {
              recentArgs = args;
          });
          var zzz = createParentAndChildren(em);
          ok(recentArgs.hasChanges === true);
          stop();
          em.saveChanges(null, null).then(function(saveResult) {
              ok(recentArgs.hasChanges === false);
              ok(zzz.cust1.entityAspect.entityState.isUnchanged());
              ok(zzz.cust2.entityAspect.entityState.isUnchanged());
              ok(zzz.order1.entityAspect.entityState.isUnchanged());
              ok(zzz.order2.entityAspect.entityState.isUnchanged());
              ok(zzz.cust1.getProperty(testFns.customerKeyName) != zzz.keyValues[0], "cust1.customerID should not match original values");
              ok(zzz.cust2.getProperty(testFns.customerKeyName) != zzz.keyValues[1], "cust2.customerID should not match original values");
              ok(zzz.order1.getProperty(testFns.orderKeyName) != zzz.keyValues[2]);
              ok(zzz.order2.getProperty(testFns.orderKeyName) != zzz.keyValues[3]);
              ok(zzz.order1.getProperty("customer") === zzz.cust1);
              ok(zzz.order2.getProperty("customer") === zzz.cust1);
              ok(zzz.cust1.getProperty("orders").length === 2);
              ok(zzz.cust2.getProperty("orders").length === 0);
              ok(!em.hasChanges());
          }).fail(function(err) {
              ok(false, "should not get here - " + err);
          }).fin(start);
      });
  
      test("allow concurrent saves with concurrency column",  function() {
          var em = newEm();
          em.saveOptions = new SaveOptions({ allowConcurrentSaves: true });
          var q = new EntityQuery()
              .from("Customers")
              .take(2);
          stop();
          
          em.executeQuery(q).then(function(data) {
              // query cust
              var cust = data.results[0];
              testFns.morphStringProp(cust, "companyName");
  
              return Q.all([em.saveChanges(), em.saveChanges()]);
          }).then(function(x) {
              ok(false, "one save should have failed for concurrency reasons");
          }).fail(function(e) {
              var msg = e.message;
              if (msg.indexOf("Store update, insert") >= 0) {
                  ok(true, "got expected (EF) exception " + msg);
              } else if (msg.indexOf("Row was updated or deleted by another transaction") >= 0) {
                  ok(true, "got expected (Hibernate) exception " + msg);
              } else if (msg.indexOf("concurrency check") >= 0) {
                  ok(true, "got expected (Mongo) exception " + msg);
              } else {
                  ok(false, msg);
              }
          }).fin(start);
  
      });
      
      test("allow concurrent saves with NO concurrency column",  function() {
          var em = newEm();
          em.saveOptions = new SaveOptions({ allowConcurrentSaves: true });
          var q = new EntityQuery()
              .from("Products")
              .take(2);
  
          stop();
          var prod;
          
          em.executeQuery(q).then(function(data) {
              // query cust
              prod = data.results[0];
              var price = prod.getProperty("unitPrice");
              prod.setProperty("unitPrice", price + .01);
  
              return Q.all([em.saveChanges(), em.saveChanges()]);
          }).then(function(x) {
              ok(true, "expected both to succeed");
          }).fail(function(e) {
              ok(false, "both saves should have been ok but: " + e.message);
          }).fin(start);
              
      });
      
      test("disallow concurrent saves with NO concurrency column",  function() {
          var em = newEm();
          // Next line is not needed because it is the default
          // em.saveOptions = new SaveOptions({ allowConcurrentSaves: false });
          var q = new EntityQuery()
              .from("Products")
              .take(2);
  
          stop();
          var prod;
          
          em.executeQuery(q).then(function(data) {
              // query cust
              prod = data.results[0];
              var price = prod.getProperty("unitPrice");
              prod.setProperty("unitPrice", price + .01);
  
              return Q.all([em.saveChanges(), em.saveChanges()]);
          }).then(function(x) {
              ok(false, "expected only one to complete");
          }).fail(function (e) {
              if (e.message.indexOf("allowConcurrentSaves") >= 0) {
                  ok(true, "got expected error: " + e.message);
              } else {
                  ok(false, "unexpected error: " + e.message);
              }
          }).fin(start);
  
      });
  
      test("modify one", function () {
          var em = newEm();
          var query = new EntityQuery()
              .from("Customers")
              .where("companyName", "startsWith", "C")
              .take(2);
          stop();
          var newCompanyName, cust;
          em.executeQuery(query).then(function(data) {
              cust = data.results[0];
              var orders = cust.getProperty("orders");
              var companyName = cust.getProperty("companyName");
              newCompanyName = testFns.morphString(companyName);
              cust.setProperty("companyName", newCompanyName);
              return em.saveChanges();
          }).then(function(saveResult) {
              ok(!em.hasChanges());
              var entities = saveResult.entities;
              ok(entities.length === 1);
              ok(saveResult.keyMappings.length === 0);
              ok(entities[0] === cust);
              ok(cust.getProperty("companyName") === newCompanyName);
              ok(cust.entityAspect.entityState.isUnchanged());
              var q2 = EntityQuery.fromEntities(cust);
              return em.executeQuery(q2);
          }).then(function(data2) {
              var entities2 = data2.results;
              ok(entities2.length === 1);
              ok(entities2[0] === cust);
              ok(cust.getProperty("companyName") === newCompanyName);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("modify parent and children", function () {
          var em = newEm();
          var query = new EntityQuery()
              .from("Customers")
              .where("companyName", "startsWith", "C")
              .take(5);
          stop();
          var companyName, newCompanyName, orders, cust;
          var custs, cust;
          em.executeQuery(query).then(function(data) {
              custs = data.results;
              var promises = custs.map(function(c) {
                  return c.entityAspect.loadNavigationProperty("orders");
              });
              return Q.all(promises);
          }).then(function() {
              cust = core.arrayFirst(custs, function(c) {
                  return c.getProperty("orders").length > 0;
              });
              if (cust == null) {
                  throw new Error("Test error - need a customer with orders");
              }
              companyName = cust.getProperty("companyName");
              newCompanyName = testFns.morphStringProp(cust, "companyName");
              ok(cust.entityAspect.entityState.isModified(), "should be modified");
              orders = cust.getProperty("orders");
              orders.forEach(function(o) {
                  testFns.morphStringProp(o, "shipName");
                  ok(o.entityAspect.entityState.isModified(), "should be modified");
              });
              return em.saveChanges();
          }).then(function(saveResult) {
              ok(!em.hasChanges());
              var entities = saveResult.entities;
              ok(entities.length === 1 + orders.length, "wrong number of entities returned");
              ok(saveResult.keyMappings.length === 0, "no key mappings should be returned");
  
              entities.forEach(function(e) {
                  ok(e.entityAspect.entityState.isUnchanged, "entity is not in unchanged state");
                  if (e.entityType === cust.entityType) {
                      ok(e === cust, "cust does not match");
                  } else {
                      ok(orders.indexOf(e) >= 0, "order does not match");
                  }
              });
  
              ok(cust.getProperty("companyName") === newCompanyName, "company name was not changed");
              ok(cust.entityAspect.entityState.isUnchanged(), "entityState should be unchanged");
              var q2 = EntityQuery.fromEntities(cust);
  
              return em.executeQuery(q2);
          }).then(function(data2) {
              var entities2 = data2.results;
              ok(entities2.length === 1, "should only get a single entity");
              ok(entities2[0] === cust, "requery does not match cust");
              ok(cust.getProperty("companyName") === newCompanyName, "company name was not changed on requery");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete parent, children stranded", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              zzz.cust1.entityAspect.setDeleted();
              var order1custid = zzz.order1.getProperty("customerID");
              ok(order1custid == null, "cust id should have been null'd");
              return em.saveChanges();
          }).then(function(sr) {
              ok(true, "saved ok - children null'd");
              // this can occur if we have a foreign key constraint on customer.orders            
              //}).fail(function (error) {
              //ok(em.hasChanges());
              //ok(error instanceof Error, "should be an error");
              //ok(error.message.indexOf("FOREIGN KEY") >= 0, "message should contain 'FOREIGN KEY'");
              //});
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("delete parent, then clear", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              ok(!em.hasChanges());
              zzz.cust1.entityAspect.setDeleted();
              zzz.order1.entityAspect.setDeleted();
              zzz.order2.entityAspect.setDeleted();
              ok(zzz.order1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(zzz.cust1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(em.hasChanges());
              return em.saveChanges();
          }).then(function (sr) {
              try {
                  em.clear();
                  
              } catch(e) {
                  ok(false, "clear should not fail: " + e);
              }
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete parent then delete children", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              ok(!em.hasChanges());
              zzz.cust1.entityAspect.setDeleted();
              zzz.order1.entityAspect.setDeleted();
              zzz.order2.entityAspect.setDeleted();
              ok(zzz.order1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(zzz.cust1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(em.hasChanges());
              return em.saveChanges();
          }).then(function(sr) {
              ok(!em.hasChanges());
              ok(sr.entities.length === 3, "should be 3 entities saved");
              ok(zzz.order1.entityAspect.entityState.isDetached(), "order1 should be marked as detached");
              ok(zzz.order2.entityAspect.entityState.isDetached(), "order2 should be marked as detached");
              ok(zzz.cust1.entityAspect.entityState.isDetached(), "cust1 should be marked as detached");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete children then delete parent", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              var orders = zzz.cust1.getProperty("orders");
              ok(zzz.order1 === orders[0]);
              var cust1a = zzz.order1.getProperty("customer");
              ok(cust1a === zzz.cust1);
  
  
              zzz.order1.entityAspect.setDeleted();
              zzz.order2.entityAspect.setDeleted();
              zzz.cust1.entityAspect.setDeleted();
              ok(zzz.order1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(zzz.cust1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              return em.saveChanges();
          }).then(function (sr) {
              ok(!em.hasChanges());
              ok(sr.entities.length === 3, "should be 3 entities saved");
              ok(zzz.order1.entityAspect.entityState.isDetached(), "order1 should be marked as detached");
              ok(zzz.order2.entityAspect.entityState.isDetached(), "order2 should be marked as detached");
              ok(zzz.cust1.entityAspect.entityState.isDetached(), "cust1 should be marked as detached");
          }).fail(testFns.handleFail).fin(start);
      });
      
      test("delete children then delete parent after query", function () {
          var em = newEm();
          var em2 = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          var cust;
          em.saveChanges().then(function(saveResult) {
              var q = EntityQuery.fromEntities(zzz.cust1);
              q = EntityQuery.from("Customers").where(q.wherePredicate);
              return em2.executeQuery(q);
          }).then(function(data) {
              // this step is to avoid having to do an expand above - allows this test to be used for Mongo as well.
              cust = data.results[0];
              return cust.entityAspect.loadNavigationProperty("orders");
          }).then(function(data2) {
              var orders = cust.getProperty("orders").slice(0);
              orders.forEach(function(o) {
                  o.entityAspect.setDeleted();
              });
              cust.entityAspect.setDeleted();
              ok(orders[0].entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(cust.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              return em2.saveChanges();
          }).then(function (sr) {
              ok(!em2.hasChanges(), "em should have no changes");
              ok(sr.entities.length === 3, "should be 3 entities saved");
              sr.entities.forEach(function(e) {
                  ok(e.entityAspect.entityState.isDetached(), "entity should be marked as detached");
              });
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete children, leave parent alone", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              zzz.order1.entityAspect.setDeleted();
              ok(zzz.cust1.getProperty("orders").length === 1, "should only be 1 order now");
              zzz.order2.entityAspect.setDeleted();
              ok(zzz.cust1.getProperty("orders").length === 0, "should be no orders now");
              ok(zzz.order1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              ok(zzz.cust1.entityAspect.entityState.isUnchanged(), "should be unchanged");
              return em.saveChanges();
          }).then(function(sr) {
              ok(!em.hasChanges());
              ok(zzz.order1.entityAspect.entityState.isDetached(), "should be marked as detached");
              ok(zzz.cust1.getProperty("orders").length === 0, "should be no orders now");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("delete parent, move children", function () {
          var em = newEm();
          var zzz = createParentAndChildren(em);
          stop();
          em.saveChanges().then(function(saveResult) {
              zzz.cust1.entityAspect.setDeleted();
              zzz.order1.setProperty("customer", zzz.cust2);
              ok(zzz.order1.entityAspect.entityState.isModified(), "should be marked as modified");
              zzz.order2.setProperty("customer", zzz.cust2);
              ok(zzz.cust1.entityAspect.entityState.isDeleted(), "should be marked as deleted");
              return em.saveChanges();
          }).then(function(sr2) {
              ok(!em.hasChanges());
              ok(sr2.entities.length === 3);
              ok(zzz.cust1.entityAspect.entityState.isDetached(), "should be marked as detached");
              ok(zzz.order1.entityAspect.entityState.isUnchanged(), "should be marked as unchanged");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("concurrency violation", function () {
          var em = newEm();
          var em2 = newEm();
          var q = new EntityQuery()
              .from("Customers")
              .take(2);
  
          stop();
          var cust;
          var sameCust;
          em.executeQuery(q).then(function(data) {
              // query cust
              cust = data.results[0];
              var q2 = EntityQuery.fromEntities(cust);
              return em2.executeQuery(q2);
          }).then(function(data2) {
              // query same cust in dif em
              // and modify it and resave it
              ok(data2.results.length == 1, "should only have 1 result");
              sameCust = data2.results[0];
              ok(cust.entityAspect.getKey().equals(sameCust.entityAspect.getKey()), "should be the same key");
              testFns.morphStringProp(sameCust, "companyName");
              return em2.saveChanges();
          }).then(function(sr2) {
              testFns.morphStringProp(cust, "companyName");
              return em.saveChanges();
          }).then(function(sr2) {
              ok(false, "should not get here, save should have failed");
          }).fail(function(error) {
              ok(em.hasChanges());
              var exceptionType = error.detail.ExceptionType.toLowerCase();
              ok((exceptionType.indexOf("concurrency") >= 0 || exceptionType.indexOf("staleobjectstate") >= 0), "wrong error message: " + error.detail.ExceptionType);
          }).fin(start);
      });
      
      //test("concurrency violation on delete", function () {
      //    ok(false, "not yet implemented");
      //});
  
      test("insert of existing entity", function () {
          var em = newEm();
          // need to use a resource that does NOT do autoGeneratedKeys
          var resourceName = testFns.DEBUG_MONGO ? "Products" : "OrderDetails";
          var q = new EntityQuery()
              .from(resourceName)
              .take(2);
  
          stop();
          var em2;
          em.executeQuery(q).then(function(data) {
              var o = data.results[0];
              em.detachEntity(o);
              em2 = newEm();
              em2.addEntity(o);
              return em2.saveChanges();
          }).then(function(sr) {
              ok(false, "shouldn't get here");
          }).fail(function(error) {
              ok(em2.hasChanges());
              var frag = (testFns.DEBUG_MONGO) ? "duplicate key error" : "primary key constraint";
              ok(error.message.toLowerCase().indexOf(frag) >= 0, "wrong error message: " + error.message);
  
          }).fin(start);
      });
  
      test("insert using existing entity re-attached", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped tests - not applicable to OData");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "TODO for Mongo - need to create this test");
              return;
          }
  
          var em = newEm();
          var q = new EntityQuery()
              .from("TimeGroups")
              .take(2);
  
          stop();
          em.executeQuery(q).then(function (data) {
              var tg = data.results[0];
              em.detachEntity(tg);
              tg.Id = -1;
              em.attachEntity(tg, breeze.EntityState.Added);
              tg.Comment = "This was re-attached";
              return em.saveChanges();
          }).then(function (sr) {
              ok(true, "save successful");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("insert with generated key", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server side key generator (except identity)");
              return;
          };
  
          var em = newEm();
  
          var region1 = createRegion(em, "1");
          var k1 = region1.entityAspect.getKey();
  
          var region2 = createRegion(em, "2");
          var k2 = region2.entityAspect.getKey();
  
          stop();
          em.saveChanges().then(function (data) {
              ok(!em.hasChanges());
              ok(data.entities.length === 2);
              ok(!region1.entityAspect.getKey().equals(k1));
              ok(!region2.entityAspect.getKey().equals(k2));
              return data;
          }).then(function(data2) {
              // curious about synchronous results
              ok(data2.entities.length == 2);
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("insert uni (1-n) relationships with generated key", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server side key generator (except identity)");
              return;
          };
  
          var em = newEm();
  
          var region1 = createRegion(em, "1");
          var k1 = region1.entityAspect.getKey();
          var terrs1 = region1.getProperty("territories");
          var terr1a = createTerritory(em, "test 1a");
          var terr1b = createTerritory(em, "test 1b");
          terrs1.push(terr1a);
          terrs1.push(terr1b);
  
          var region2 = createRegion(em, "2");
          var k2 = region2.entityAspect.getKey();
          var terrs2 = region2.getProperty("territories");
          var terr2a = createTerritory(em, "test 2a");
          var terr2b = createTerritory(em, "test 2b");
          terrs2.push(terr2a);
          terrs2.push(terr2b);
  
          ok(region1.getProperty("territories").length === 2, "should have two terrs");
          ok(region2.getProperty("territories").length === 2, "should have two terrs");
  
          stop();
          var terrs1x, terrs2x, region1y, terrs1y;
          var em2 = newEm();
          em.saveChanges().then(function (data) {
              ok(!em.hasChanges());
  
              ok(data.entities.length === 6);
              ok(!region1.entityAspect.getKey().equals(k1));
              terrs1x = region1.getProperty("territories");
              ok(terrs1x === terrs1, "territories should be the same");
              ok(terrs1x.length == 2, "terrs1 - length should be 2");
              ok(!region2.entityAspect.getKey().equals(k2));
              terrs2x = region2.getProperty("territories");
              ok(terrs2x === terrs2, "territories should be the same");
              ok(terrs2x.length == 2, "terrs2 - length should be 2");
              ok(terrs2x[0].getProperty("regionID") === region2.getProperty(testFns.regionKeyName), "regionId should have been updated");
              // now move them all onto region1;
              terrs2x.slice(0).forEach(function (t) {
                  t.setProperty("regionID", region1.getProperty(testFns.regionKeyName));
              });
              ok(terrs1x.length == 4, "terrs1x should now be length 4");
              ok(terrs2x.length == 0, "terrs2x should now be length 0");
              return em.saveChanges();
          }).then(function(sr2) {
              ok(sr2.entities.length == 2, "should have saved 2 recs");
              ok(terrs1x.length == 4, "terrs1x should now be length 4");
              ok(terrs2x.length == 0, "terrs2x should now be length 0");
              return EntityQuery.fromEntities(region1).using(em2).execute();
          }).then(function (data3) {
              region1y = data3.results[0];
              terrs1y = region1y.getProperty("territories");
              return terrs1y.load();
          }).then(function(data4) {
              ok(data4.results.length === 4, "should be 4 terrs");
              ok(terrs1y.length === 4, "terrs1y should be of length 4");
  
          }).fail(testFns.handleFail).fin(start);
      });
  
  
      test("insert uni (1-n) relationships with generated key - v2", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server side key generator (except identity)");
              return;
          };
  
          if (testFns.DEBUG_MONGO) {
              ok(true, "N/A for MONGO - expand is not YET supported.");
              return;
          }
  
          var em = newEm();
          var em2 = newEm();
  
          var region1 = createRegion(em, "1");
          var k1 = region1.entityAspect.getKey();
          var terrs1 = region1.getProperty("territories");
          var terr1a = createTerritory(em, "test 1a");
          var terr1b = createTerritory(em, "test 1b");
          terr1a.setProperty("regionID", region1.getProperty(testFns.regionKeyName));
          terr1b.setProperty("regionID", region1.getProperty(testFns.regionKeyName));
  
          var region2 = createRegion(em, "2");
          var k2 = region2.entityAspect.getKey();
          var terrs2 = region2.getProperty("territories");
          var terr2a = createTerritory(em, "test 2a");
          var terr2b = createTerritory(em, "test 2b");
          terr2a.setProperty("regionID", region2.getProperty(testFns.regionKeyName));
          terr2b.setProperty("regionID", region2.getProperty(testFns.regionKeyName));
  
          ok(region1.getProperty("territories").length === 2, "should have two terrs");
          ok(region2.getProperty("territories").length === 2, "should have two terrs");
  
          var terrs1x, terrs2x, region1ID, region1y, terrs1y;
          
          stop();
          em.saveChanges().then(function (data) {
              ok(!em.hasChanges());
              ok(data.entities.length === 6);
              ok(!region1.entityAspect.getKey().equals(k1));
              terrs1x = region1.getProperty("territories");
              ok(terrs1x === terrs1,"territories should be the same");
              ok(terrs1x.length == 2, "terrs1 - length should be 2");
              ok(!region2.entityAspect.getKey().equals(k2));
              terrs2x = region2.getProperty("territories");
              ok(terrs2x === terrs2, "territories should be the same");
              ok(terrs2x.length == 2, "terrs2 - length should be 2");
              ok(terrs2x[0].getProperty("regionID") === region2.getProperty(testFns.regionKeyName), "regionId should have been updated");
              // now move them all onto region1;
              terrs2x.slice(0).forEach(function (t) {
                  terrs1x.push(t);
              });
              ok(terrs1x.length == 4, "terrs1x should now be length 4");
              ok(terrs2x.length == 0, "terrs2x should now be length 0");
              return em.saveChanges();
          }).then(function (sr2) {
              ok(sr2.entities.length == 2, "should have saved 2 recs");
              ok(terrs1x.length == 4, "terrs1x should now be length 4");
              ok(terrs2x.length == 0, "terrs2x should now be length 0");
              return EntityQuery.fromEntities(region1).expand("territories").using(em).execute();
          }).then(function (data3) {
              ok(data3.results.length === 1, "should be 1 region");
              ok(region1 === data3.results[0], "should be same region");
              ok(terrs1x.length === 4, "terrs1x should be of length 4");
              return EntityQuery.fromEntities(region1).expand("territories").using(em2).execute();
          }).then(function(data4) {
              ok(data4.results.length === 1, "should be 1 region");
              terrs1y = data4.results[0].getProperty("territories");
              ok(terrs1y.length === 4, "should still be 4 recs");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("insert uni (1-n) relationships with unattached children - v3", function () {
          if (testFns.DEBUG_ODATA) {
              ok(true, "Skipped test - OData does not support server side key generator (except identity)");
              return;
          };
  
          var em = newEm();
          var em2 = newEm();
  
          var region1 = createRegion(em, "1");
          var k1 = region1.entityAspect.getKey();
          var terrs1 = region1.getProperty("territories");
          var terr1a = createTerritory(em, "test 1a");
          var terr1b = createTerritory(em, "test 1b");
          terr1a.setProperty("regionID", region1.getProperty(testFns.regionKeyName));
          terr1b.setProperty("regionID", region1.getProperty(testFns.regionKeyName));
  
          var region2 = createRegion(em, "2");
          var k2 = region2.entityAspect.getKey();
          var terrs2 = region2.getProperty("territories");
          var terr2a = createTerritory(em, "test 2a");
          var terr2b = createTerritory(em, "test 2b");
          terr2a.setProperty("regionID", region2.getProperty(testFns.regionKeyName));
          terr2b.setProperty("regionID", region2.getProperty(testFns.regionKeyName));
  
          ok(region1.getProperty("territories").length === 2, "should have two terrs");
          ok(region2.getProperty("territories").length === 2, "should have two terrs");
          var terrs1x, terrs2x, region1ID, region1y, terrs1y;
          
          
          stop();
          em.saveChanges().then(function (data) {
              ok(!em.hasChanges());
              ok(data.entities.length === 6);
              ok(!region1.entityAspect.getKey().equals(k1));
              var territories = em.getEntities("Territory");
              return EntityQuery.fromEntities(territories).using(em2).execute();
          }).then(function(data2) {
              ok(data2.results.length === 4, "should be 4 recs");
              return EntityQuery.fromEntities(region1).using(em2).execute();
          }).then(function (data3) {
              ok(data3.results.length === 1, "should be 1 rec");
              var region1a = data3.results[0];
              var terrs1a = region1a.getProperty("territories");
              ok(terrs1a.length == 2, "should be 2 terrs in region1");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("save of deleted entity should not trigger validation", function () {
          // TODO: OData bug here is because of region - AutoGeneratedKeyType
          var em = newEm();
          var cust = createCustomer(em);
          stop();
          ok(em.hasChanges());
          em.saveChanges().then(function (sr) {
              ok(!em.hasChanges());
              ok(sr.entities.length === 1, "one entity should have been saved");
              ok(sr.entities[0] === cust, "save result should contain cust");
              cust.setProperty("companyName", "");
              cust.entityAspect.setDeleted();
              ok(em.hasChanges());
              return em.saveChanges();
          }).then(function (sr2) {
              ok(!em.hasChanges());
              ok(sr2.entities.length === 1, "one entity should have been saved");
              ok(sr2.entities[0] === cust, "save result should contain region");
              ok(cust.entityAspect.entityState.isDetached(), "cust should now be detached");
          }).fail(testFns.handleFail).fin(start);
      });
  
      test("bad save call", function () {
          var em = newEm();
          try {
              em.saveChanges(null, new SaveOptions(), "adfa");
          } catch (e) {
              ok(e.message.indexOf("callback") >= 0);
          }
          try {
              em.saveChanges(null, "adfa");
          } catch (e) {
              ok(e.message.indexOf("saveOptions") >= 0);
          }
          try {
              em.saveChanges("adfa");
          } catch (e) {
              ok(e.message.indexOf("entities") >= 0);
          }
  
      });
  
      test("cleanup  test data", function() {
          if (testFns.DEBUG_MONGO) {
              ok(true, "NA for Mongo - expand not yet supported");
              return;
          }
  
          var em = newEm();
          var p = breeze.Predicate.create("companyName", FilterQueryOp.StartsWith, "Test")
              .or("companyName", FilterQueryOp.StartsWith, "foo");
          var q = EntityQuery.from("Customers").where(p).expand("orders")
          stop();
          em.executeQuery(q).then(function(data) {
              data.results.forEach(function(cust) {
                  var orders = cust.getProperty("orders").slice(0);
                  orders.forEach(function(order) {
                      order.entityAspect.setDeleted();
                  });
                  cust.entityAspect.setDeleted();
              });
              return em.saveChanges();
          }).then(function(sr) {
              ok(sr, "save failed");
              ok(sr.entities.length, "deleted count:" + sr.entities.length);
          }).fail(testFns.handleFail).fin(start);
      });
  
      function createCustomer(em) {
          var metadataStore = em.metadataStore;
          var custType = metadataStore.getEntityType("Customer");
          var cust1 = custType.createEntity();
          cust1.setProperty("companyName", "Test_js_1");
          cust1.setProperty("city", "Oakland");
          cust1.setProperty("rowVersion", 13);
          cust1.setProperty("fax", "510 999-9999");
          em.addEntity(cust1);
          return cust1;
      }
  
      function createParentAndChildren(em) {
          var metadataStore = em.metadataStore;
          var custType = metadataStore.getEntityType("Customer");
          var orderType = metadataStore.getEntityType("Order");
          var cust1 = custType.createEntity();
          cust1.setProperty("companyName", "Test_js_1");
          cust1.setProperty("city", "Oakland");
          cust1.setProperty("rowVersion", 13);
          cust1.setProperty("fax", "510 999-9999");
          var cust2 = custType.createEntity();
          cust2.setProperty("companyName", "Test_js_2");
          cust2.setProperty("city", "Emeryville");
          cust2.setProperty("rowVersion", 1);
          cust2.setProperty("fax", "510 888-8888");
          em.addEntity(cust1);
          em.addEntity(cust2);
          var order1 = orderType.createEntity();
          order1.setProperty("orderDate", new Date());
          var order2 = orderType.createEntity();
          var orders = cust1.getProperty("orders");
          order2.setProperty("orderDate", new Date());
          orders.push(order1);
          orders.push(order2);
          var keyValues = [cust1.getProperty(testFns.customerKeyName),
              cust2.getProperty(testFns.customerKeyName),
              order1.getProperty(testFns.orderKeyName),
              order2.getProperty(testFns.orderKeyName)];
          return {
              cust1: cust1,
              cust2: cust2,
              order1: order1,
              order2: order2,
              keyValues: keyValues
          };
      }
      
      function createParentAndManyChildren(em) {
          var metadataStore = em.metadataStore;
          var custType = metadataStore.getEntityType("Customer");
          var orderType = metadataStore.getEntityType("Order");
          var cust1 = custType.createEntity();
          cust1.setProperty("companyName", "Test_js_1");
          cust1.setProperty("city", "Oakland");
          cust1.setProperty("rowVersion", 13);
          cust1.setProperty("fax", "510 999-9999");
          em.addEntity(cust1);
          var orders = cust1.getProperty("orders");
          for (var i = 1; i < 50; i++) {
              var order1 = orderType.createEntity();
              order1.setProperty("shipRegion", "foo-region");
              orders.push(order1);
          }
      }
  
      function createRegion(em, descr) {
          var regionType = testFns.metadataStore.getEntityType("Region");
          var region = regionType.createEntity();
          
          region.setProperty("regionDescription", "Test-" + descr + "-" + new Date().toDateString());
          em.addEntity(region);
          return region;
      }
  
      function createTerritory(em, descr) {
          var territoryType = testFns.metadataStore.getEntityType("Territory");
          var territory = territoryType.createEntity();
          territory.setProperty("territoryDescription", "Test-" + descr + "-" + new Date().toDateString());
          em.addEntity(territory);
          return territory;
      }
      
  })(breezeTestFns);