private async Task TestIncrementalSync(MobileServiceTableQueryDescription query, JArray result, DateTime maxUpdatedAt, bool savesMax, string firstQuery, string secondQuery)
        {
            var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, "latestItems", query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, null, CancellationToken.None);

            this.opQueue.Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult <IDisposable>(null));
            this.opQueue.Setup(q => q.CountPending(It.IsAny <string>())).Returns(Task.FromResult(0L));
            this.table.Setup(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)));

            if (result.Any())
            {
                this.table.Setup(t => t.ReadAsync(secondQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
                .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            }

            if (result.Any())
            {
                this.store.Setup(s => s.UpsertAsync("test", It.IsAny <IEnumerable <JObject> >(), true)).Returns(Task.FromResult(0));
            }

            this.settings.Setup(s => s.GetDeltaTokenAsync("test", "latestItems")).Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)));
            if (savesMax)
            {
                this.settings.Setup(s => s.SetDeltaTokenAsync("test", "latestItems", maxUpdatedAt)).Returns(Task.FromResult(0));
            }

            await action.ExecuteAsync();

            this.store.VerifyAll();
            this.opQueue.VerifyAll();
            this.table.VerifyAll();
            this.settings.VerifyAll();

            store.Verify(s => s.DeleteAsync("test", It.IsAny <IEnumerable <string> >()), Times.Never(), "There shouldn't be any call to delete");
        }
        public async Task DoesNotUpsertAnObject_IfItDoesNotHaveAnId()
        {
            var query = new MobileServiceTableQueryDescription("test");
            var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, null, query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, pullOptions: null, reader: null, cancellationToken: CancellationToken.None);

            var itemWithId = new JObject() { { "id", "abc" }, { "text", "has id" } };
            var itemWithoutId = new JObject() { { "text", "no id" } };
            var result = new JArray(new[]{
                itemWithId,
                itemWithoutId
            });
            this.opQueue.Setup(q => q.LockTableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<IDisposable>(null));
            this.opQueue.Setup(q => q.CountPending(It.IsAny<string>())).Returns(Task.FromResult(0L));
            this.table.SetupSequence(t => t.ReadAsync(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<MobileServiceFeatures>()))
                      .Returns(Task.FromResult(QueryResult.Parse(result, null, false)))
                      .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            this.store.Setup(s => s.UpsertAsync("test", It.IsAny<IEnumerable<JObject>>(), true))
                      .Returns(Task.FromResult(0))
                      .Callback<string, IEnumerable<JObject>, bool>((tableName, items, fromServer) =>
                        {
                            Assert.AreEqual(1, items.Count());
                            Assert.AreEqual(itemWithId, items.First());
                        });

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();

            store.Verify(s => s.DeleteAsync("test", It.IsAny<IEnumerable<string>>()), Times.Never(), "There shouldn't be any call to delete");
        }
        public async Task DoesNotSaveTheMaxUpdatedAt_IfThereAreNoResults()
        {
            string testName  = "test";
            string queryName = "latestItems";

            var store    = new Mock <IMobileServiceLocalStore>(MockBehavior.Strict);
            var settings = new Mock <MobileServiceSyncSettingsManager>(MockBehavior.Strict);
            var opQueue  = new Mock <OperationQueue>(MockBehavior.Strict, store.Object);
            var handler  = new Mock <IMobileServiceSyncHandler>(MockBehavior.Strict);
            var client   = new Mock <MobileServiceClient>();

            client.Object.Serializer = new MobileServiceSerializer();
            var context = new Mock <MobileServiceSyncContext>(client.Object);
            var table   = new Mock <MobileServiceTable <ToDoWithSystemPropertiesType> >(testName, client.Object);

            var    query      = new MobileServiceTableQueryDescription(testName);
            var    result     = new JArray();
            string firstQuery = "$filter=(updatedAt ge 2013-01-01T00%3A00%3A00.0000000%2B00%3A00)&$orderby=updatedAt&$skip=0&$top=50";

            var action = new PullAction(
                table.Object, MobileServiceTableKind.Table,
                context.Object,
                queryName, query,
                null, null,
                opQueue.Object,
                settings.Object,
                store.Object,
                MobileServiceRemoteTableOptions.All,
                pullOptions: null,
                reader: null,
                cancellationToken: CancellationToken.None);

            opQueue
            .Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult <IDisposable>(null));
            opQueue
            .Setup(q => q.CountPending(It.IsAny <string>()))
            .Returns(Task.FromResult(0L));
            table
            .Setup(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)));
            settings
            .Setup(s => s.GetDeltaTokenAsync(testName, queryName))
            .Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)));

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();
            settings.VerifyAll();
        }
        public async Task SavesTheMaxUpdatedAt_IfQueryIdIsSpecified_WithoutFilter()
        {
            string testName  = "test";
            string queryName = "latestItems";

            var store    = new Mock <IMobileServiceLocalStore>(MockBehavior.Strict);
            var settings = new Mock <MobileServiceSyncSettingsManager>(MockBehavior.Strict);
            var opQueue  = new Mock <OperationQueue>(MockBehavior.Strict, store.Object);
            var handler  = new Mock <IMobileServiceSyncHandler>(MockBehavior.Strict);
            var client   = new Mock <MobileServiceClient>();

            client.Object.Serializer = new MobileServiceSerializer();
            var context = new Mock <MobileServiceSyncContext>(client.Object);
            var table   = new Mock <MobileServiceTable <ToDoWithSystemPropertiesType> >(testName, client.Object);

            var query        = new MobileServiceTableQueryDescription(testName);
            var maxUpdatedAt = new DateTime(2014, 07, 09);
            var result       = new JArray(new[]
            {
                new JObject()
                {
                    { "id", "abc" }, { "text", "has id" }, { "updatedAt", "1985-07-17" }
                },
                new JObject()
                {
                    { "id", "abc" }, { "text", "has id" }, { "updatedAt", "2014-07-09" }
                }
            });
            string firstQuery  = "$filter=(updatedAt ge 2013-01-01T00%3A00%3A00.0000000%2B00%3A00)&$orderby=updatedAt&$skip=0&$top=50";
            string secondQuery = "$filter=(updatedAt ge 2014-07-09T07%3A00%3A00.0000000%2B00%3A00)&$orderby=updatedAt&$skip=0&$top=50";

            var action = new PullAction(
                table.Object, MobileServiceTableKind.Table,
                context.Object,
                queryName, query,
                null, null,
                opQueue.Object,
                settings.Object,
                store.Object,
                MobileServiceRemoteTableOptions.All,
                pullOptions: null,
                reader: null,
                cancellationToken: CancellationToken.None);

            opQueue
            .Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult <IDisposable>(null));
            opQueue
            .Setup(q => q.CountPending(It.IsAny <string>()))
            .Returns(Task.FromResult(0L));
            table
            .Setup(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)));
            opQueue
            .Setup(q => q.GetOperationByItemIdAsync(It.IsAny <string>(), It.IsAny <string>()))
            .Returns(Task.FromResult <MobileServiceTableOperation>(null));
            table
            .Setup(t => t.ReadAsync(secondQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            store
            .Setup(s => s.UpsertAsync(testName, It.IsAny <IEnumerable <JObject> >(), true))
            .Returns(Task.FromResult(0));
            settings
            .Setup(s => s.GetDeltaTokenAsync(testName, queryName))
            .Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)));
            settings
            .Setup(s => s.SetDeltaTokenAsync(testName, queryName, maxUpdatedAt))
            .Returns(Task.FromResult(0));

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.Verify(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()), Times.AtLeastOnce());
            settings.VerifyAll();
            store.Verify(s => s.DeleteAsync(testName, It.IsAny <IEnumerable <string> >()), Times.Never(), "There shouldn't be any call to delete");
        }
        public async Task DoesNotUpsertAnObject_IfRecordIsPresentInOperationQueue()
        {
            string testName = "test";

            var store    = new Mock <IMobileServiceLocalStore>(MockBehavior.Strict);
            var settings = new Mock <MobileServiceSyncSettingsManager>(MockBehavior.Strict);
            var opQueue  = new Mock <OperationQueue>(MockBehavior.Strict, store.Object);
            var handler  = new Mock <IMobileServiceSyncHandler>(MockBehavior.Strict);
            var client   = new Mock <MobileServiceClient>();

            client.Object.Serializer = new MobileServiceSerializer();
            var context = new Mock <MobileServiceSyncContext>(client.Object);
            var table   = new Mock <MobileServiceTable <ToDoWithSystemPropertiesType> >(testName, client.Object);

            var query  = new MobileServiceTableQueryDescription(testName);
            var action = new PullAction(
                table.Object, MobileServiceTableKind.Table,
                context.Object, null, query, null, null,
                opQueue.Object, settings.Object, store.Object,
                MobileServiceRemoteTableOptions.All,
                pullOptions: null, reader: null, cancellationToken: CancellationToken.None);

            //// item with insert operation from server
            var insertItemWithPendingOperation = new JObject()
            {
                { "id", "abc" }, { "text", "has pending operation" }
            };

            //// item with update operation from server
            var updateItemWithPendingOperation = new JObject()
            {
                { "id", "abc2" }, { "text", "has pending operation" }
            };

            //// item with delete operation from server
            var deleteItemWithPendingOperation = new JObject()
            {
                { "id", "abc3" }, { "text", "has pending operation" }
            };

            //// item with no pending operation from server
            var itemWithNoPendingOperation = new JObject()
            {
                { "id", "abc4" }, { "text", "has no pending operation" }
            };

            var result = new JArray(new[] {
                insertItemWithPendingOperation,
                updateItemWithPendingOperation,
                deleteItemWithPendingOperation,
                itemWithNoPendingOperation
            });

            opQueue
            .Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult <IDisposable>(null));
            opQueue
            .Setup(q => q.CountPending(It.IsAny <string>()))
            .Returns(Task.FromResult(0L));
            opQueue
            .Setup(q => q.GetOperationByItemIdAsync(It.IsAny <string>(), It.IsAny <string>()))
            .Returns((string tableName, string id) =>
            {
                if (id.Equals("abc") || id.Equals("abc2") || id.Equals("abc3"))
                {
                    return(Task.FromResult <MobileServiceTableOperation>(new InsertOperation(tableName, MobileServiceTableKind.Table, id)));
                }
                else
                {
                    return(Task.FromResult <MobileServiceTableOperation>(null));
                }
            });

            //// below two reads correspond to first and second page from the server
            table
            .SetupSequence(t => t.ReadAsync(It.IsAny <string>(), It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)))
            .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));

            store
            .Setup(s => s.UpsertAsync(testName, It.IsAny <IEnumerable <JObject> >(), true))
            .Returns(Task.FromResult(0))
            .Callback <string, IEnumerable <JObject>, bool>((tableName, items, fromServer) =>
            {
                Assert.Single(items);
                Assert.Equal(itemWithNoPendingOperation, items.First());
            });

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();

            store.Verify(s => s.DeleteAsync(testName, It.IsAny <IEnumerable <string> >()), Times.Never(), "There shouldn't be any call to delete");
        }
        public async Task DoesNotUpsertAnObject_IfItDoesNotHaveAnId()
        {
            string testName = "test";

            var store    = new Mock <IMobileServiceLocalStore>(MockBehavior.Strict);
            var settings = new Mock <MobileServiceSyncSettingsManager>(MockBehavior.Strict);
            var opQueue  = new Mock <OperationQueue>(MockBehavior.Strict, store.Object);
            var handler  = new Mock <IMobileServiceSyncHandler>(MockBehavior.Strict);
            var client   = new Mock <MobileServiceClient>();

            client.Object.Serializer = new MobileServiceSerializer();
            var context = new Mock <MobileServiceSyncContext>(client.Object);
            var table   = new Mock <MobileServiceTable <ToDoWithSystemPropertiesType> >(testName, client.Object);

            var query  = new MobileServiceTableQueryDescription(testName);
            var action = new PullAction(
                table.Object, MobileServiceTableKind.Table,
                context.Object,
                null, query,
                null, null,
                opQueue.Object,
                settings.Object,
                store.Object,
                MobileServiceRemoteTableOptions.All,
                pullOptions: null,
                reader: null,
                cancellationToken: CancellationToken.None);

            var itemWithId = new JObject()
            {
                { "id", "abc" }, { "text", "has id" }
            };
            var itemWithoutId = new JObject()
            {
                { "text", "no id" }
            };
            var result = new JArray(new[] {
                itemWithId,
                itemWithoutId
            });

            opQueue
            .Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult <IDisposable>(null));
            opQueue
            .Setup(q => q.CountPending(It.IsAny <string>()))
            .Returns(Task.FromResult(0L));
            opQueue
            .Setup(q => q.GetOperationByItemIdAsync(It.IsAny <string>(), It.IsAny <string>()))
            .Returns(Task.FromResult <MobileServiceTableOperation>(null));
            table
            .SetupSequence(t => t.ReadAsync(It.IsAny <string>(), It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)))
            .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            store
            .Setup(s => s.UpsertAsync(testName, It.IsAny <IEnumerable <JObject> >(), true))
            .Returns(Task.FromResult(0))
            .Callback <string, IEnumerable <JObject>, bool>((tableName, items, fromServer) =>
            {
                Assert.Single(items);
                Assert.Equal(itemWithId, items.First());
            });

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();

            store.Verify(s => s.DeleteAsync("test", It.IsAny <IEnumerable <string> >()), Times.Never(), "There shouldn't be any call to delete");
        }