public async Task ReadAsyncWithStringIdTypeAndStringIdResponseContent()
        {
            string[] testIdData = IdTestData.ValidStringIds.Concat(
                                  IdTestData.EmptyStringIds).Concat(
                                  IdTestData.InvalidStringIds).ToArray();

            foreach (string testId in testIdData)
            {
                TestHttpHandler hijack = new TestHttpHandler();

                // Make the testId JSON safe
                string jsonTestId = testId.Replace("\\", "\\\\").Replace("\"", "\\\"");

                hijack.SetResponseContent("[{\"id\":\"" + jsonTestId + "\",\"String\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

                IMobileServiceTable<StringIdType> table = service.GetTable<StringIdType>();

                IEnumerable<StringIdType> results = await table.ReadAsync();
                StringIdType[] items = results.ToArray();

                Assert.AreEqual(1, items.Count());
                Assert.AreEqual(testId, items[0].Id);
                Assert.AreEqual("Hey", items[0].String);
            }
        }
        public async Task ReadAsync_WithRelativeUri_Generic()
        {
            var data = new[]
            {
                new 
                {
                    ServiceUri = "http://www.test.com", 
                    QueryUri = "/about?$filter=a eq b&$orderby=c", 
                    RequestUri = "http://www.test.com/about?$filter=a eq b&$orderby=c"
                },
                new 
                {
                    ServiceUri = "http://www.test.com/", 
                    QueryUri = "/about?$filter=a eq b&$orderby=c", 
                    RequestUri = "http://www.test.com/about?$filter=a eq b&$orderby=c"
                }
            };

            foreach (var item in data)
            {
                var hijack = new TestHttpHandler();
                hijack.SetResponseContent("[{\"col1\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient(item.ServiceUri, "secret...", hijack);

                IMobileServiceTable<ToDo> table = service.GetTable<ToDo>();

                await table.ReadAsync<ToDo>(item.QueryUri);

                Assert.AreEqual("TT", hijack.Request.Headers.GetValues("X-ZUMO-FEATURES").First());
                Assert.AreEqual(item.RequestUri, hijack.Request.RequestUri.ToString());
            }
        }
        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 void DoesNotRewireSingleWiredDelegatingHandler()
        {
            string appUrl = MobileAppUriValidator.DummyMobileApp;

            TestHttpHandler innerHandler = new TestHttpHandler();
            DelegatingHandler wiredHandler = new TestHttpHandler();
            wiredHandler.InnerHandler = innerHandler;

            IMobileServiceClient service = new MobileServiceClient(appUrl, handlers: wiredHandler);

            Assert.AreEqual(wiredHandler.InnerHandler, innerHandler, "The prewired handler passed in should not have been rewired");
        }
        public async Task DateOffsetUri()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            IMobileServiceClient client = new MobileServiceClient("http://www.test.com", null, hijack);
            IMobileServiceTable<DateOffsetExample> table = client.GetTable<DateOffsetExample>();

            hijack.Response.StatusCode = HttpStatusCode.OK;
            hijack.SetResponseContent("[]");

            DateTimeOffset date = new DateTimeOffset(2009, 11, 21, 14, 22, 59, 860, TimeSpan.FromHours(-8));
            await table.Where(b => b.Date == date).ToEnumerableAsync();
            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "$filter=(DateOffsetExampleDate eq datetimeoffset'2009-11-21T14:22:59.8600000-08:00')");
        }
        public async Task ReadAsync_WithAbsoluteUri_Generic()
        {
            var hijack = new TestHttpHandler();
            hijack.SetResponseContent("[{\"col1\":\"Hey\"}]");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            IMobileServiceTable<ToDo> table = service.GetTable<ToDo>();

            await table.ReadAsync<ToDo>("http://www.test.com/about?$filter=a eq b&$orderby=c");

            Assert.AreEqual("TT,LH", hijack.Request.Headers.GetValues("X-ZUMO-FEATURES").First());
            Assert.AreEqual("http://www.test.com/about?$filter=a eq b&$orderby=c", hijack.Request.RequestUri.ToString());
        }
        public async Task DateOffsetUri()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            IMobileServiceClient client = new MobileServiceClient("http://www.test.com", null, hijack);
            IMobileServiceTable<DateOffsetExample> table = client.GetTable<DateOffsetExample>();

            hijack.Response = new HttpResponseMessage(HttpStatusCode.OK);
            hijack.SetResponseContent("[]");

            var date = DateTimeOffset.Parse("2009-11-21T06:22:59.8600000-08:00");
            await table.Where(b => b.Date == date).ToEnumerableAsync();
            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "$filter=(DateOffsetExampleDate eq datetimeoffset'2009-11-21T06:22:59.8600000-08:00')");
        }
        public async Task LookupAsyncGeneric()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable<StringType> table = service.GetTable<StringType>();

            hijack.SetResponseContent("{\"id\":12,\"String\":\"Hello\"}");

            StringType expected = await table.LookupAsync(12);

            Assert.Contains(hijack.Request.RequestUri.ToString(), "12");
            Assert.AreEqual(12, expected.Id);
            Assert.AreEqual("Hello", expected.String);
        }
        public async Task ReadAsyncWithStringIdTypeAndNullIdResponseContent()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            hijack.SetResponseContent("[{\"id\":null,\"String\":\"Hey\"}]");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            IMobileServiceTable<StringIdType> table = service.GetTable<StringIdType>();

            IEnumerable<StringIdType> results = await table.ReadAsync();
            StringIdType[] items = results.ToArray();

            Assert.AreEqual(1, items.Count());
            Assert.AreEqual(null, items[0].Id);
            Assert.AreEqual("Hey", items[0].String);
        }
        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");
        }
        public async Task LookupAsyncGenericWithUserParameters()
        {
            var userDefinedParameters = new Dictionary<string, string>() { { "state", "CA" } };

            TestHttpHandler hijack = new TestHttpHandler();
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable<StringType> table = service.GetTable<StringType>();

            hijack.SetResponseContent("{\"id\":12,\"String\":\"Hello\"}");

            StringType expected = await table.LookupAsync(12, userDefinedParameters);

            Assert.Contains(hijack.Request.RequestUri.ToString(), "12");
            Assert.Contains(hijack.Request.RequestUri.Query, "state=CA");
            Assert.AreEqual(12, expected.Id);
            Assert.AreEqual("Hello", expected.String);
        }
        [AsyncTestMethod] // this is the default buggy behavior that we've already shipped
        public async Task ReadAsync_ModifiesStringId_IfItContainsIsoDateValue()
        {
            var hijack = new TestHttpHandler();
            hijack.SetResponseContent(@"[{
                                        ""id"": ""2014-01-29T23:01:33.444Z"",
                                        ""__createdAt"": ""2014-01-29T23:01:33.444Z""
                                        }]");

            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable<ToDoWithSystemPropertiesType> table = service.GetTable<ToDoWithSystemPropertiesType>();

            IEnumerable<ToDoWithSystemPropertiesType> results = await table.ReadAsync();
            ToDoWithSystemPropertiesType item = results.First();

            Assert.AreEqual(item.Id, "01/29/2014 23:01:33");
            Assert.AreEqual(item.CreatedAt, DateTime.Parse("2014-01-29T23:01:33.444Z"));
        }
        public async Task ReadAsync()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            hijack.SetResponseContent("{\"Count\":1, People: [{\"Id\":\"12\", \"String\":\"Hey\"}] }");

            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            IMobileServiceTable table = service.GetTable("tests");
            JToken people = await table.ReadAsync("$filter=id eq 12");

            Assert.Contains(hijack.Request.RequestUri.ToString(), "tests");
            Assert.Contains(hijack.Request.RequestUri.ToString(), "$filter=id eq 12");

            Assert.AreEqual(1, (int)people["Count"]);
            Assert.AreEqual(12, (int)people["People"][0]["Id"]);
            Assert.AreEqual("Hey", (string)people["People"][0]["String"]);
        }
        public async Task ReadAsyncGeneric()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            hijack.SetResponseContent("[{\"id\":12,\"String\":\"Hey\"}]");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            IMobileServiceTable<StringType> table = service.GetTable<StringType>();

            IEnumerable<StringType> results = await table.ReadAsync();
            StringType[] people = results.Cast<StringType>().ToArray();

            Assert.Contains(hijack.Request.RequestUri.ToString(), "StringType");

            Assert.AreEqual(1, people.Count());
            Assert.AreEqual(12, people[0].Id);
            Assert.AreEqual("Hey", people[0].String);
        }
        public async Task SingleHttpHandlerConstructor()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            IMobileServiceClient service =
                new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, handlers: hijack);
            MobileAppUriValidator mobileAppUriValidator = new MobileAppUriValidator(service);

            // Ensure properties are copied over
            Assert.AreEqual(MobileAppUriValidator.DummyMobileApp, service.MobileAppUri.ToString());

            // Set the handler to return an empty array
            hijack.SetResponseContent("[]");
            JToken response = await service.GetTable("foo").ReadAsync("bar");

            // Verify the handler was in the loop
            Assert.StartsWith(hijack.Request.RequestUri.ToString(), mobileAppUriValidator.TableBaseUri);
        }
        [AsyncTestMethod] // user has to set the serializer setting to round trip dates in string fields correctly
        public async Task ReadAsync_DoesNotModifyStringId_IfItContainsIsoDateValueAndSerializerIsConfiguredToNotParseDates()
        {
            var hijack = new TestHttpHandler();
            hijack.SetResponseContent(@"[{
                                        ""id"": ""2014-01-29T23:01:33.444Z"",
                                        ""__createdAt"": ""2014-01-29T23:01:33.444Z""
                                        }]");

            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            service.SerializerSettings.DateParseHandling = DateParseHandling.None;
            IMobileServiceTable<ToDoWithSystemPropertiesType> table = service.GetTable<ToDoWithSystemPropertiesType>();

            IEnumerable<ToDoWithSystemPropertiesType> results = await table.ReadAsync();
            ToDoWithSystemPropertiesType item = results.First();

            Assert.AreEqual(item.Id, "2014-01-29T23:01:33.444Z");
            Assert.AreEqual(item.CreatedAt, DateTime.Parse("2014-01-29T23:01:33.444Z"));
        }
        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 SingleHttpHandlerConstructor()
        {
            string appUrl = "http://www.test.com/";
            string appKey = "secret...";
            TestHttpHandler hijack = new TestHttpHandler();

            IMobileServiceClient service =
                new MobileServiceClient(new Uri(appUrl), appKey, hijack);

            // Ensure properties are copied over
            Assert.AreEqual(appUrl, service.ApplicationUri.ToString());
            Assert.AreEqual(appKey, service.ApplicationKey);

            // Set the handler to return an empty array
            hijack.SetResponseContent("[]");
            JToken response = await service.GetTable("foo").ReadAsync("bar");

            // Verify the handler was in the loop
            Assert.StartsWith(hijack.Request.RequestUri.ToString(), appUrl);
        }
        public async Task ReadAsyncWithUserParameters()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            hijack.SetResponseContent("{\"Count\":1, People: [{\"Id\":\"12\", \"String\":\"Hey\"}] }");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            var userDefinedParameters = new Dictionary<string, string>() { { "tags", "#pizza #beer" } };

            IMobileServiceTable table = service.GetTable("tests");

            JToken people = await table.ReadAsync("$filter=id eq 12", userDefinedParameters);

            Assert.Contains(hijack.Request.RequestUri.ToString(), "tests");
            Assert.Contains(hijack.Request.RequestUri.AbsoluteUri, "tags=%23pizza%20%23beer");
            Assert.Contains(hijack.Request.RequestUri.ToString(), "$filter=id eq 12");

            Assert.AreEqual(1, (int)people["Count"]);
            Assert.AreEqual(12, (int)people["People"][0]["Id"]);
            Assert.AreEqual("Hey", (string)people["People"][0]["String"]);
        }
        public async Task ReadAsync_WithAbsoluteUri_Generic()
        {
            var data = new[]
            {
                new 
                {
                    ServiceUri = MobileAppUriValidator.DummyMobileApp, 
                    Result = MobileAppUriValidator.DummyMobileApp + "about?$filter=a eq b&$orderby=c"
                },
                new 
                {
                    ServiceUri = MobileAppUriValidator.DummyMobileAppWithoutTralingSlash, 
                    Result = MobileAppUriValidator.DummyMobileAppWithoutTralingSlash + "/about?$filter=a eq b&$orderby=c"
                },
                new 
                {
                    ServiceUri = MobileAppUriValidator.DummyMobileAppUriWithFolder, 
                    Result = MobileAppUriValidator.DummyMobileAppUriWithFolder + "about?$filter=a eq b&$orderby=c"
                },
                new 
                {
                    ServiceUri = MobileAppUriValidator.DummyMobileAppUriWithFolderWithoutTralingSlash, 
                    Result = MobileAppUriValidator.DummyMobileAppUriWithFolderWithoutTralingSlash + "/about?$filter=a eq b&$orderby=c"
                },
            };

            foreach (var item in data)
            {
                var hijack = new TestHttpHandler();
                hijack.SetResponseContent("[{\"col1\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient(item.ServiceUri, hijack);
                MobileAppUriValidator mobileAppUriValidator = new MobileAppUriValidator(service);

                IMobileServiceTable<ToDo> table = service.GetTable<ToDo>();

                await table.ReadAsync<ToDo>(item.Result);

                Assert.AreEqual("TT,LH", hijack.Request.Headers.GetValues("X-ZUMO-FEATURES").First());
                Assert.AreEqual(item.Result, hijack.Request.RequestUri.ToString());
            }
        }
        public async Task InvokeCustomAPI_ErrorStringAndNoContentType()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            hijack.Response         = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
            hijack.Response.Content = new StringContent("message", Encoding.UTF8, null);
            hijack.Response.Content.Headers.ContentType = null;

            MobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack);

            try
            {
                await service.InvokeApiAsync("testapi");

                Assert.Fail("Invoke API should have thrown");
            }
            catch (Exception e)
            {
                Assert.AreEqual(e.Message, "The request could not be completed.  (Bad Request)");
            }
        }
        public async Task DateUri()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            IMobileServiceClient client = new MobileServiceClient("http://www.test.com", null, hijack);
            IMobileServiceTable<DateExample> table = client.GetTable<DateExample>();

            hijack.Response = new HttpResponseMessage(HttpStatusCode.OK);
            hijack.SetResponseContent("[]");

            // Verify a full UTC date
            DateTime date = new DateTime(2009, 11, 21, 14, 22, 59, 860, DateTimeKind.Utc);
            await table.Where(b => b.Date == date).ToEnumerableAsync();
            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "$filter=(DateExampleDate eq datetime'2009-11-21T14:22:59.860Z')");

            // Local date is converted to UTC
            hijack.Response = new HttpResponseMessage(HttpStatusCode.OK);
            hijack.SetResponseContent("[]");
            date = new DateTime(2009, 11, 21, 14, 22, 59, 860, DateTimeKind.Local);
            await table.Where(b => b.Date == date).ToEnumerableAsync();
            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "Z')");
        }
        public async Task InvokeCustomAPIResponseWithParams()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            hijack.Response         = new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
            hijack.Response.Content = new StringContent("{\"id\":\"2\"}", Encoding.UTF8, "application/json");
            var myParams = new Dictionary <string, string>()
            {
                { "a", "1" }, { "b", "2" }
            };

            MobileServiceClient   service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack);
            MobileAppUriValidator mobileAppUriValidator = new MobileAppUriValidator(service);

            HttpResponseMessage response = await service.InvokeApiAsync("calculator/add", null, HttpMethod.Post, null, myParams);

            Assert.AreEqual(hijack.Request.RequestUri.LocalPath, mobileAppUriValidator.GetApiUriPath("calculator/add"));
            Assert.AreEqual(hijack.Request.RequestUri.Query, "?a=1&b=2");
            Assert.IsNull(hijack.Request.Content);
            Assert.Contains(response.Content.ReadAsStringAsync().Result, "{\"id\":\"2\"}");
        }
        public static DelegatingHandler CreateTestHttpHandler(string expectedUri, HttpMethod expectedMethod, string responseContent, HttpStatusCode?httpStatusCode = null, Uri location = null, string expectedRequestContent = null)
        {
            var handler = new TestHttpHandler
            {
                OnSendingRequest = message =>
                {
                    Assert.AreEqual(expectedUri, message.RequestUri.OriginalString, "The Http Uri used to send the request is different than expected.");
                    Assert.AreEqual(expectedMethod, message.Method, "The Http Method used to send the request is different than expected.");

                    if (expectedRequestContent != null)
                    {
                        var messageContent = Regex.Replace(message.Content.ReadAsStringAsync().Result, @"\s+", String.Empty);
                        expectedRequestContent = Regex.Replace(expectedRequestContent, @"\s+", String.Empty);
                        Assert.AreEqual(expectedRequestContent, messageContent, "The Http request content is different than expected.");
                    }

                    return(Task.FromResult(message));
                }
            };

            if (responseContent != null)
            {
                handler.SetResponseContent(responseContent);
            }
            else
            {
                handler.Response = new HttpResponseMessage(HttpStatusCode.OK);
            }

            if (location != null)
            {
                handler.Response.Headers.Location = location;
            }

            if (httpStatusCode.HasValue)
            {
                handler.Response.StatusCode = httpStatusCode.Value;
            }
            return(handler);
        }
        public static DelegatingHandler CreateTestHttpHandler(string expectedUri, HttpMethod expectedMethod, string responseContent, HttpStatusCode? httpStatusCode = null, Uri location = null, string expectedRequestContent = null)
        {
            var handler = new TestHttpHandler
            {
                OnSendingRequest = message =>
                {
                    Assert.AreEqual(expectedUri, message.RequestUri.OriginalString, "The Http Uri used to send the request is different than expected.");
                    Assert.AreEqual(expectedMethod, message.Method, "The Http Method used to send the request is different than expected.");

                    if (expectedRequestContent != null)
                    {
                        var messageContent = Regex.Replace(message.Content.ReadAsStringAsync().Result, @"\s+", String.Empty);
                        expectedRequestContent = Regex.Replace(expectedRequestContent, @"\s+", String.Empty);
                        Assert.AreEqual(expectedRequestContent, messageContent, "The Http request content is different than expected.");
                    }

                    return Task.FromResult(message);
                }
            };

            if (responseContent != null)
            {
                handler.SetResponseContent(responseContent);
            }
            else
            {
                handler.Response = new HttpResponseMessage(HttpStatusCode.OK);
            }

            if (location != null)
            {
                handler.Response.Headers.Location = location;
            }

            if (httpStatusCode.HasValue)
            {
                handler.Response.StatusCode = httpStatusCode.Value;
            }
            return handler;
        }
        public async Task PushAsync_Succeeds_WithClientWinsPolicy()
        {
            var hijack = new TestHttpHandler();

            hijack.Responses.Add(new HttpResponseMessage(HttpStatusCode.PreconditionFailed)
            {
                Content = new StringContent("{\"id\":\"abc\",\"version\":\"Hey\"}")
            });
            hijack.AddResponseContent(@"{""id"": ""abc""}");

            var handler = new MobileServiceSyncHandlerMock();

            handler.TableOperationAction = async op =>
            {
                for (int i = 0; i < 2; i++)
                {
                    try
                    {
                        return(await op.ExecuteAsync());
                    }
                    catch (MobileServicePreconditionFailedException ex)
                    {
                        op.Item[MobileServiceSystemColumns.Version] = ex.Value[MobileServiceSystemColumns.Version];
                    }
                }
                return(null);
            };
            IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack);
            await service.SyncContext.InitializeAsync(new MobileServiceLocalStoreMock(), handler);

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

            await table.UpdateAsync(new JObject()
            {
                { "id", "abc" }, { "version", "Wow" }
            });

            await service.SyncContext.PushAsync();
        }
        private static async Task RefreshUserAsync_Setup(string alternateLoginUri = null)
        {
            string appUrl          = MobileAppUriValidator.DummyMobileApp;
            string newAuthToken    = "new-auth-token";
            string userId          = "sid:xxxxxxxxxxxxxxxxx";
            string responseContent = "{\"authenticationToken\":\"" + newAuthToken + "\",\"user\":{\"userId\":\"" + userId + "\"}}";

            TestHttpHandler hijack = new TestHttpHandler();

            hijack.Response         = new HttpResponseMessage(HttpStatusCode.OK);
            hijack.Response.Content = new StringContent(responseContent);

            MobileServiceHttpClient.DefaultHandlerFactory = () => hijack;
            MobileServiceClient client = new MobileServiceClient(appUrl, hijack);

            client.CurrentUser = new MobileServiceUser(userId)
            {
                MobileServiceAuthenticationToken = "auth-token"
            };

            string refreshUrl;

            if (!string.IsNullOrEmpty(alternateLoginUri))
            {
                refreshUrl = alternateLoginUri + ".auth/refresh";
                client.AlternateLoginHost = new Uri(alternateLoginUri);
            }
            else
            {
                refreshUrl = appUrl + ".auth/refresh";
            }
            MobileServiceUser user = await client.RefreshUserAsync();

            Assert.AreEqual(EnumValueAttribute.GetValue(MobileServiceFeatures.RefreshToken),
                            hijack.Request.Headers.GetValues(MobileServiceHttpClient.ZumoFeaturesHeader).FirstOrDefault());
            Assert.AreEqual(hijack.Request.RequestUri.OriginalString, refreshUrl);
            Assert.AreEqual(newAuthToken, user.MobileServiceAuthenticationToken);
            Assert.AreEqual(userId, user.UserId);
        }
        public async Task WithParametersAsyncGeneric()
        {
            var userDefinedParameters = new Dictionary <string, string>()
            {
                { "state", "WY" }
            };

            TestHttpHandler hijack = new TestHttpHandler();

            hijack.SetResponseContent("[{\"id\":12,\"String\":\"Hey\"}]");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            IMobileServiceTable <StringType> table = service.GetTable <StringType>();
            List <StringType> people = await table.WithParameters(userDefinedParameters).ToListAsync();

            Assert.Contains(hijack.Request.RequestUri.ToString(), "StringType");
            Assert.Contains(hijack.Request.RequestUri.Query, "state=WY");

            Assert.AreEqual(1, people.Count);
            Assert.AreEqual(12, people[0].Id);
            Assert.AreEqual("Hey", people[0].String);
        }
        public async Task ReadAsyncWithNonStringAndNonIntIdResponseContent()
        {
            object[] testIdData = IdTestData.NonStringNonIntValidJsonIds;

            foreach (object testId in testIdData)
            {
                string stringTestId = testId.ToString().ToLower();

                TestHttpHandler hijack = new TestHttpHandler();
                hijack.SetResponseContent("[{\"id\":" + stringTestId + ",\"String\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

                IMobileServiceTable table = service.GetTable("someTable");

                JToken results = await table.ReadAsync("");
                JToken[] items = results.ToArray();

                Assert.AreEqual(1, items.Count());
                Assert.AreEqual(testId, items[0]["id"].ToObject(testId.GetType()));
                Assert.AreEqual("Hey", (string)items[0]["String"]);
            }
        }
        public async Task InsertAsyncGenericWithUserParameters()
        {
            var userDefinedParameters = new Dictionary <string, string>()
            {
                { "state", "CA" }
            };

            TestHttpHandler                  hijack  = new TestHttpHandler();
            IMobileServiceClient             service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable <StringType> table   = service.GetTable <StringType>();

            StringType obj = new StringType();

            obj.String = "new";

            hijack.SetResponseContent("{\"id\":12,\"value\":\"new\"}");
            await table.InsertAsync(obj, userDefinedParameters);

            Assert.AreEqual(12, obj.Id);
            Assert.Contains(hijack.Request.RequestUri.ToString(), "StringType");
            Assert.Contains(hijack.Request.RequestUri.Query, "state=CA");
        }
        public async Task ReadAsyncWithIntIdResponseContent()
        {
            long[] testIdData = IdTestData.ValidIntIds.Concat(
                                 IdTestData.InvalidIntIds).ToArray();

            foreach (long testId in testIdData)
            {
                string stringTestId = testId.ToString();

                TestHttpHandler hijack = new TestHttpHandler();
                hijack.SetResponseContent("[{\"id\":" + stringTestId + ",\"String\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

                IMobileServiceTable table = service.GetTable("someTable");

                JToken results = await table.ReadAsync("");
                JToken[] items = results.ToArray();

                Assert.AreEqual(1, items.Count());
                Assert.AreEqual(testId, (long)items[0]["id"]);
                Assert.AreEqual("Hey", (string)items[0]["String"]);
            }
        }
        public async Task DateUri()
        {
            TestHttpHandler      hijack             = new TestHttpHandler();
            IMobileServiceClient client             = new MobileServiceClient("http://www.test.com", null, hijack);
            IMobileServiceTable <DateExample> table = client.GetTable <DateExample>();

            hijack.Response.StatusCode = HttpStatusCode.OK;
            hijack.SetResponseContent("[]");

            // Verify a full UTC date
            DateTime date = new DateTime(2009, 11, 21, 14, 22, 59, 860, DateTimeKind.Utc);
            await table.Where(b => b.Date == date).ToEnumerableAsync();

            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "$filter=(DateExampleDate eq datetime'2009-11-21T14:22:59.860Z')");

            // Local date is converted to UTC
            hijack.Response = new HttpResponseMessage(HttpStatusCode.OK);
            hijack.SetResponseContent("[]");
            date = new DateTime(2009, 11, 21, 14, 22, 59, 860, DateTimeKind.Local);
            await table.Where(b => b.Date == date).ToEnumerableAsync();

            Assert.EndsWith(hijack.Request.RequestUri.ToString(), "Z')");
        }
        public async Task RefreshAsyncGenericWithUserParameters()
        {
            var userDefinedParameters = new Dictionary <string, string>()
            {
                { "state", "CA" }
            };

            TestHttpHandler                  hijack  = new TestHttpHandler();
            IMobileServiceClient             service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable <StringType> table   = service.GetTable <StringType>();

            StringType expected = new StringType();

            expected.Id = 12;

            hijack.SetResponseContent("{\"id\":12,\"String\":\"Goodbye\"}");
            await table.RefreshAsync(expected, userDefinedParameters);

            Assert.Contains(hijack.Request.RequestUri.ToString(), "$filter=id eq 12");
            Assert.Contains(hijack.Request.RequestUri.Query, "state=CA");
            Assert.AreEqual(12, expected.Id);
            Assert.AreEqual("Goodbye", expected.String);
        }
        public async Task DeleteAsyncGenericWithParameters()
        {
            var userDefinedParameters = new Dictionary <string, string>()
            {
                { "state", "WY" }
            };

            TestHttpHandler                  hijack  = new TestHttpHandler();
            IMobileServiceClient             service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            IMobileServiceTable <StringType> table   = service.GetTable <StringType>();

            StringType obj = new StringType();

            obj.Id     = 12;
            obj.String = "new";

            await table.DeleteAsync(obj, userDefinedParameters);

            Assert.AreEqual(0, obj.Id);
            Assert.Contains(hijack.Request.RequestUri.ToString(), "StringType");
            Assert.IsNull(hijack.Request.Content);
            Assert.Contains(hijack.Request.RequestUri.Query, "state=WY");
        }
        public async Task MultipleHttpHandlerConstructor()
        {
            string          appUrl = "http://www.test.com/";
            string          appKey = "secret...";
            TestHttpHandler hijack = new TestHttpHandler();

            string firstBeforeMessage  = "Message before 1";
            string firstAfterMessage   = "Message after 1";
            string secondBeforeMessage = "Message before 2";
            string secondAfterMessage  = "Message after 2";

            ComplexDelegatingHandler firstHandler  = new ComplexDelegatingHandler(firstBeforeMessage, firstAfterMessage);
            ComplexDelegatingHandler secondHandler = new ComplexDelegatingHandler(secondBeforeMessage, secondAfterMessage);

            IMobileServiceClient service =
                new MobileServiceClient(new Uri(appUrl), appKey, firstHandler, secondHandler, hijack);

            // Validate that handlers are properly chained
            Assert.AreSame(hijack, secondHandler.InnerHandler);
            Assert.AreSame(secondHandler, firstHandler.InnerHandler);

            // Clears the messages on the handler
            ComplexDelegatingHandler.ClearStoredMessages();

            // Set the handler to return an empty array
            hijack.SetResponseContent("[]");
            JToken response = await service.GetTable("foo").ReadAsync("bar");

            var storedMessages = new List <string>(ComplexDelegatingHandler.AllMessages);

            Assert.AreEqual(4, storedMessages.Count);
            Assert.AreEqual(firstBeforeMessage, storedMessages[0]);
            Assert.AreEqual(secondBeforeMessage, storedMessages[1]);
            Assert.AreEqual(secondAfterMessage, storedMessages[2]);
            Assert.AreEqual(firstAfterMessage, storedMessages[3]);
        }
        public async Task ReadAsyncWithStringIdTypeAndNonStringIdResponseContent()
        {
            object[] testIdData = IdTestData.ValidIntIds.Concat(
                                  IdTestData.InvalidIntIds).Cast<object>().Concat(
                                  IdTestData.NonStringNonIntValidJsonIds).ToArray();

            foreach (object testId in testIdData)
            {
                string stringTestId = testId.ToString().ToLower();

                TestHttpHandler hijack = new TestHttpHandler();
                hijack.SetResponseContent("[{\"id\":" + stringTestId + ",\"String\":\"Hey\"}]");
                IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

                IMobileServiceTable<StringIdType> table = service.GetTable<StringIdType>();

                IEnumerable<StringIdType> results = await table.ReadAsync();
                StringIdType[] items = results.ToArray();

                Assert.AreEqual(1, items.Count());
                Assert.AreEqual(testId.ToString(), items[0].Id);
                Assert.AreEqual("Hey", items[0].String);
            }
        }
        public async Task ReadAsyncWithUserParameters()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            hijack.SetResponseContent("{\"Count\":1, People: [{\"Id\":\"12\", \"String\":\"Hey\"}] }");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            var userDefinedParameters = new Dictionary <string, string>()
            {
                { "tags", "#pizza #beer" }
            };

            IMobileServiceTable table = service.GetTable("tests");

            JToken people = await table.ReadAsync("$filter=id eq 12", userDefinedParameters);

            Assert.Contains(hijack.Request.RequestUri.ToString(), "tests");
            Assert.Contains(hijack.Request.RequestUri.AbsoluteUri, "tags=%23pizza%20%23beer");
            Assert.Contains(hijack.Request.RequestUri.ToString(), "$filter=id eq 12");

            Assert.AreEqual(1, (int)people["Count"]);
            Assert.AreEqual(12, (int)people["People"][0]["Id"]);
            Assert.AreEqual("Hey", (string)people["People"][0]["String"]);
        }
        public async Task MultipleHttpHandlerConstructor()
        {
            string appUrl = "http://www.test.com/";
            string appKey = "secret...";
            TestHttpHandler hijack = new TestHttpHandler();

            string firstBeforeMessage = "Message before 1";
            string firstAfterMessage = "Message after 1";
            string secondBeforeMessage = "Message before 2";
            string secondAfterMessage = "Message after 2";

            ComplexDelegatingHandler firstHandler = new ComplexDelegatingHandler(firstBeforeMessage, firstAfterMessage);
            ComplexDelegatingHandler secondHandler = new ComplexDelegatingHandler(secondBeforeMessage, secondAfterMessage);

            IMobileServiceClient service =
                new MobileServiceClient(new Uri(appUrl), appKey, firstHandler, secondHandler, hijack);

            // Validate that handlers are properly chained
            Assert.AreSame(hijack, secondHandler.InnerHandler);
            Assert.AreSame(secondHandler, firstHandler.InnerHandler);

            // Clears the messages on the handler
            ComplexDelegatingHandler.ClearStoredMessages();

            // Set the handler to return an empty array
            hijack.SetResponseContent("[]");
            JToken response = await service.GetTable("foo").ReadAsync("bar");

            var storedMessages = new List<string>(ComplexDelegatingHandler.AllMessages);
            Assert.AreEqual(4, storedMessages.Count);
            Assert.AreEqual(firstBeforeMessage, storedMessages[0]);
            Assert.AreEqual(secondBeforeMessage, storedMessages[1]);
            Assert.AreEqual(secondAfterMessage, storedMessages[2]);
            Assert.AreEqual(firstAfterMessage, storedMessages[3]);
        }
        private async Task ValidateFeaturesHeader(string expectedFeaturesHeader, Func<IMobileServiceClient, Task> operation)
        {
            TestHttpHandler hijack = new TestHttpHandler();
            bool validationDone = false;
            hijack.OnSendingRequest = (request) =>
            {
                Assert.AreEqual(expectedFeaturesHeader, request.Headers.GetValues("X-ZUMO-FEATURES").First());
                validationDone = true;
                return Task.FromResult(request);
            };

            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            hijack.SetResponseContent("{\"id\":3}");
            await operation(service);
            Assert.IsTrue(validationDone);
        }
        public async Task InvokeApiTypedOverloads_HasCorrectFeaturesHeader()
        {
            TestHttpHandler hijack = new TestHttpHandler();
            hijack.OnSendingRequest = (request) =>
            {
                Assert.AreEqual("AT", request.Headers.GetValues("X-ZUMO-FEATURES").First());
                return Task.FromResult(request);
            };

            MobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            hijack.SetResponseContent("{\"id\":3}");
            await service.InvokeApiAsync<IntType>("apiName");

            hijack.SetResponseContent("{\"id\":3}");
            await service.InvokeApiAsync<IntType, IntType>("apiName", new IntType { Id = 1 });

            hijack.OnSendingRequest = (request) =>
            {
                Assert.AreEqual("AT,QS", request.Headers.GetValues("X-ZUMO-FEATURES").First());
                return Task.FromResult(request);
            };

            var dic = new Dictionary<string, string> { { "a", "b" } };
            hijack.SetResponseContent("{\"id\":3}");
            await service.InvokeApiAsync<IntType>("apiName", HttpMethod.Get, dic);

            hijack.SetResponseContent("{\"hello\":\"world\"}");
            await service.InvokeApiAsync<IntType, IntType>("apiName", new IntType { Id = 1 }, HttpMethod.Put, dic);
        }
        public async Task InvokeCustomAPI_ErrorStringAndNoContentType()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            hijack.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
            hijack.Response.Content = new StringContent("message", Encoding.UTF8, null);
            hijack.Response.Content.Headers.ContentType = null;

            MobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            try
            {
                await service.InvokeApiAsync("testapi");
                Assert.Fail("Invoke API should have thrown");
            }
            catch (Exception e)
            {
                Assert.AreEqual(e.Message, "The request could not be completed.  (Bad Request)");
            }
        }
        public async Task InvokeCustomAPI_ErrorWithString()
        {
            TestHttpHandler hijack = new TestHttpHandler();

            hijack.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
            hijack.Response.Content = new StringContent("message", Encoding.UTF8, "text/html");

            MobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);

            try
            {
                await service.InvokeApiAsync("testapi");
                Assert.Fail("Invoke API should have thrown");
            }
            catch (Exception e)
            {
                Assert.AreEqual(e.Message, "message");
            }
        }
        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 PushAsync_Succeeds_WithPendingOperations_AndOpQueueIsConsistent()
        {
            // Essentially async ManualResetEvents
            SemaphoreSlim untilPendingOpsCreated = new SemaphoreSlim(0, 1);
            SemaphoreSlim untilAboutToExecuteOp  = new SemaphoreSlim(0, 1);

            int pushState = 0;

            var handler = new MobileServiceSyncHandlerMock();

            handler.TableOperationAction = async op =>
            {
                try
                {
                    untilAboutToExecuteOp.Release();
                    await untilPendingOpsCreated.WaitAsync();

                    JObject result = await op.ExecuteAsync();

                    if (0 == pushState)
                    {
                        Assert.AreEqual(MobileServiceTableOperationKind.Insert, op.Kind);
                        Assert.AreEqual(0, op.Item.Value <int>("value"));
                    }
                    else
                    {
                        Assert.AreEqual(MobileServiceTableOperationKind.Update, op.Kind);
                        Assert.AreEqual(2, op.Item.Value <int>("value")); // We shouldn't see the value == 1, since it should have been collapsed
                    }

                    // We don't care what the server actually returned, as long as there was no exception raised in our Push logic
                    return(result);
                }
                catch (Exception ex)
                {
                    Assert.Fail("Things are bad: " + ex.Message);
                }

                return(null);
            };

            var hijack = new TestHttpHandler();

            IMobileServiceClient service        = new MobileServiceClient("http://www.test.com", hijack);
            LocalStoreWithDelay  mockLocalStore = new LocalStoreWithDelay();
            await service.SyncContext.InitializeAsync(mockLocalStore, handler);

            JObject item = null;

            // Add the initial operation and perform a push
            IMobileServiceSyncTable table = service.GetSyncTable("someTable");

            string responseContent = @"{ ""id"": ""abc"", ""value"": ""0"", ""version"": ""v0"" }"; // Whatever is fine, since we won't use it or look at it

            // Do this Insert/Push/Update+Update/Push cycle several times fast to try to hit any race conditions that would cause an error
            for (int id = 0; id < 10; id++)
            {
                hijack.SetResponseContent(responseContent);
                string idStr = "id" + id; // Generate a new Id each time in case the mock objects ever care that we insert an item that already exists

                // The Operations and PushAction don't necessarily clone the JObject, so we need a fresh one for each operation or else we'll change
                // the in-memory representation of the JObject stored in all operations, as well as in the "batch" the PushAction owns. This is problematic.
                item = new JObject()
                {
                    { "id", idStr }, { "value", 0 }
                };
                await table.InsertAsync(item);

                Task pushComplete = service.SyncContext.PushAsync();

                // Make sure the PushAction has actually called into our SyncHandler, otherwise the two UpdateOperations could collapse onto it, and
                // there won't necessarily even be a second PushAction
                await untilAboutToExecuteOp.WaitAsync();

                // Add some more operations while that push is in flight. Since these operations affect the same item in someTable, the operations
                // will be stuck awaiting the PushAction since it locks on the row.
                item = new JObject()
                {
                    { "id", idStr }, { "value", 1 }
                };
                Task updateOnce = table.UpdateAsync(item);

                item = new JObject()
                {
                    { "id", idStr }, { "value", 2 }
                };
                Task updateTwice = table.UpdateAsync(item);

                // Before we let the push finish, let's inject a delay that will cause it to take a long time deleting the operation from the queue.
                // This will give the other operations, if there's an unaddressed race condition, a chance to wreak havoc on the op queue.
                mockLocalStore.SetLookupDelay(500);

                // Let the first push finish
                untilPendingOpsCreated.Release();
                await pushComplete;

                mockLocalStore.SetLookupDelay(0);

                await updateOnce;
                await updateTwice;

                // Push again, but now the operation condensed from the two updates should be executed remotely
                pushState = (pushState + 1) % 2;
                hijack.SetResponseContent(responseContent);
                pushComplete = service.SyncContext.PushAsync();
                await untilAboutToExecuteOp.WaitAsync(); // not strictly necessary other than to keep the semaphore count at 0

                untilPendingOpsCreated.Release();

                await pushComplete;
                pushState = (pushState + 1) % 2;
            }
        }