private async Task ResolveConflictAsync <T>(MobileServiceTableOperationError error) where T : TableData
        {
            var serverItem = error.Result.ToObject <T>();
            var localItem  = error.Item.ToObject <T>();

            // TEMP: Solve internal server error
            if (serverItem == null)
            {
                return;
            }

            // Note that you need to implement the public override Equals(TodoItem item)
            // method in the Model for this to work
            if (serverItem.Equals(localItem))
            {
                // Items are the same, so ignore the conflict
                await error.CancelAndDiscardItemAsync();

                return;
            }

            // Client Always Wins
            localItem.Version = serverItem.Version;
            await error.UpdateOperationAsync(JObject.FromObject(localItem));

            // Server Always Wins
            // await error.CancelAndDiscardItemAsync();
        }
Exemplo n.º 2
0
        private async Task ResolveErrorAsync(MobileServiceTableOperationError error)
        {
            //if (result.Result == null || result.Item == null)
            //    return;

            //var serverItem = result.Result.ToObject<DiaryEntry>();
            //var localItem = result.Item.ToObject<DiaryEntry>();

            //if (serverItem.Id == localItem.Id
            //    && serverItem.Title ==localItem.Title)
            //{
            //    await result.CancelAndDiscardItemAsync();
            //}
            //else
            //{
            //    localItem.AzureVersion = serverItem.AzureVersion;
            //    await result.UpdateOperationAsync(JObject.FromObject(localItem));
            //}

            if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
            {
                //Update failed, reverting to server's copy.
                await error.CancelAndUpdateItemAsync(error.Result);
            }
            else
            {
                // Discard local change.
                await error.CancelAndDiscardItemAsync();
            }

            Debug.WriteLine(@"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
        }
        async Task ResolveError(MobileServiceTableOperationError result)
        {
            //			throw new NotImplementedException ();
            if (result.Result == null || result.Item == null)
            {
                return;
            }

            var serverItem = result.Result.ToObject <SurveyResponse> ();
            var localItem  = result.Item.ToObject <SurveyResponse> ();

            if (serverItem.Name == localItem.Name &&
                serverItem.ResponseIndex == localItem.ResponseIndex &&
                serverItem.SurveyQuestionId == localItem.SurveyQuestionId)
            {
                // Items are the same, so ignore the conflict
                await result.CancelAndDiscardItemAsync();
            }
            else
            {
                // Always take the client
                localItem.AzureVersion = serverItem.AzureVersion;
                await result.UpdateOperationAsync(JObject.FromObject(localItem));
            }
        }
Exemplo n.º 4
0
        private async Task ResolveErrorAsync(MobileServiceTableOperationError result)
        {
            // Ignoramos si no podemos validar ambas partes.
            if (result.Result == null || result.Item == null)
            {
                return;
            }


            var serverAviso = result.Result.ToObject <Aviso>();
            var localAviso  = result.Item.ToObject <Aviso>();


            if (serverAviso.Id == localAviso.Id)
            {
                // Los elementos sin iguales, ignoramos el conflicto
                await result.CancelAndDiscardItemAsync();
            }
            else
            {
                // Para nosotros, gana el cliente
                localAviso.AzureVersion = serverAviso.AzureVersion;
                await result.UpdateOperationAsync(JObject.FromObject(localAviso));
            }
        }
Exemplo n.º 5
0
        async Task ResolveConflictAsync <T>(MobileServiceTableOperationError error, bool overrideServerChanges) where T : TableData
        {
            var serverItem = error.Result.ToObject <T>();
            var localItem  = error.Item.ToObject <T>();

            // Note that you need to implement the public override Equals(T) method in the Model for this to work
            //if (serverItem.Equals(localItem))
            //{
            //    // Items are the same, so ignore the conflict
            //    await error.CancelAndDiscardItemAsync();
            //    return;
            //}

            if (overrideServerChanges)
            {
                // Client wins
                localItem.Version = serverItem.Version;
                await error.UpdateOperationAsync(JObject.FromObject(localItem));
            }
            else
            {
                // Server wins
                await error.CancelAndDiscardItemAsync();
            }
        }
        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);
        }
        private async Task ConflictOperation_WithNotificationsEnabled_UsesTheCorrectStoreOperationSource(Func <MobileServiceSyncContext, MobileServiceTableOperationError, Task> handler)
        {
            var client           = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp);
            var store            = new MobileServiceLocalStoreMock();
            var context          = new MobileServiceSyncContext(client);
            var manualResetEvent = new ManualResetEventSlim();

            await context.InitializeAsync(store, StoreTrackingOptions.NotifyLocalConflictResolutionOperations);

            string operationId = "abc";
            string itemId      = string.Empty;
            string tableName   = "test";

            store.TableMap[MobileServiceLocalSystemTables.OperationQueue].Add(operationId, new JObject()
            {
                { "version", 1 }
            });


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


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

            await handler(context, error);

            bool resetEventSignaled = manualResetEvent.Wait(1000);

            subscription.Dispose();

            Assert.IsTrue(resetEventSignaled);
            Assert.IsTrue(sourceIsLocalConflict);
        }
        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));
        }
