public async Task PushAsync_FeatureHeaderPresentWhenRehydrated() { var hijack = new TestHttpHandler(); IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); var store = new MobileServiceLocalStoreMock(); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); JObject item1 = new JObject() { { "id", "abc" } }; await table.InsertAsync(item1); // create a new service to test that operations are loaded from store hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests[0].Headers.GetValues("X-ZUMO-FEATURES").First(), "TU,OL"); }
public async Task PullAsync_Cancels_WhenCancellationTokenIsCancelled() { var store = new MobileServiceLocalStoreMock(); var handler = new MobileServiceSyncHandlerMock(); handler.TableOperationAction = op => Task.Delay(TimeSpan.MaxValue).ContinueWith<JObject>(t => null); // long slow operation var hijack = new TestHttpHandler(); hijack.OnSendingRequest = async req => { await Task.Delay(1000); return req; }; IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, handler); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); using (var cts = new CancellationTokenSource()) { Task pullTask = table.PullAsync(null, null, null, cancellationToken: cts.Token); cts.Cancel(); var ex = await ThrowsAsync<Exception>(() => pullTask); Assert.IsTrue((ex is OperationCanceledException || ex is TaskCanceledException)); Assert.AreEqual(pullTask.Status, TaskStatus.Canceled); } }
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 PushAsync_ExecutesThePendingOperations_InOrder() { var hijack = new TestHttpHandler(); IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); var store = new MobileServiceLocalStoreMock(); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); JObject item1 = new JObject() { { "id", "abc" } }, item2 = new JObject() { { "id", "def" } }; await table.InsertAsync(item1); await table.InsertAsync(item2); Assert.AreEqual(hijack.Requests.Count, 0); // create a new service to test that operations are loaded from store hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); hijack.AddResponseContent("{\"id\":\"def\",\"String\":\"What\"}"); service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); Assert.AreEqual(hijack.Requests.Count, 0); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests.Count, 2); Assert.AreEqual(hijack.RequestContents[0], item1.ToString(Formatting.None)); Assert.AreEqual(hijack.Requests[0].Headers.GetValues("X-ZUMO-FEATURES").First(), "TU,OL"); Assert.AreEqual(hijack.RequestContents[1], item2.ToString(Formatting.None)); Assert.AreEqual(hijack.Requests[1].Headers.GetValues("X-ZUMO-FEATURES").First(), "TU,OL"); // create yet another service to make sure the old items were purged from queue hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); Assert.AreEqual(hijack.Requests.Count, 0); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests.Count, 0); }
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 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)); }
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); }
public async Task PushAsync_FeatureHeaderPresent() { var hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); var store = new MobileServiceLocalStoreMock(); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); JObject item1 = new JObject() { { "id", "abc" } }; await table.InsertAsync(item1); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests[0].Headers.GetValues("X-ZUMO-FEATURES").First(), "TU,OL"); }
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 PullAsync_Cancels_WhenCancellationTokenIsCancelled() { var store = new MobileServiceLocalStoreMock(); var handler = new MobileServiceSyncHandlerMock(); handler.TableOperationAction = op => Task.Delay(TimeSpan.MaxValue).ContinueWith<JObject>(t => null); // long slow operation IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret..."); await service.SyncContext.InitializeAsync(store, handler); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); using (var cts = new CancellationTokenSource()) { // now pull Task pullTask = table.PullAsync(null, null, null, cancellationToken: cts.Token); cts.Cancel(); var ex = await ThrowsAsync<Exception>(() => pullTask); Assert.IsTrue((ex is OperationCanceledException || ex is TaskCanceledException)); Assert.AreEqual(pullTask.Status, TaskStatus.Canceled); } }
public async Task PushAsync_ExecutesThePendingOperations_InOrder_BatchedOne() { var hijack = new TestHttpHandler(); IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); var store = new MobileServiceLocalStoreMock(); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); JObject item1 = new JObject() { { "id", "abc" } }, item2 = new JObject() { { "id", "def" } }; await table.InsertAsync(item1); await table.InsertAsync(item2); Assert.AreEqual(hijack.Requests.Count, 0); // create a new service to test that operations are loaded from store hijack = new TestHttpHandler(); var content = new MultipartContent("mixed", "6f078995-ef2a-4617-a4c9-5d8746b26d32"); var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{\"id\":\"abc\",\"String\":\"Hey\"}", Encoding.UTF8, "application/json") }; content.Add(new HttpMessageContent(response)); hijack.AddResponseContent(content); content = new MultipartContent("mixed", "6f078995-ef2a-4617-a4c9-5d8746b26d32"); response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{\"id\":\"def\",\"String\":\"What\"}", Encoding.UTF8, "application/json") }; content.Add(new HttpMessageContent(response)); hijack.AddResponseContent(content); service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); service.SyncContext.BatchApiEndpoint = "batch"; service.SyncContext.BatchSize = 1; Assert.AreEqual(hijack.Requests.Count, 0); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests.Count, 2); Assert.IsTrue(hijack.RequestContents[0].Contains(item1.ToString(Formatting.None))); Assert.IsTrue(hijack.RequestContents[0].Contains("X-ZUMO-FEATURES: TU,OL")); Assert.IsTrue(hijack.RequestContents[1].Contains(item2.ToString(Formatting.None))); Assert.IsTrue(hijack.RequestContents[1].Contains("X-ZUMO-FEATURES: TU,OL")); // create yet another service to make sure the old items were purged from queue hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); Assert.AreEqual(hijack.Requests.Count, 0); await service.SyncContext.PushAsync(); Assert.AreEqual(hijack.Requests.Count, 0); }
public async Task PullAsync_Supports_AbsoluteAndRelativeUri() { var data = new string[] { "http://www.test.com/api/todoitem", "/api/todoitem" }; foreach (string uri in data) { var hijack = new TestHttpHandler(); hijack.AddResponseContent("[{\"id\":\"abc\",\"String\":\"Hey\"},{\"id\":\"def\",\"String\":\"How\"}]"); // first page hijack.AddResponseContent("[]"); var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); await table.PullAsync(null, uri); Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 2); AssertEx.MatchUris(hijack.Requests, "http://www.test.com/api/todoitem?$skip=0&$top=50&__includeDeleted=true&__systemproperties=__version%2C__deleted", "http://www.test.com/api/todoitem?$skip=2&$top=50&__includeDeleted=true&__systemproperties=__version%2C__deleted"); Assert.AreEqual("QS,OL", hijack.Requests[0].Headers.GetValues("X-ZUMO-FEATURES").First()); Assert.AreEqual("QS,OL", hijack.Requests[1].Headers.GetValues("X-ZUMO-FEATURES").First()); } }
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)); }
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 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); }
public async Task PullAsync_Incremental_MovesByUpdatedAt_ThenUsesSkipAndTop_WhenUpdatedAtDoesNotChange() { var hijack = new TestHttpHandler(); hijack.OnSendingRequest = req => { return Task.FromResult(req); }; hijack.AddResponseContent(@"[{""id"":""abc"",""String"":""Hey"", ""__updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""}]"); hijack.AddResponseContent(@"[{""id"":""def"",""String"":""World"", ""__updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""}]"); hijack.AddResponseContent("[]"); // last page var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); await table.PullAsync("items", table.CreateQuery()); AssertEx.MatchUris(hijack.Requests, "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'1970-01-01T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'2001-02-03T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'2001-02-03T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=1&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted"); }
public async Task PullAsync_DoesNotPurge_WhenItemIsMissing() { var hijack = new TestHttpHandler(); hijack.AddResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); // for insert hijack.AddResponseContent("[{\"id\":\"def\",\"String\":\"World\"}]"); // remote item hijack.AddResponseContent("[]"); // last page var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable table = service.GetSyncTable("someTable"); await table.InsertAsync(new JObject() { { "id", "abc" } }); // insert an item await service.SyncContext.PushAsync(); // push to clear the queue // now pull await table.PullAsync(null, null); Assert.AreEqual(store.TableMap[table.TableName].Count, 2); // 1 from remote and 1 from local Assert.AreEqual(hijack.Requests.Count, 3); // one for push and 2 for pull }
public async Task PullAsync_Throws_WhenPushThrows() { var hijack = new TestHttpHandler(); hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.NotFound)); // for push var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); // insert an item but don't push IMobileServiceSyncTable table = service.GetSyncTable("someTable"); await table.InsertAsync(new JObject() { { "id", "abc" } }); Assert.AreEqual(store.TableMap[table.TableName].Count, 1); // item is inserted // this should trigger a push var ex = await ThrowsAsync<MobileServicePushFailedException>(() => table.PullAsync(null, null)); Assert.AreEqual(ex.PushResult.Errors.Count(), 1); Assert.AreEqual(hijack.Requests.Count, 1); // 1 for push }
public async Task PullAsync_Succeeds() { var hijack = new TestHttpHandler(); hijack.OnSendingRequest = req => { return Task.FromResult(req); }; hijack.AddResponseContent("[{\"id\":\"abc\",\"String\":\"Hey\"},{\"id\":\"def\",\"String\":\"World\"}]"); // for pull hijack.AddResponseContent("[]"); // last page var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); var query = table.Skip(5) .Take(3) .Where(t => t.String == "world") .OrderBy(o => o.Id) .WithParameters(new Dictionary<string, string>() { { "param1", "val1" } }) .OrderByDescending(o => o.String) .IncludeTotalCount(); await table.PullAsync(null, query, cancellationToken: CancellationToken.None); Assert.AreEqual(hijack.Requests.Count, 2); AssertEx.QueryEquals(hijack.Requests[0].RequestUri.Query, "?$filter=(String%20eq%20'world')&$orderby=String%20desc,id&$skip=5&$top=3¶m1=val1&__includeDeleted=true&__systemproperties=__version%2C__deleted"); }
public async Task PullAsync_DoesNotFollowLink_IfResultIsEmpty() { var hijack = new TestHttpHandler(); hijack.AddResponseContent("[]"); // first page hijack.Responses[0].Headers.Add("Link", "http://localhost:31475/tables/Green?$top=1&$select=Text%2CDone%2CId&$skip=2; rel=next"); hijack.AddResponseContent("[{\"id\":\"ghi\",\"String\":\"Are\"},{\"id\":\"jkl\",\"String\":\"You\"}]"); // second page var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); await table.PullAsync(null, table.Take(1)); Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); }
public async Task PullAsync_DefaultsTo50_IfGreaterThan50() { var hijack = new TestHttpHandler(); hijack.AddResponseContent("[{\"id\":\"abc\",\"String\":\"Hey\"},{\"id\":\"def\",\"String\":\"How\"}]"); // first page hijack.AddResponseContent("[]"); // end of the list var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); await table.PullAsync(null, table.Take(51)); Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 2); AssertEx.MatchUris(hijack.Requests, "http://www.test.com/tables/stringId_test_table?$skip=0&$top=50&__includeDeleted=true&__systemproperties=__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$skip=2&$top=49&__includeDeleted=true&__systemproperties=__version%2C__deleted"); }
public async Task PullAsync_DoesNotFollowLink_IfLinkHasNonSupportedOptions() { var hijack = new TestHttpHandler(); hijack.AddResponseContent("[{\"id\":\"abc\",\"String\":\"Hey\"},{\"id\":\"def\",\"String\":\"How\"}]"); // first page hijack.Responses[0].Headers.Add("Link", "http://contoso.com:31475/tables/Green?$top=1&$select=Text%2CDone%2CId&$skip=2; rel=next"); hijack.AddResponseContent("[{\"id\":\"ghi\",\"String\":\"Are\"},{\"id\":\"jkl\",\"String\":\"You\"}]"); // second page hijack.AddResponseContent("[]"); // end of the list var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); table.SupportedOptions &= ~MobileServiceRemoteTableOptions.Skip; Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); await table.PullAsync(null, null); Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 2); AssertEx.MatchUris(hijack.Requests, "http://www.test.com/tables/stringId_test_table?$top=50&__includeDeleted=true&__systemproperties=__version%2C__deleted"); }
public async Task PurgeAsync_Throws_WhenThereIsOperationInTable_AndForceIsTrue_AndQueryIsSpecified() { var hijack = new TestHttpHandler(); hijack.SetResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); // insert an item but don't push IMobileServiceSyncTable table = service.GetSyncTable("someTable"); await table.InsertAsync(new JObject() { { "id", "abc" } }); Assert.AreEqual(store.TableMap[table.TableName].Count, 1); // item is inserted // this should trigger a push var ex = await ThrowsAsync<InvalidOperationException>(() => table.PurgeAsync(null, "$filter=a eq b", true, CancellationToken.None)); Assert.AreEqual(ex.Message, "The table cannot be purged because it has pending operations."); Assert.AreEqual(service.SyncContext.PendingOperations, 1L); // operation still in queue }
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); }
public async Task PurgeAsync_DeletesOperations_WhenThereIsOperationInTable_AndForceIsTrue() { var hijack = new TestHttpHandler(); hijack.SetResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); // put a dummy delta token string deltaKey = "deltaToken|someTable|abc"; store.TableMap[MobileServiceLocalSystemTables.Config] = new Dictionary<string, JObject>() { { deltaKey, new JObject() } }; // insert an item but don't push IMobileServiceSyncTable table = service.GetSyncTable("someTable"); await table.InsertAsync(new JObject() { { "id", "abc" } }); Assert.AreEqual(store.TableMap[table.TableName].Count, 1); // item is inserted Assert.AreEqual(service.SyncContext.PendingOperations, 1L); await table.PurgeAsync("abc", null, force: true, cancellationToken: CancellationToken.None); Assert.AreEqual(store.TableMap[table.TableName].Count, 0); // item is deleted Assert.AreEqual(service.SyncContext.PendingOperations, 0L); // operation is also removed // deleted delta token Assert.IsFalse(store.TableMap[MobileServiceLocalSystemTables.Config].ContainsKey(deltaKey)); }
private static async Task TestPullAsyncIncrementalWithOptions(MobileServiceRemoteTableOptions options, params string[] uris) { var store = new MobileServiceLocalStoreMock(); var settings = new MobileServiceSyncSettingsManager(store); await settings.SetDeltaTokenAsync("stringId_test_table", "incquery", new DateTime(2001, 02, 01, 0, 0, 0, DateTimeKind.Utc)); await TestIncrementalPull(store, options, uris); }
public async Task PullAsync_Incremental_WithoutDeltaTokenInDb() { var store = new MobileServiceLocalStoreMock(); store.TableMap[MobileServiceLocalSystemTables.Config] = new Dictionary<string, JObject>(); await TestIncrementalPull(store, MobileServiceRemoteTableOptions.All, "http://test.com/tables/stringId_test_table?$filter=((String eq 'world') and (__updatedAt ge datetimeoffset'1970-01-01T00:00:00.0000000%2B00:00'))&$orderby=__updatedAt&$skip=0&$top=50¶m1=val1&__includeDeleted=true&__systemproperties=__createdAt%2C__updatedAt%2C__deleted", "http://test.com/tables/stringId_test_table?$filter=((String eq 'world') and (__updatedAt ge datetimeoffset'2001-02-03T00:00:00.0000000%2B00:00'))&$orderby=__updatedAt&$skip=0&$top=50¶m1=val1&__includeDeleted=true&__systemproperties=__createdAt%2C__updatedAt%2C__deleted"); }
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 static async Task TestIncrementalPull(MobileServiceLocalStoreMock store, MobileServiceRemoteTableOptions options, params string[] expectedUris) { var hijack = new TestHttpHandler(); hijack.AddResponseContent(@"[{""id"":""abc"",""String"":""Hey"", ""__updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""}, {""id"":""def"",""String"":""World"", ""__updatedAt"": ""2001-02-03T00:03:00.0000000+07:00""}]"); // for pull hijack.AddResponseContent(@"[]"); store.TableMap[MobileServiceLocalSystemTables.Config]["systemProperties|stringId_test_table"] = new JObject { { MobileServiceSystemColumns.Id, "systemProperties|stringId_test_table" }, { "value", "1" } }; IMobileServiceClient service = new MobileServiceClient("http://test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); table.SupportedOptions = options; var query = table.Where(t => t.String == "world") .WithParameters(new Dictionary<string, string>() { { "param1", "val1" } }) .IncludeTotalCount(); await table.PullAsync("incquery", query, cancellationToken: CancellationToken.None); AssertEx.MatchUris(hijack.Requests, expectedUris); }
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); }
private async Task TestPullQueryOverrideThrows(IDictionary<string, string> parameters, string errorMessage) { var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret..."); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); var query = table.CreateQuery() .WithParameters(parameters); var ex = await ThrowsAsync<ArgumentException>(() => table.PullAsync(null, query, cancellationToken: CancellationToken.None)); Assert.AreEqual(errorMessage, ex.Message); }
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 PurgeAsync_DoesNotTriggerPush_WhenThereIsNoOperationInTable() { var hijack = new TestHttpHandler(); hijack.SetResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}"); var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); // insert item in purge table IMobileServiceSyncTable table1 = service.GetSyncTable("someTable"); await table1.InsertAsync(new JObject() { { "id", "abc" } }); // but push to clear the queue await service.SyncContext.PushAsync(); Assert.AreEqual(store.TableMap[table1.TableName].Count, 1); // item is inserted Assert.AreEqual(hijack.Requests.Count, 1); // first push // then insert item in other table IMobileServiceSyncTable<StringIdType> table2 = service.GetSyncTable<StringIdType>(); var item = new StringIdType() { Id = "an id", String = "what?" }; await table2.InsertAsync(item); // try purge on first table now await table1.PurgeAsync(); Assert.AreEqual(store.DeleteQueries[0].TableName, MobileServiceLocalSystemTables.SyncErrors); // push deletes all sync erros Assert.AreEqual(store.DeleteQueries[1].TableName, table1.TableName); // purged table Assert.AreEqual(hijack.Requests.Count, 1); // still 1 means no other push happened Assert.AreEqual(store.TableMap[table2.TableName].Count, 1); // this table should not be touched }
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); }
public async Task PurgeAsync_ResetsDeltaToken_WhenQueryIdIsSpecified() { var hijack = new TestHttpHandler(); hijack.AddResponseContent(@"[{""id"":""abc"",""String"":""Hey"", ""__updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""}, {""id"":""def"",""String"":""How"", ""__updatedAt"": ""2001-02-04T00:00:00.0000000+00:00""}]"); // first page hijack.AddResponseContent("[]"); // last page of first pull hijack.AddResponseContent("[]"); // second pull var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); // ensure there is no delta token present already Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); // now pull down data await table.PullAsync("items", table.CreateQuery()); // ensure items were pulled down Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 2); Assert.AreEqual(store.TableMap["stringId_test_table"]["abc"].Value<string>("String"), "Hey"); Assert.AreEqual(store.TableMap["stringId_test_table"]["def"].Value<string>("String"), "How"); // ensure delta token was updated Assert.Equals(store.TableMap[MobileServiceLocalSystemTables.Config]["deltaToken|stringId_test_table|items"]["value"], "2001-02-04T00:00:00.0000000+00:00"); // now purge and forget the delta token await table.PurgeAsync("items", null, false, CancellationToken.None); // make sure data is purged Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 0); // make sure delta token is removed Assert.IsFalse(store.TableMap[MobileServiceLocalSystemTables.Config].ContainsKey("deltaToken|stringId_test_table|items")); // pull again await table.PullAsync("items", table.CreateQuery()); // verify request urls AssertEx.MatchUris(hijack.Requests, "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'1970-01-01T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'2001-02-04T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$filter=(__updatedAt ge datetimeoffset'1970-01-01T00:00:00.0000000%2B00:00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__updatedAt%2C__version%2C__deleted"); }
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 PullAsync_UsesSkipAndTakeThenFollowsLinkThenUsesSkipAndTake() { var hijack = new TestHttpHandler(); // first page hijack.AddResponseContent("[{\"id\":\"abc\",\"String\":\"Hey\"},{\"id\":\"def\",\"String\":\"How\"}]"); // second page with a link hijack.AddResponseContent("[{\"id\":\"ghi\",\"String\":\"Are\"},{\"id\":\"jkl\",\"String\":\"You\"}]"); hijack.Responses[1].Headers.Add("Link", "http://localhost:31475/tables/Green?$top=1&$select=Text%2CDone%2CId&$skip=2; rel=next"); // forth page without link hijack.AddResponseContent("[{\"id\":\"mno\",\"String\":\"Mr\"},{\"id\":\"pqr\",\"String\":\"X\"}]"); // last page hijack.AddResponseContent("[]"); var store = new MobileServiceLocalStoreMock(); IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack); await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler()); IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>(); Assert.IsFalse(store.TableMap.ContainsKey("stringId_test_table")); await table.PullAsync(null, table.Take(51).Skip(3)); Assert.AreEqual(store.TableMap["stringId_test_table"].Count, 6); Assert.AreEqual(store.TableMap["stringId_test_table"]["abc"].Value<string>("String"), "Hey"); Assert.AreEqual(store.TableMap["stringId_test_table"]["def"].Value<string>("String"), "How"); Assert.AreEqual(store.TableMap["stringId_test_table"]["ghi"].Value<string>("String"), "Are"); Assert.AreEqual(store.TableMap["stringId_test_table"]["jkl"].Value<string>("String"), "You"); Assert.AreEqual(store.TableMap["stringId_test_table"]["mno"].Value<string>("String"), "Mr"); Assert.AreEqual(store.TableMap["stringId_test_table"]["pqr"].Value<string>("String"), "X"); AssertEx.MatchUris(hijack.Requests, "http://www.test.com/tables/stringId_test_table?$skip=3&$top=50&__includeDeleted=true&__systemproperties=__version%2C__deleted", "http://www.test.com/tables/stringId_test_table?$skip=5&$top=49&__includeDeleted=true&__systemproperties=__version%2C__deleted", "http://localhost:31475/tables/Green?$top=1&$select=Text%2CDone%2CId&$skip=2", "http://www.test.com/tables/stringId_test_table?$skip=9&$top=45&__includeDeleted=true&__systemproperties=__version%2C__deleted"); }