public async Task UpdateOperationAsync_UpsertTheItemInOperation_AndDeletesTheError()
        {
            var client  = new MobileServiceClient("http://www.test.com");
            var store   = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId      = "def";
            string tableName   = "test";

            var item = new JObject()
            {
                { "id", itemId }, { "name", "unknown" }
            };

            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary <string, JObject>()
            {
                { operationId, new JObject()
                  {
                      { "id", operationId }, { "version", 1 }
                  } }
            };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject()
            {
                { "id", operationId }, { "version", 1 }, { "item", item.ToString() }, { "kind", (int)MobileServiceTableOperationKind.Delete }
            });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item does not exist
            Assert.IsNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Delete,
                                                             HttpStatusCode.PreconditionFailed,
                                                             tableName,
                                                             item: new JObject()
            {
                { "id", itemId }
            },
                                                             rawResult: "{}",
                                                             result: new JObject());
            var item2 = new JObject()
            {
                { "id", itemId }, { "name", "unknown" }, { "version", 2 }
            };
            await context.UpdateOperationAsync(error, item2);

            var operation = await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId);

            // operation is updated
            Assert.IsNotNull(operation);
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            Assert.AreEqual(operation.GetValue("item").ToString(), item2.ToString(Formatting.None));
        }
        public async Task CancelAndUpdateItemAsync_UpsertsTheItemInLocalStore_AndDeletesTheOperationAndError()
        {
            var client  = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp);
            var store   = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId      = "def";
            string tableName   = "test";


            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary <string, JObject>()
            {
                { operationId, new JObject() }
            };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject());

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item doesn't exist before upsert
            Assert.IsNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             0,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject()
            {
                { "id", itemId }
            },
                                                             rawResult: "{}",
                                                             result: new JObject());

            var item = new JObject()
            {
                { "id", itemId }, { "name", "unknown" }
            };
            await context.CancelAndUpdateItemAsync(error, item);

            // operation is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            JObject upserted = await store.LookupAsync(tableName, itemId);

            // item is upserted
            Assert.IsNotNull(upserted);
            Assert.AreEqual(item, upserted);
        }
        public async Task CancelAndDiscardItemAsync_DeletesTheItemInLocalStore_AndDeletesTheOperationAndError()
        {
            var client  = new MobileServiceClient("http://www.test.com");
            var store   = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId      = "def";
            string tableName   = "test";

            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary <string, JObject>()
            {
                { operationId, new JObject() }
            };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject());
            store.TableMap.Add(tableName, new Dictionary <string, JObject>()
            {
                { itemId, new JObject() }
            });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item exists before upsert
            Assert.IsNotNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             0,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject()
            {
                { "id", itemId }
            },
                                                             rawResult: "{}",
                                                             result: new JObject());

            await context.CancelAndDiscardItemAsync(error);

            // operation is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            // item is upserted
            Assert.IsNull(await store.LookupAsync(tableName, itemId));
        }
        public async Task UpdateOperationAsync_UpsertsTheItemInLocalStore_AndDeletesTheError_FromSyncHandler()
        {
            // Arrange
            string itemId = "abc";
            var    hijack = new TestHttpHandler();

            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.InternalServerError));

            var handler = new MobileServiceSyncHandlerMock();

            handler.PushCompleteAction = async pushCompletionResult =>
            {
                foreach (var error in pushCompletionResult.Errors)
                {
                    await error.UpdateOperationAsync(JObject.Parse("{\"id\":\"abc\",\"__version\":\"Hey\"}"));
                }
            };
            var store = new MobileServiceLocalStoreMock();
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", hijack);
            await service.SyncContext.InitializeAsync(store, handler);

            IMobileServiceSyncTable table = service.GetSyncTable("someTable");

            await table.InsertAsync(new JObject()
            {
                { "id", "abc" }, { "__version", "Wow" }
            });

            // Act
            await(service.SyncContext as MobileServiceSyncContext).PushAsync(CancellationToken.None, MobileServiceTableKind.Table);


            // Assert
            var     syncError = store.TableMap[MobileServiceLocalSystemTables.SyncErrors].Values;
            var     operation = store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Values.FirstOrDefault();
            var     item      = JObject.Parse("{\"id\":\"abc\",\"__version\":\"Hey\"}");
            JObject upserted  = await store.LookupAsync("someTable", itemId);

            // item is upserted
            Assert.IsNotNull(upserted);

            // verify if the record was updated
            Assert.AreEqual(item.ToString(), upserted.ToString());

            // verify if the errors were cleared
            Assert.AreEqual(0, syncError.Count);

            // Verify operation is still present
            Assert.AreEqual(operation.Value <string>("itemId"), itemId);
        }
        private async Task TestOperationModifiedException(bool operationExists, Func <MobileServiceTableOperationError, MobileServiceSyncContext, Task> action, String errorMessage)
        {
            var client  = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp);
            var store   = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId      = "def";
            string tableName   = "test";

            if (operationExists)
            {
                store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject()
                {
                    { "version", 3 }
                });
            }
            else
            {
                // operation exists before cancel
                Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            }

            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject()
            {
                { "id", itemId }
            },
                                                             rawResult: "{}",
                                                             result: new JObject());

            var ex = await ThrowsAsync <InvalidOperationException>(() => action(error, context));

            Assert.AreEqual(ex.Message, errorMessage);
        }
        private async Task UpdateOperationAsync_ConflictOperation_WithNotificationsEnabled_UsesTheCorrectStoreSource()
        {                        
            var client = new MobileServiceClient("http://www.test.com");
            var store = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store, StoreTrackingOptions.NotifyLocalConflictResolutionOperations);
            var manualResetEvent = new ManualResetEventSlim();

            string operationId = "abc";
            string itemId = "def";
            string tableName = "test";


            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary<string, JObject>() { { operationId, new JObject() { { "id", operationId }, { "version", 1 } } } };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject() { { "id", operationId }, { "version", 1 } });
            store.TableMap.Add(tableName, new Dictionary<string, JObject>() { { itemId, new JObject() } });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item exists before upsert
            Assert.IsNotNull(await store.LookupAsync(tableName, itemId));


            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject() { { "id", itemId } },
                                                             rawResult: "{}",
                                                             result: new JObject());
            var item = new JObject() { { "id", itemId }, { "name", "unknown" } };                       
            
            bool sourceIsLocalConflict = false;
            IDisposable subscription = client.EventManager.Subscribe<StoreOperationCompletedEvent>(o =>
            {
                sourceIsLocalConflict = o.Operation.Source == StoreOperationSource.LocalConflictResolution;
                manualResetEvent.Set();
            });

            await context.UpdateOperationAsync(error, item);

            bool resetEventSignaled = manualResetEvent.Wait(1000);
            subscription.Dispose();

            Assert.IsTrue(resetEventSignaled);
            Assert.IsTrue(sourceIsLocalConflict);
        }
        private async Task TestOperationModifiedException(bool operationExists, Func<MobileServiceTableOperationError, MobileServiceSyncContext, Task> action, String errorMessage)
        {
            var client = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp);
            var store = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId = "def";
            string tableName = "test";

            if (operationExists)
            {
                store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject() { { "version", 3 } });
            }
            else
            {
                // operation exists before cancel
                Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            }

            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject() { { "id", itemId } },
                                                             rawResult: "{}",
                                                             result: new JObject());

            var ex = await ThrowsAsync<InvalidOperationException>(() => action(error, context));

            Assert.AreEqual(ex.Message, errorMessage);
        }
        public async Task CancelAndDiscardItemAsync_DeletesTheItemInLocalStore_AndDeletesTheOperationAndError()
        {
            var client = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp);
            var store = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId = "def";
            string tableName = "test";

            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary<string, JObject>() { { operationId, new JObject() } };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject());
            store.TableMap.Add(tableName, new Dictionary<string, JObject>() { { itemId, new JObject() } });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item exists before upsert
            Assert.IsNotNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             0,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject() { { "id", itemId } },
                                                             rawResult: "{}",
                                                             result: new JObject());

            await context.CancelAndDiscardItemAsync(error);

            // operation is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            // item is upserted
            Assert.IsNull(await store.LookupAsync(tableName, itemId));
        }
        public async Task UpdateOperationAsync_UpsertTheItemInOperation_AndDeletesTheError()
        {
            var client = new MobileServiceClient("http://www.test.com");
            var store = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId = "def";
            string tableName = "test";

            var item = new JObject() { { "id", itemId }, { "name", "unknown" } };

            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary<string, JObject>() { { operationId, new JObject() { { "id", operationId }, { "version", 1 } } } };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject() { { "id", operationId }, { "version", 1 }, { "item", item.ToString() }, { "kind", (int)MobileServiceTableOperationKind.Delete } });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item does not exist
            Assert.IsNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Delete,
                                                             HttpStatusCode.PreconditionFailed,
                                                             tableName,
                                                             item: new JObject() { { "id", itemId } },
                                                             rawResult: "{}",
                                                             result: new JObject());
            var item2 = new JObject() { { "id", itemId }, { "name", "unknown" }, { "version", 2 } };
            await context.UpdateOperationAsync(error, item2);

            var operation = await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId);
            // operation is updated
            Assert.IsNotNull(operation);
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            Assert.AreEqual(operation.GetValue("item").ToString(), item2.ToString(Formatting.None));
        }
        public async Task UpdateOperationAsync_UpsertsTheItemInLocalStore_AndDeletesTheError_AndUpdatesTheOperation()
        {
            var client = new MobileServiceClient("http://www.test.com");
            var store = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store);

            string operationId = "abc";
            string itemId = "def";
            string tableName = "test";


            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary<string, JObject>() { { operationId, new JObject() { { "id", operationId }, { "version", 1 } } } };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject() { { "id", operationId }, { "version", 1 } });
            store.TableMap.Add(tableName, new Dictionary<string, JObject>() { { itemId, new JObject() } });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item exists before upsert
            Assert.IsNotNull(await store.LookupAsync(tableName, itemId));

            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject() { { "id", itemId } },
                                                             rawResult: "{}",
                                                             result: new JObject());

            var item = new JObject() { { "id", itemId }, { "name", "unknown" } };
            await context.UpdateOperationAsync(error, item);

            // operation is updated
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // error is deleted
            Assert.IsNull(await store.LookupAsync(MobileServiceLocalSystemTables.SyncErrors, operationId));

            JObject upserted = await store.LookupAsync(tableName, itemId);
            // item is upserted
            Assert.IsNotNull(upserted);
            Assert.AreEqual(item, upserted);
        }
        public async Task UpdateOperationAsync_UpsertsTheItemInLocalStore_AndDeletesTheError_FromSyncHandler()
        {
            // Arrange
            string itemId = "abc";
            var hijack = new TestHttpHandler();
            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.InternalServerError));

            var handler = new MobileServiceSyncHandlerMock();
            handler.PushCompleteAction = async pushCompletionResult =>
            {
                foreach (var error in pushCompletionResult.Errors)
                {
                    await error.UpdateOperationAsync(JObject.Parse("{\"id\":\"abc\",\"__version\":\"Hey\"}"));
                }
            };
            var store = new MobileServiceLocalStoreMock();
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", hijack);
            await service.SyncContext.InitializeAsync(store, handler);

            IMobileServiceSyncTable table = service.GetSyncTable("someTable");

            await table.InsertAsync(new JObject() { { "id", "abc" }, { "__version", "Wow" } });

            // Act
            await (service.SyncContext as MobileServiceSyncContext).PushAsync(CancellationToken.None, MobileServiceTableKind.Table);


            // Assert
            var syncError = store.TableMap[MobileServiceLocalSystemTables.SyncErrors].Values;
            var operation = store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Values.FirstOrDefault();
            var item = JObject.Parse("{\"id\":\"abc\",\"__version\":\"Hey\"}");
            JObject upserted = await store.LookupAsync("someTable", itemId);
            // item is upserted
            Assert.IsNotNull(upserted);

            // verify if the record was updated
            Assert.AreEqual(item.ToString(), upserted.ToString());

            // verify if the errors were cleared
            Assert.AreEqual(0, syncError.Count);

            // Verify operation is still present
            Assert.AreEqual(operation.Value<string>("itemId"),itemId);
        }
        private async Task UpdateOperationAsync_ConflictOperation_WithNotificationsEnabled_UsesTheCorrectStoreSource()
        {
            var client  = new MobileServiceClient("http://www.test.com");
            var store   = new MobileServiceLocalStoreMock();
            var context = new MobileServiceSyncContext(client);
            await context.InitializeAsync(store, StoreTrackingOptions.NotifyLocalConflictResolutionOperations);

            var manualResetEvent = new ManualResetEventSlim();

            string operationId = "abc";
            string itemId      = "def";
            string tableName   = "test";


            store.TableMap[MobileServiceLocalSystemTables.SyncErrors] = new Dictionary <string, JObject>()
            {
                { operationId, new JObject()
                  {
                      { "id", operationId }, { "version", 1 }
                  } }
            };
            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject()
            {
                { "id", operationId }, { "version", 1 }
            });
            store.TableMap.Add(tableName, new Dictionary <string, JObject>()
            {
                { itemId, new JObject() }
            });

            // operation exists before cancel
            Assert.IsNotNull(await store.LookupAsync(MobileServiceLocalSystemTables.OperationQueue, operationId));
            // item exists before upsert
            Assert.IsNotNull(await store.LookupAsync(tableName, itemId));


            var error = new MobileServiceTableOperationError(operationId,
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.Conflict,
                                                             tableName,
                                                             item: new JObject()
            {
                { "id", itemId }
            },
                                                             rawResult: "{}",
                                                             result: new JObject());
            var item = new JObject()
            {
                { "id", itemId }, { "name", "unknown" }
            };

            bool        sourceIsLocalConflict = false;
            IDisposable subscription          = client.EventManager.Subscribe <StoreOperationCompletedEvent>(o =>
            {
                sourceIsLocalConflict = o.Operation.Source == StoreOperationSource.LocalConflictResolution;
                manualResetEvent.Set();
            });

            await context.UpdateOperationAsync(error, item);

            bool resetEventSignaled = manualResetEvent.Wait(1000);

            subscription.Dispose();

            Assert.IsTrue(resetEventSignaled);
            Assert.IsTrue(sourceIsLocalConflict);
        }