Exemplo n.º 10
0
        public async Task ResolveConflictAsync(MobileServiceTableOperationError error)
        {
            var serverItem = error.Result.ToObject <MedicineHistory>();
            var localItem  = error.Item.ToObject <MedicineHistory>();

            // Note that you need to implement the public override Equals(TodoItem item)
            // method in the Model for this to work
            // Client Always Wins
            //localItem.Version = serverItem.Version;
            await error.UpdateOperationAsync(JObject.FromObject(localItem));

            Debug.WriteLine("update here");

            // Server Always Wins
            // await error.CancelAndDiscardItemAsync();
        }
Exemplo n.º 11
0
        public async Task PushAsync_DiscardsOperationAndDeletesTheItem_WhenCancelAndDiscardItemAsync()
        {
            ResetDatabase(TestTable);

            var    hijack         = new TestHttpHandler();
            string conflictResult = "{\"id\":\"b\",\"String\":\"Wow\",\"version\":\"def\"}";

            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.PreconditionFailed)
            {
                Content = new StringContent(conflictResult)
            });                                                                                                                               // first push

            var store = new MobileServiceSQLiteStore(TestDbName);

            store.DefineTable <ToDoWithSystemPropertiesType>();

            IMobileServiceClient service = await CreateClient(hijack, store);

            IMobileServiceSyncTable <ToDoWithSystemPropertiesType> table = service.GetSyncTable <ToDoWithSystemPropertiesType>();

            // first insert an item
            var updatedItem = new ToDoWithSystemPropertiesType()
            {
                Id = "b", String = "Hey", Version = "abc"
            };
            await table.UpdateAsync(updatedItem);

            // then push it to server
            var ex = await Assert.ThrowsAsync <MobileServicePushFailedException>(service.SyncContext.PushAsync);

            Assert.NotNull(ex.PushResult);
            MobileServiceTableOperationError error = ex.PushResult.Errors.FirstOrDefault();

            Assert.NotNull(error);

            Assert.Equal(1L, service.SyncContext.PendingOperations); // operation is not removed
            updatedItem = await table.LookupAsync("b");

            Assert.Equal("Hey", updatedItem.String); // item is not updated

            await error.CancelAndDiscardItemAsync();

            Assert.Equal(0L, service.SyncContext.PendingOperations); // operation is removed
            updatedItem = await table.LookupAsync("b");

            Assert.Null(updatedItem); // item is deleted
        }
        Task ResolveConflictAsync(MobileServiceTableOperationError error)
        {
            var serverItem = error.Result.ToObject <CircleMessage>();
            var localItem  = error.Item.ToObject <CircleMessage>();

            if (serverItem.Equals(localItem))
            {
                // Items are identical, ignore the conflict; server wins.
                return(error.CancelAndDiscardItemAsync());
            }
            else
            {
                // otherwise, the client wins.
                localItem.Version = serverItem.Version;
                return(error.UpdateOperationAsync(JObject.FromObject(localItem)));
            }
        }
 private async Task ResolveConflictAsync(MobileServiceTableOperationError error)
 {
     //var serverItem = error.Result.ToObject<TModel>();
     //var localItem = error.Item.ToObject<TModel>();
     //// Note that you need to implement the public override Equals(TModel item)
     //// method in the Model for this to work
     //if (serverItem.Equals(localItem))
     //{
     //    // Items are the same, so ignore the conflict
     //    await error.CancelAndDiscardItemAsync();
     //    return;
     //}
     //// Client Always Wins
     //localItem.Version = serverItem.Version;
     //await error.UpdateOperationAsync(JObject.FromObject(localItem));
     // Server Always Wins
     //await error.CancelAndDiscardItemAsync();
 }
