private VersionedType(VersionedType other) { this.Id = other.Id; this.Name = other.Name; this.Number = other.Number; this.Version = other.Version; this.CreatedAt = other.CreatedAt; this.UpdatedAt = other.UpdatedAt; }
private static ZumoTest CreateSystemPropertiesTest(bool useTypedTable) { return new ZumoTest("System properties in " + (useTypedTable ? "" : "un") + "typed tables", async delegate(ZumoTest test) { var client = ZumoTestGlobals.Instance.Client; var typedTable = client.GetTable<VersionedType>(); var untypedTable = client.GetTable(ZumoTestGlobals.StringIdRoundTripTableName); untypedTable.SystemProperties = MobileServiceSystemProperties.CreatedAt | MobileServiceSystemProperties.UpdatedAt | MobileServiceSystemProperties.Version; DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; test.AddLog("Using seed: {0}", seed); Random rndGen = new Random(seed); VersionedType item = null; JObject untypedItem = null; DateTime createdAt, updatedAt; string id; if (useTypedTable) { item = new VersionedType(rndGen); await typedTable.InsertAsync(item); test.AddLog("Inserted: {0}", item); id = item.Id; createdAt = item.CreatedAt; updatedAt = item.UpdatedAt; } else { untypedItem = new JObject(); untypedItem.Add("name", "unused"); untypedItem = (JObject)(await untypedTable.InsertAsync(untypedItem)); test.AddLog("Inserted: {0}", untypedItem); id = (string)untypedItem["id"]; createdAt = untypedItem["__createdAt"].ToObject<DateTime>(); updatedAt = untypedItem["__updatedAt"].ToObject<DateTime>(); } test.AddLog("Now adding a new item"); DateTime otherCreatedAt, otherUpdatedAt; string otherId; if (useTypedTable) { item = new VersionedType(rndGen); await typedTable.InsertAsync(item); test.AddLog("Inserted: {0}", item); otherId = item.Id; otherCreatedAt = item.CreatedAt; otherUpdatedAt = item.UpdatedAt; } else { untypedItem = new JObject(); untypedItem.Add("name", "unused"); untypedItem = (JObject)(await untypedTable.InsertAsync(untypedItem)); test.AddLog("Inserted: {0}", untypedItem); otherId = (string)untypedItem["id"]; otherCreatedAt = untypedItem["__createdAt"].ToObject<DateTime>(); otherUpdatedAt = untypedItem["__updatedAt"].ToObject<DateTime>(); } if (createdAt >= otherCreatedAt) { test.AddLog("Error, first __createdAt value is not smaller than second one"); return false; } if (updatedAt >= otherUpdatedAt) { test.AddLog("Error, first __updatedAt value is not smaller than second one"); return false; } createdAt = otherCreatedAt; updatedAt = otherUpdatedAt; test.AddLog("Now updating the item"); if (useTypedTable) { item = new VersionedType(rndGen) { Id = otherId }; await typedTable.UpdateAsync(item); test.AddLog("Updated: {0}", item); otherUpdatedAt = item.UpdatedAt; otherCreatedAt = item.CreatedAt; } else { untypedItem = new JObject(new JProperty("id", otherId), new JProperty("name", "other name")); untypedItem = (JObject)(await untypedTable.UpdateAsync(untypedItem)); test.AddLog("Updated: {0}", untypedItem); otherCreatedAt = untypedItem["__createdAt"].ToObject<DateTime>(); otherUpdatedAt = untypedItem["__updatedAt"].ToObject<DateTime>(); } if (createdAt != otherCreatedAt) { test.AddLog("Error, update changed the value of the __createdAt property"); return false; } if (otherUpdatedAt <= updatedAt) { test.AddLog("Error, update did not change the __updatedAt property to a later value"); return false; } test.AddLog("Cleanup: deleting items"); await untypedTable.DeleteAsync(new JObject(new JProperty("id", id))); await untypedTable.DeleteAsync(new JObject(new JProperty("id", otherId))); return true; }); }
private static ZumoTest CreateOptimisticConcurrencyTest(string testName, Func<VersionedType, VersionedType, VersionedType> mergingPolicy) { return new ZumoTest(testName, async delegate(ZumoTest test) { var client = ZumoTestGlobals.Instance.Client; var table = client.GetTable<VersionedType>(); DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; test.AddLog("Using seed: {0}", seed); Random rndGen = new Random(seed); var item = new VersionedType(rndGen); await table.InsertAsync(item); test.AddLog("[client 1] Inserted item: {0}", item); var client2 = new MobileServiceClient(client.ApplicationUri, client.ApplicationKey); var table2 = client.GetTable<VersionedType>(); var item2 = await table2.LookupAsync(item.Id); test.AddLog("[client 2] Retrieved the item"); item2.Name = Util.CreateSimpleRandomString(rndGen, 20); item2.Number = rndGen.Next(100000); test.AddLog("[client 2] Updated the item, will update on the server now"); await table2.UpdateAsync(item2); test.AddLog("[client 2] Item has been updated: {0}", item2); test.AddLog("[client 1] Will try to update; should fail"); MobileServicePreconditionFailedException<VersionedType> ex = null; try { item.Name = Util.CreateSimpleRandomString(rndGen, 20); await table.UpdateAsync(item); test.AddLog("[client 1] Error, the update succeeded, but it should have failed. Item = {0}", item); return false; } catch (MobileServicePreconditionFailedException<VersionedType> e) { test.AddLog("[client 1] Received expected exception; server item = {0}", e.Item); ex = e; } var serverItem = ex.Item; if (serverItem.Version != item2.Version) { test.AddLog("[client 1] Error, server item's version is not the same as the second item version"); return false; } var cachedMergedItem = mergingPolicy(item, serverItem); var mergedItem = mergingPolicy(item, serverItem); test.AddLog("[client 1] Merged item: {0}", mergedItem); test.AddLog("[client 1] Trying to update it again, should succeed this time"); await table.UpdateAsync(mergedItem); test.AddLog("[client 1] Updated the item: {0}", mergedItem); if (!cachedMergedItem.Equals(mergedItem)) { test.AddLog("[client 1] Error, the server version of the merged item doesn't match the client one"); return false; } test.AddLog("[client 2] Refreshing the item"); await table2.RefreshAsync(item2); test.AddLog("[client 2] Refreshed the item: {0}", item2); if (!item2.Equals(mergedItem)) { test.AddLog("[client] Error, item is different than the item from the client 1"); return false; } return true; }); }
private static ZumoTest CreateOptimisticConcurrencyWithServerConflictsTest(string testName, bool clientWins) { return new ZumoTest(testName, async delegate(ZumoTest test) { var client = ZumoTestGlobals.Instance.Client; var table = client.GetTable<VersionedType>(); DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; test.AddLog("Using seed: {0}", seed); Random rndGen = new Random(seed); var item = new VersionedType(rndGen); await table.InsertAsync(item); test.AddLog("[client 1] Inserted item: {0}", item); var client2 = new MobileServiceClient(client.ApplicationUri, client.ApplicationKey); var table2 = client.GetTable<VersionedType>(); var item2 = await table2.LookupAsync(item.Id); test.AddLog("[client 2] Retrieved the item"); item2.Name = Util.CreateSimpleRandomString(rndGen, 20); item2.Number = rndGen.Next(100000); test.AddLog("[client 2] Updated the item, will update on the server now"); await table2.UpdateAsync(item2); test.AddLog("[client 2] Item has been updated: {0}", item2); test.AddLog("[client 1] Will try to update - using policy that data on {0} wins", clientWins ? "client" : "server"); string oldName = item2.Name; string newName = Util.CreateSimpleRandomString(rndGen, 20); item.Name = newName; await table.UpdateAsync(item, new Dictionary<string, string> { { "conflictPolicy", clientWins ? "clientWins" : "serverWins" } }); test.AddLog("[client 1] Updated the item: {0}", item); test.AddLog("[client 2] Now refreshing the second item"); await table2.RefreshAsync(item2); test.AddLog("[client 2] Refreshed: {0}", item2); if (clientWins) { // The name should be the new one if (item.Name != newName || item2.Name != newName) { test.AddLog("Error, name wasn't updated in a 'client wins' policy"); return false; } } else { // The name should have remained the old one if (item.Name != oldName || item2.Name != oldName) { test.AddLog("Error, name was updated in a 'server wins' policy"); return false; } } test.AddLog("Table operations behaved as expected. Cleaning up..."); await table.DeleteAsync(item); test.AddLog("...done"); return true; }); }