public void TopBatchReturingODataError()
        {
            const string payload = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>
<error xmlns=""http://docs.oasis-open.org/odata/ns/metadata"">
  <code></code>
  <message xml:lang=""en-US"">This error must show up in the error returned below</message>
</error>";

            IODataRequestMessage requestMessage = new ODataTestMessage();
            var responseMessage = new ODataTestMessage();
            responseMessage.SetHeader("Content-Type", "application\atom+xml");
            responseMessage.SetHeader("OData-Version", "4.0");
            responseMessage.StatusCode = 400;
            responseMessage.WriteToStream(payload);
            responseMessage.SetHeader("Content-Length", "0");

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, requestMessage, responseMessage);
            context.AddObject("Products", new SimpleNorthwind.Product() { ID = 1 });
            Action test = () => context.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);

            // we should be returning an ODataErrorException instead of the full contents
            test.ShouldThrow<DataServiceRequestException>()
                .WithInnerException<DataServiceClientException>()
                .And.InnerException.Message.Should().Be("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n<error xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">\r\n  <code></code>\r\n  <message xml:lang=\"en-US\">This error must show up in the error returned below</message>\r\n</error>");
        }
        public void WebExceptionShouldNotBeSurfacedWhenGetResponseThrowsOnBatch()
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => requestMessage, () => { throw new WebException("web exception on getting response"); });
            Action test = () =>  context.ExecuteBatch(context.CreateQuery<NorthwindModel.Products>("Products"));

            test.ShouldThrow<WebException>().WithMessage("web exception on getting response");
        }
        public void WebExceptionShouldNotBeSurfacedWhenSaveChangesWithBatch()
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => requestMessage, () => { throw new WebException("web exception on getting response"); });
            context.AddObject("Products", new SimpleNorthwind.Product() { ID = 1 });
            Action test = () => context.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);

            test.ShouldThrow<WebException>().WithMessage("web exception on getting response");
        }
        public void WebExceptionShouldNotBeSurfacedWhenSaveChangesWithNonBatch()
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => requestMessage, () => { throw new WebException("web exception on getting response"); });
            context.AddObject("Products", new SimpleNorthwind.Product() { ID = 1 });
            Action test = () => context.SaveChanges(SaveChangesOptions.None);

            var innerInnerException = test.ShouldThrow<DataServiceRequestException>().WithInnerException<DataServiceClientException>().And.InnerException.InnerException;
            innerInnerException.Should().BeOfType<WebException>();
            innerInnerException.Message.Should().Be("web exception on getting response");
        }
        public void AttachEntityAddEntityAndBindSaveChanges()
        {
            var odataRequestMessage = new ODataTestMessage();
            var odataResponseMessage = new ODataTestMessage()
            {
                StatusCode = 202,
                MemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>
<entry xml:base=""http://services.odata.org/OData/OData.svc/"" xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices"" xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" xmlns=""http://www.w3.org/2005/Atom"">
    <id>http://services.odata.org/OData/OData.svc/Suppliers(2)</id>
    <category term=""ODataDemo.Product"" scheme=""http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"" />
    <link rel=""edit"" title=""AnnotationTests_Product"" href=""Products(2)"" />
    <title />
    <updated>2012-11-15T19:29:45Z</updated>
    <author>
      <name />
    </author>
    <content type=""application/xml"">
      <m:properties>
        <d:ID m:type=""Edm.Int32"">1</d:ID>
      </m:properties>
    </content>
  </entry>")) };
            odataResponseMessage.SetHeader("DataServiceId", "http://service/foo/Products(2)");
            odataResponseMessage.SetHeader("Location", "http://service/foo/Products(2)");
            odataResponseMessage.SetHeader("Content-Type", "application/atom+xml");

            DataServiceContextWithCustomTransportLayer context = new DataServiceContextWithCustomTransportLayer(DataServiceProtocolVersion.V3, () => odataRequestMessage, () => odataResponseMessage);

            WritingNavigationLinkArgs startingNavigLinkArgs = null;
            WritingNavigationLinkArgs endingNavigLinkArgs = null;
            WritingEntityReferenceLinkArgs args = null;
            context.Configurations.RequestPipeline.OnNavigationLinkStarting((r => startingNavigLinkArgs = r));
            context.Configurations.RequestPipeline.OnNavigationLinkEnding((r => endingNavigLinkArgs = r));
            context.Configurations.RequestPipeline.OnEntityReferenceLink((r => args = r));

            SimpleNorthwind.Product product = new SimpleNorthwind.Product() { ID = 1 };
            SimpleNorthwind.Supplier supplier = new SimpleNorthwind.Supplier() { ID = 2 };

            context.AddObject("Products", product);
            context.AttachTo("Suppliers", supplier);

            context.SetLink(product, "Supplier", supplier);
            context.SaveChanges();

            VerifyWritingNavigationLinkArgs(startingNavigLinkArgs, null, product, supplier, "Supplier");
            VerifyWritingNavigationLinkArgs(endingNavigLinkArgs, null, product, supplier, "Supplier");

            args.Should().NotBeNull();
            args.EntityReferenceLink.Url.AbsoluteUri.Should().Be("http://somedummyuri/myService.svc/Suppliers(2)");
        }
        public void BindTwoEntitiesSaveChanges()
        {
            var odataRequestMessage = new ODataTestMessage();
            var odataResponseMessage = new ODataTestMessage(){StatusCode = 204};
            DataServiceContextWithCustomTransportLayer context = new DataServiceContextWithCustomTransportLayer(DataServiceProtocolVersion.V3, () => odataRequestMessage, () => odataResponseMessage);

            WritingEntityReferenceLinkArgs args = null;
            context.Configurations.RequestPipeline.OnEntityReferenceLink((r => args = r));

            SimpleNorthwind.Product product = new SimpleNorthwind.Product() {ID = 1};
            SimpleNorthwind.Supplier supplier = new SimpleNorthwind.Supplier() {ID = 2};

            context.AttachTo("Products", product);
            context.AttachTo("Suppliers", supplier);

            context.SetLink(product, "Supplier", supplier);
            context.SaveChanges();

            // Note writing entity links is not triggered here as its not in the payload
            args.Should().BeNull();
            odataRequestMessage.Method.Should().Be("PUT");
            odataRequestMessage.Url.AbsoluteUri.Should().Be("http://somedummyuri/myService.svc/Products(1)/$links/Supplier");
        }
        private static void RunClientRequest(Action<DataServiceContext> runTest, string expectedRequestPayload = null, string responsePayload = null, Action<IODataResponseMessage> setupResponse = null)
        {
            using (var requestStream = new MemoryStream())
            using (var responseStream = responsePayload == null ? new MemoryStream() : new MemoryStream(Encoding.UTF8.GetBytes(responsePayload)))
            {
                responseStream.Position = 0;
                var requestMessage = new InMemoryMessage {Stream = requestStream};
                var responseMessage = new InMemoryMessage {Stream = responseStream};
                if (setupResponse != null)
                {
                    setupResponse(responseMessage);
                }

                DataServiceContext ctx = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, requestMessage, responseMessage);
                runTest(ctx);

                if (expectedRequestPayload != null)
                {
                    var actualRequestPayload = Encoding.UTF8.GetString(requestStream.ToArray());
                    Assert.AreEqual(expectedRequestPayload, actualRequestPayload);
                }
            }
        }
        private DataServiceContextWithCustomTransportLayer CreateTransportLayerContext(string payload, string odataVersion)
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();
            var responseMessage = new ODataTestMessage();
            responseMessage.SetHeader("Content-Type", "application/json");
            responseMessage.SetHeader("OData-Version", odataVersion);
            responseMessage.StatusCode = 200;
            responseMessage.WriteToStream(payload);
            responseMessage.SetHeader("Content-Length", responseMessage.MemoryStream.Length.ToString());

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, requestMessage, responseMessage);
            context.ResolveName = ResolveName;
            context.ResolveType = ResolveType;
            context.Format.UseJson(Model);
            return context;
        }
        public void NotModifiedTest()
        {
            var odataRequestMessage = new ODataTestMessage();
            var odataResponseMessage = new ODataTestMessage()
            {
                StatusCode = 304,
                MemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(@""))
            };
            odataResponseMessage.SetHeader("Content-Type", "0");

            DataServiceContextWithCustomTransportLayer context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => odataRequestMessage, () => odataResponseMessage);

            Action test = () => context.CreateQuery<SimpleNorthwind.Product>("Products").ToList();
            test.ShouldThrow<DataServiceQueryException>().WithInnerMessage("NotModified");
        }
        private static void SendRequestAndVerifyUriAndContext(DSPServiceDefinition service,
            Func<IEnumerable<Tuple<Func<DataServiceContext, IQueryable>[], Func<DataServiceContext, string>, Action<DataServiceContext, IQueryable, List<object>>>>> queryUrisAndValidations)
        {
            using (InProcessWebRequest request = (InProcessWebRequest)service.CreateForInProcess())
            {
                request.StartService();
                ODataProtocolVersion[] versions = new ODataProtocolVersion[] { ODataProtocolVersion.V4 };
                t.TestUtil.RunCombinations(queryUrisAndValidations(), versions, (testCase, version) =>
                {
                    var getQueries = testCase.Item1;
                    var getExpectedUri = testCase.Item2;
                    var validate = testCase.Item3;
                    foreach (var getQuery in getQueries)
                    {
                        var host = new t.TestServiceHost2();
                        var requestMessage = new DataServiceHostRequestMessage(host);
                        var ctx = new DataServiceContextWithCustomTransportLayer(request.ServiceRoot, version, () => requestMessage, () =>
                        {
                            request.SendRequest(host);
                            return new DataServiceHostResponseMessage(host);
                        });
                        ctx.EnableAtom = true;
                        ctx.Format.UseAtom();

                        if (version < ODataProtocolVersion.V4)
                        {
                            var query = getQuery(ctx);
                            try
                            {
                                foreach (var p in query) { }
                                Assert.Fail("Exception expected but received none.");
                            }
                            catch (NotSupportedException e)
                            {
                                Assert.IsTrue(
                                    "The expression 'TypeAs' is not supported when MaxProtocolVersion is less than '4.0'." == e.Message ||
                                    "The method 'OfType' is not supported when MaxProtocolVersion is less than '4.0'." == e.Message);
                            }
                        }
                        else
                        {
                            #region Setup resolvers and events

                            ctx.ResolveType = (typeName) =>
                            {
                                if (typeName == typeof(Employee).FullName)
                                {
                                    return typeof(Employee);
                                }

                                if (typeName == typeof(PeopleManager).FullName)
                                {
                                    return typeof(PeopleManager);
                                }

                                return null;
                            };

                            ctx.SendingRequest2 += (sender, e) =>
                            {
                                Assert.AreEqual("4.0", e.RequestMessage.GetHeader("OData-Version"), "OData-Version mismatch.");
                                Assert.AreEqual("4.0", e.RequestMessage.GetHeader("OData-MaxVersion"), "OData-MaxVersion mismatch.");
                            };

                            #endregion Setup resolvers and events

                            // Validate Uri
                            var query = getQuery(ctx);
                            string clientGeneratedUri = query.ToString();
                            Assert.AreEqual(getExpectedUri(ctx), clientGeneratedUri);

                            #region Validate entities

                            List<object> materializedObjects = new List<object>();

                            // Materialize and validate LinkInfos
                            foreach (var e in query)
                            {
                                EntityDescriptor descriptor = ctx.GetEntityDescriptor(e);
                                if (descriptor != null)
                                {
                                    foreach (var link in descriptor.LinkInfos)
                                    {
                                        switch (link.Name)
                                        {
                                            case "Aquaintances":
                                            case "DirectReports":
                                            case "Manager":
                                            case "Colleagues":
                                            case "BestFriend":
                                            case "Friends":
                                                // If the entity is not of the base type (Person), then expect navigation links to have a type segment.
                                                if (descriptor.ServerTypeName != "AstoriaUnitTests.Tests.DerivedProperty.Person")
                                                {
                                                    t.TestUtil.AssertContains(link.NavigationLink.OriginalString, descriptor.ServerTypeName);
                                                    if (link.AssociationLink != null)
                                                    {
                                                        t.TestUtil.AssertContains(link.AssociationLink.OriginalString, descriptor.ServerTypeName);
                                                    }
                                                }
                                                break;
                                            default:
                                                Assert.Fail("Unexpected link: " + link.Name);
                                                return;
                                        }
                                    }
                                }

                                materializedObjects.Add(e);
                            }

                            #endregion Validate entities

                            #region Validate Links

                            // Validate LinkDescriptors
                            foreach (LinkDescriptor link in ctx.Links)
                            {
                                string identity = ctx.GetEntityDescriptor(link.Source).Identity.AbsoluteUri;
                                int startIdx = identity.IndexOf('(') + 1;
                                int endIdx = identity.IndexOf(')');
                                int sourceId = int.Parse(identity.Substring(startIdx, endIdx - startIdx));

                                identity = ctx.GetEntityDescriptor(link.Target).Identity.AbsoluteUri;
                                startIdx = identity.IndexOf('(') + 1;
                                endIdx = identity.IndexOf(')');
                                int targetId = int.Parse(identity.Substring(startIdx, endIdx - startIdx));

                                switch (link.SourceProperty)
                                {
                                    case "DirectReports":
                                        switch (sourceId)
                                        {
                                            case 1: //"Foo":
                                                Assert.Fail("DirectReports link not expected for Foo");
                                                break;
                                            case 2: //"Andy":
                                                //Assert.IsTrue(targetName == "Pratik" || targetName == "Jimmy");
                                                Assert.IsTrue(targetId == 3 || targetId == 4);
                                                break;
                                            case 3: //"Pratik":
                                                Assert.Fail("DirectReports link not expected for Pratik");
                                                break;
                                            case 4: //"Jimmy":
                                                Assert.Fail("DirectReports link not expected for Jimmy");
                                                break;
                                            case 5: //"Shyam":
                                                //Assert.IsTrue(targetName == "Andy" || targetName == "Marcelo");
                                                Assert.IsTrue(targetId == 2 || targetId == 6);
                                                break;
                                            case 6: //"Marcelo":
                                                Assert.Fail("DirectReports link not expected for Marcelo");
                                                break;
                                            default:
                                                Assert.Fail("Unrecognized id: " + sourceId);
                                                break;
                                        }
                                        break;
                                    case "Manager":
                                        switch (sourceId)
                                        {
                                            case 1: //"Foo":
                                                Assert.Fail("Manager link not expected for Foo");
                                                break;
                                            case 2: //"Andy":
                                                //Assert.AreEqual("Shyam", targetName);
                                                Assert.AreEqual(5, targetId);
                                                break;
                                            case 3: //"Pratik":
                                                //Assert.AreEqual("Andy", targetName);
                                                Assert.AreEqual(2, targetId);
                                                break;
                                            case 4: //"Jimmy":
                                                //Assert.AreEqual("Andy", targetName);
                                                Assert.AreEqual(2, targetId);
                                                break;
                                            case 5: //"Shyam":
                                                //Assert.AreEqual("Shyam", targetName);
                                                Assert.AreEqual(5, targetId);
                                                break;
                                            case 6: //"Marcelo":
                                                //Assert.AreEqual("Shyam", targetName);
                                                Assert.AreEqual(5, targetId);
                                                break;
                                            default:
                                                Assert.Fail("Unrecognized id: " + sourceId);
                                                break;
                                        }
                                        break;
                                    case "Colleagues":
                                        switch (sourceId)
                                        {
                                            case 1: //"Foo":
                                                Assert.Fail("Colleagues link not expected for Foo");
                                                break;
                                            case 2: //"Andy":
                                                //Assert.AreEqual("Marcelo", targetName);
                                                Assert.AreEqual(6, targetId);
                                                break;
                                            case 3: //"Pratik":
                                                //Assert.AreEqual("Jimmy", targetName);
                                                Assert.AreEqual(4, targetId);
                                                break;
                                            case 4: //"Jimmy":
                                                //Assert.AreEqual("Pratik", targetName);
                                                Assert.AreEqual(3, targetId);
                                                break;
                                            case 5: //"Shyam":
                                                Assert.Fail("Colleagues link not expected for Shyam");
                                                break;
                                            case 6: //"Marcelo":
                                                //Assert.AreEqual("Andy", targetName);
                                                Assert.AreEqual(2, targetId);
                                                break;
                                            default:
                                                Assert.Fail("Unrecognized id: " + sourceId);
                                                break;
                                        }
                                        break;
                                    case "BestFriend":
                                        switch (sourceId)
                                        {
                                            case 1: //"Foo":
                                                //Assert.AreEqual("Pratik", targetName);
                                                Assert.AreEqual(3, targetId);
                                                break;
                                            case 2: //"Andy":
                                                //Assert.AreEqual("Shyam", targetName);
                                                Assert.AreEqual(5, targetId);
                                                break;
                                            case 3: //"Pratik":
                                                //Assert.AreEqual("Andy", targetName);
                                                Assert.AreEqual(2, targetId);
                                                break;
                                            case 4: //"Jimmy":
                                                //Assert.AreEqual("Foo", targetName);
                                                Assert.AreEqual(1, targetId);
                                                break;
                                            case 5: //"Shyam":
                                                //Assert.AreEqual("Marcelo", targetName);
                                                Assert.AreEqual(6, targetId);
                                                break;
                                            case 6: //"Marcelo":
                                                //Assert.AreEqual("Jimmy", targetName);
                                                Assert.AreEqual(4, targetId);
                                                break;
                                            default:
                                                Assert.Fail("Unrecognized id: " + sourceId);
                                                break;
                                        }
                                        break;
                                    case "Friends":
                                        break;
                                    default:
                                        Assert.Fail("Unexpected link descriptor: " + link.SourceProperty);
                                        return;
                                }
                            }

                            #endregion Validate Links

                            // Validation specific to the test case.
                            validate(ctx, query, materializedObjects);
                        }
                    }
                });
            }
        }
        public void WebExceptionShouldeSurfacedWhenGetResponseThrowsOnNonBatch()
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => requestMessage, () => { throw new WebException("web exception on getting response"); });
            Action test = () => context.CreateQuery<NorthwindModel.Products>("Products").ToList();

            var exception = test.ShouldThrow<Exception>().And;

            // Should not be surfacing an internal exception
            exception.Should().BeOfType<WebException>();
            exception.Message.Should().Be("web exception on getting response");
        }
        private static Action GetQueryWithInjectedObjectDisposedOnGetStream(bool useExecuteBatch)
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();
            var responseMessage = CreateResponseMessageWithGetStreamThrowingObjectDisposeException();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, requestMessage, responseMessage);
            if (useExecuteBatch)
            {
                return () => context.ExecuteBatch(context.CreateQuery<NorthwindModel.Products>("Products"));
            }
            else
            {
                return () => context.CreateQuery<NorthwindModel.Products>("Products").ToList();
            }
        }
        private static Action AddObjectWithInjectedObjectDisposedOnGetStreamO(SaveChangesOptions saveChangesOptions)
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();
            var responseMessage = CreateResponseMessageWithGetStreamThrowingObjectDisposeException();

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, requestMessage, responseMessage);
            context.AddObject("Products", new SimpleNorthwind.Product() { ID = 1 });
            return  () => context.SaveChanges(saveChangesOptions);
        }
        private static DataServiceQueryException TopLevelErrorPayloadWithTransportException(int statusCode, Action<DataServiceContextWithCustomTransportLayer> contextAction)
        {
            IODataRequestMessage requestMessage = new ODataTestMessage();

            TopLevelBatchResponseMessage topLevelResponse = new TopLevelBatchResponseMessage();
            byte[] payload = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n<error xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">\r\n  <code></code>\r\n  <message xml:lang=\"en-US\">This error must show up in the error returned below</message>\r\n</error>");
            topLevelResponse.SetHeader("OData-Version", "4.0");
            topLevelResponse.SetHeader("Content-Type", "application/xml");
            topLevelResponse.SetHeader("Content-Length", payload.Length.ToString());
            topLevelResponse.StatusCode = statusCode;
            topLevelResponse.GetStreamFunc = () =>
            {
                return new MemoryStream(payload);
            };

            var context = new DataServiceContextWithCustomTransportLayer(ODataProtocolVersion.V4, () => requestMessage, () => { throw new DataServiceTransportException(topLevelResponse, new WebException()); });
            context.EnableAtom = true;
            Action test = () => contextAction(context);

            var exception = test.ShouldThrow<DataServiceQueryException>().And;
            return exception;
        }