Exemplo n.º 14
0
        private async Task ResolveErrorAsync(MobileServiceTableOperationError result)
        {
            // Ignoramos si no podemos validar ambas partes.
            if (result.Result == null || result.Item == null)
            {
                return;
            }

            var serverItem = result.Result.ToObject <TodoItem>();
            var localItem  = result.Item.ToObject <TodoItem>();

            //if (!IsDataChanged(serverItem, localItem))
            // Los elementos son iguales, ignoramos el conflicto
            //    await result.CancelAndDiscardItemAsync();
            //else
            // El Servidor manda.
            await result.UpdateOperationAsync(JObject.FromObject(serverItem));
        }
Exemplo n.º 15
0
        private async Task ResolveConflictAsync(MobileServiceTableOperationError error)
        {
            Debug.WriteLine($"Resolve Conflict for Item: {error.Item}");

            var serverItem = error.Result.ToObject <T>();
            var localItem  = error.Item.ToObject <T>();

            if (serverItem.Equals(localItem))
            {
                // Items are the same, so ignore the conflict
                await error.CancelAndDiscardItemAsync();
            }
            else
            {
                // Always take the client
                localItem.Version = serverItem.Version;
                await error.UpdateOperationAsync(JObject.FromObject(localItem));
            }
        }
        public async Task PushAsync_ReplaysStoredErrors_IfTheyAreInStore()
        {
            var error = new MobileServiceTableOperationError("abc",
                                                             1,
                                                             MobileServiceTableOperationKind.Update,
                                                             HttpStatusCode.PreconditionFailed,
                                                             "test",
                                                             new JObject(),
                                                             "{}",
                                                             new JObject());
            var store = new MobileServiceLocalStoreMock();
            await store.UpsertAsync(MobileServiceLocalSystemTables.SyncErrors, error.Serialize(), fromServer : false);

            var hijack = new TestHttpHandler();
            IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack);
            await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());

            var ex = await ThrowsAsync <MobileServicePushFailedException>(service.SyncContext.PushAsync);
        }
        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);
        }
Exemplo n.º 18
0
        private async Task ResolveError(MobileServiceTableOperationError result)
        {
            // Ignore if we can't see both sides.
            if (result.Result == null || result.Item == null)
            {
                return;
            }

            var serverItem = result.Result.ToObject <Receipt>();
            var localItem  = result.Item.ToObject <Receipt>();

            if (serverItem.titulo == localItem.titulo)
            {
                // Items are the same, so ignore the conflict
                await result.CancelAndDiscardItemAsync();
            }
            else
            {
                // Always take the client
                localItem.version = serverItem.version;
                await result.UpdateOperationAsync(JObject.FromObject(localItem));
            }
        }
        public void Deserialize_Succeeds_WhenOperationVersionIsNull()
        {
            var serializedError = JObject.Parse(@"
            {""id"":""70cf6cc2-5981-4a32-ae6c-249572917a46"",
            ""httpStatus"": 200,
            ""operationVersion"":null,
            ""operationKind"":0,
            ""tableName"":""test"",
            ""tableKind"":1,
            ""item"":""{\""id\"":\""abc\"",\""text\"":\""example\""}"",
            ""rawResult"":""{\""id\"":\""abc\"",\""text\"":\""example\""}""
            }");
            var operation       = MobileServiceTableOperationError.Deserialize(serializedError, this.serializer.SerializerSettings);

            Assert.Equal(serializedError["id"], operation.Id);
            Assert.Equal(0, operation.OperationVersion);
            Assert.Equal(serializedError["operationKind"], (int)operation.OperationKind);
            Assert.Equal(serializedError["httpStatus"], (int)operation.Status);
            Assert.Equal(serializedError["tableName"], operation.TableName);
            Assert.Equal(serializedError["tableKind"], (int)operation.TableKind);
            Assert.Equal(serializedError["item"], operation.Item.ToString(Formatting.None));
            Assert.Equal(serializedError["rawResult"], operation.RawResult);
        }
        private async Task ResolveConflictAsync(MobileServiceTableOperationError error)
        {
            //error.Result è il valore presente nel backend
            if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
            {
                //error.Result -> Il record sul server che ha dato l'errore in formato oggetto Json
                //error.Item -> Il recod locale che ha dato provocato l'errore in formato oggetto Json


                //Vince il server !!!
                //Modifico la tupla locale con quanto ricevuto dal server
                await error.CancelAndUpdateItemAsync(error.Result);


                //Riforzo la tupla locale con i suoi stessi valori e le modifiche verranno riproposte al backend
                //await error.UpdateOperationAsync(error.Item);
            }
            else
            {
                //In alcuni casi (per esempio per proxy interposti o altri casi sfortunati) error.result è null
                await error.CancelAndDiscardItemAsync();
            }
        }
Exemplo n.º 21
0
        /// <summary>
        ///     This method is used to resolve any conflicts that occur. This can happen
        ///     if two clients update the same record and then try to push their changes.
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        private async Task ResolveErrorAsync(MobileServiceTableOperationError result)
        {
            // Ignore if we can't see both sides.
            if ((result.Result == null) || (result.Item == null))
            {
                return;
            }

            var serverItem = result.Result.ToObject <DiaryEntry>();
            var localItem  = result.Item.ToObject <DiaryEntry>();

            if ((serverItem.Title == localItem.Title) &&
                (serverItem.Text == localItem.Text))
            {
                // Items are the same, so ignore the conflict
                await result.CancelAndDiscardItemAsync();
            }
            else
            {
                // Always take the client
                localItem.AzureVersion = serverItem.AzureVersion;
                await result.UpdateOperationAsync(JObject.FromObject(localItem));
            }
        }
Exemplo n.º 22
0
        private async Task ResolveErrorAsync(MobileServiceTableOperationError result)
        {
            // Ignoramos si no podemos validar ambas partes.
            if (result.Result == null || result.Item == null)
            {
                return;
            }

            var serverItem = result.Result.ToObject <Event>();
            var localItem  = result.Item.ToObject <Event>();

            if (serverItem.Name == localItem.Name &&
                serverItem.Id == localItem.Id)
            {
                // Los elementos son iguales, ignoramos el conflicto
                await result.CancelAndDiscardItemAsync();
            }
            else
            {
                // El cliente manda.
                localItem.AzureVersion = serverItem.AzureVersion;
                await result.UpdateOperationAsync(JObject.FromObject(localItem));
            }
        }
Exemplo n.º 23
0
        public async Task PushAsync_RetriesOperation_WhenConflictOccursInLastPush()
        {
            ResetDatabase(TestTable);

            var    hijack         = new TestHttpHandler();
            string conflictResult = "{\"id\":\"b\",\"String\":\"Hey\",\"version\":\"def\"}";

            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.PreconditionFailed)
            {
                Content = new StringContent(conflictResult)
            });                                                                                                                               // first push
            string successResult = "{\"id\":\"b\",\"String\":\"Wow\",\"version\":\"def\"}";

            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(successResult)
            });                                                                                                              // second push

            var store = new MobileServiceSQLiteStore(TestDbName);

            store.DefineTable <ToDoWithSystemPropertiesType>();

            IMobileServiceClient service = await CreateClient(hijack, store);

            IMobileServiceSyncTable <ToDoWithSystemPropertiesType> table = service.GetSyncTable <ToDoWithSystemPropertiesType>();

            // first insert an item
            var updatedItem = new ToDoWithSystemPropertiesType()
            {
                Id = "b", String = "Hey", Version = "abc"
            };
            await table.UpdateAsync(updatedItem);

            // then push it to server
            var ex = await Assert.ThrowsAsync <MobileServicePushFailedException>(service.SyncContext.PushAsync);

            Assert.NotNull(ex.PushResult);
            Assert.Equal(MobileServicePushStatus.Complete, ex.PushResult.Status);
            Assert.Single(ex.PushResult.Errors);
            MobileServiceTableOperationError error = ex.PushResult.Errors.FirstOrDefault();

            Assert.NotNull(error);
            Assert.False(error.Handled);
            Assert.Equal(MobileServiceTableOperationKind.Update, error.OperationKind);
            Assert.Equal(error.RawResult, conflictResult);
            Assert.Equal(error.TableName, TestTable);
            Assert.Equal(HttpStatusCode.PreconditionFailed, error.Status);

            var errorItem = error.Item.ToObject <ToDoWithSystemPropertiesType>(JsonSerializer.Create(service.SerializerSettings));

            Assert.Equal(errorItem.Id, updatedItem.Id);
            Assert.Equal(errorItem.String, updatedItem.String);
            Assert.Equal(errorItem.Version, updatedItem.Version);
            Assert.Equal(errorItem.CreatedAt, updatedItem.CreatedAt);
            Assert.Equal(errorItem.UpdatedAt, updatedItem.UpdatedAt);

            Assert.Equal(error.Result.ToString(Formatting.None), conflictResult);

            Assert.Equal(1L, service.SyncContext.PendingOperations); // operation not removed
            updatedItem = await table.LookupAsync("b");

            Assert.Equal("Hey", updatedItem.String); // item is not updated

            await service.SyncContext.PushAsync();

            Assert.Equal(0L, service.SyncContext.PendingOperations); // operation now succeeds

            updatedItem = await table.LookupAsync("b");

            Assert.Equal("Wow", updatedItem.String); // item is updated
        }
        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);
        }