Ejemplo n.º 1
0
        public void SpatialOpenProperty_AtomWrongTypeDeserialize()
        {
            var testCases = testData.Select(kvp =>
                                            new
            {
                Type    = SpatialTestUtil.GeographyTypeFor(kvp.Key),
                WktData = new String[] { kvp.Value.Last() }
            });

            String[] edmCases = { "Edm.Double", "Edm.String" };

            TestUtil.RunCombinations(testCases, edmCases, (tcase, edmName) =>
            {
                using (TestWebRequest request = CreateSpatialPropertyService(new Geography[tcase.WktData.Length], tcase.Type, true, true).CreateForInProcess())
                {
                    request.RequestUriString   = "/Entities";
                    request.HttpMethod         = "POST";
                    request.RequestContentType = UnitTestsUtil.AtomFormat;
                    request.SetRequestStreamAsText(AggregateAtomPayloadFromWkt(tcase.Type.Name, tcase.WktData, edmName));
                    Exception ex = TestUtil.RunCatching(request.SendRequest);
                    Assert.IsNotNull(ex);
                    while (ex.InnerException != null)
                    {
                        ex = ex.InnerException;
                    }
                    Assert.AreEqual(ODataLibResourceUtil.GetString("XmlReaderExtension_InvalidNodeInStringValue", "Element"), ex.Message);
                }
            });
        }
Ejemplo n.º 2
0
        public void RequestShouldFailIfTooManyItemsAreExpandedAtMultipleLevels()
        {
            string errorMessage = ODataLibResourceUtil.GetString("UriParser_ExpandCountExceeded", 4, 3);

            ResponseShouldMatchXPath(
                "/Entities?$expand=Reference($expand=Reference2),Reference3($expand=Reference4)",
                400,
                "//adsm:error/adsm:message[text()='" + errorMessage + "']", serviceType: typeof(UriParserIntegrationTestServiceWithLowLimits));
        }
Ejemplo n.º 3
0
        public void RequestShouldFailIfExpandIsTooDeep()
        {
            string errorMessage = ODataLibResourceUtil.GetString("UriParser_ExpandDepthExceeded", 3, 2);

            ResponseShouldMatchXPath(
                "/Entities?$expand=Reference($expand=Reference($expand=Reference))",
                400,
                "//adsm:error/adsm:message[text()='" + errorMessage + "']", serviceType: typeof(UriParserIntegrationTestServiceWithLowLimits));
        }
Ejemplo n.º 4
0
        public void TestEqualityHandling()
        {
            string geographyLiteral = GetUriLiteral(typeof(GeographyPoint),
                                                    TestPoint.DefaultValues.TripLegGeography1.AsGeography());
            string geometryLiteral = GetUriLiteral(typeof(GeometryPoint),
                                                   TestPoint.DefaultValues.TripLegGeography1.AsGeography());
            var testCases = new[]
            {
                new
                {
                    Type         = typeof(GeographyPoint),
                    Left         = "GeographyProperty1",
                    Right        = geographyLiteral,
                    Operator     = "eq",
                    OperatorName = "Equal"
                },
                new
                {
                    Type         = typeof(GeographyPoint),
                    Left         = geographyLiteral,
                    Right        = "GeographyProperty1",
                    Operator     = "eq",
                    OperatorName = "Equal"
                },
                new
                {
                    Type         = typeof(GeometryPoint),
                    Left         = "GeographyProperty1",
                    Right        = geometryLiteral,
                    Operator     = "eq",
                    OperatorName = "Equal"
                },
                new
                {
                    Type         = typeof(GeometryPoint),
                    Left         = geometryLiteral,
                    Right        = "GeographyProperty1",
                    Operator     = "eq",
                    OperatorName = "Equal"
                },
            };

            TestUtil.RunCombinations(testCases,
                                     (testCase) =>
            {
                DSPUnitTestServiceDefinition roadTripServiceDefinition = GetRoadTripServiceDefinition(testCase.Type, TestPoint.DefaultValues);
                using (TestWebRequest request = roadTripServiceDefinition.CreateForInProcess())
                {
                    request.StartService();
                    // GET
                    request.Accept             = UnitTestsUtil.MimeAny;
                    request.RequestContentType = null;
                    request.HttpMethod         = "GET";
                    request.RequestUriString   = string.Format("/TripLegs/?$filter={0} {1} {2}", testCase.Left, testCase.Operator, testCase.Right);
                    int operatorPos            = testCase.Left.Length + 1;
                    WebException e             = TestUtil.RunCatching <WebException>(() => request.SendRequest());
                    Assert.IsNotNull(e, "didn't get the exception we should have gotten");
                    string expectedMessage = ODataLibResourceUtil.GetString(
                        "MetadataBinder_IncompatibleOperandsError",
                        "Edm." + testCase.Type.Name,
                        "Edm." + testCase.Type.Name,
                        testCase.OperatorName,
                        operatorPos);
                    Assert.AreEqual(expectedMessage, e.InnerException.Message, "didn't get the correct error");
                }
            });
        }
Ejemplo n.º 5
0
            [Ignore] // Remove Atom
            // [TestCategory("Partition2"), TestMethod, Variation]
            public void BatchContentTypeTest()
            {
                var testCases = new BatchContentTypeTestCase[]
                {
                    // Completely wrong content type
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "text/plain",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = DataServicesClientResourceUtil.GetString("Batch_ExpectedContentType", "text/plain")
                    },
                    // Just type is correct, subtype is wrong
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/text",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = DataServicesClientResourceUtil.GetString("Batch_ExpectedContentType", "multipart/text")
                    },
                    // No boundary - still wrong
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/mixed",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed", "boundary")
                    },
                    // Some other parameter but no boundary
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/mixed;param=value",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed;param=value", "boundary")
                    },
                    // Empty boundary - fails
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/mixed;boundary=",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("ValidationUtils_InvalidBatchBoundaryDelimiterLength", string.Empty, "70")
                    },
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/mixed;boundary=;param=value",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("ValidationUtils_InvalidBatchBoundaryDelimiterLength", string.Empty, "70")
                    },
                    // Two boundary parameters - wrong
                    new BatchContentTypeTestCase
                    {
                        ContentType                = "multipart/mixed;boundary=one;boundary=two",
                        ExpectedErrorStatusCode    = 400,
                        ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed;boundary=one;boundary=two", "boundary")
                    },
                    // Valid simple boundary
                    new BatchContentTypeTestCase
                    {
                        ContentType          = "multipart/mixed;boundary=batchboundary",
                        PayloadBatchBoundary = "batchboundary"
                    },
                    // Valid simple boundary - mimetype using different casing
                    new BatchContentTypeTestCase
                    {
                        ContentType          = "MultiPart/mIxed;boundary=batchboundary",
                        PayloadBatchBoundary = "batchboundary"
                    },
                    // Valid simple boundary - boundary parameter name different casing
                    new BatchContentTypeTestCase
                    {
                        ContentType          = "multipart/mixed;BounDary=batchboundary",
                        PayloadBatchBoundary = "batchboundary"
                    },
                };

                OpenWebDataServiceDefinition serverService = new OpenWebDataServiceDefinition()
                {
                    DataServiceType = typeof(CustomDataContext)
                };

                PlaybackServiceDefinition clientService = new PlaybackServiceDefinition();

                TestUtil.RunCombinations(
                    testCases,
                    (testCase) =>
                {
                    using (TestWebRequest request = serverService.CreateForInProcess())
                    {
                        request.RequestContentType = testCase.ContentType;
                        request.SetRequestStreamAsText(string.Format(
                                                           "--{0}\r\n" +
                                                           "Content-Type: multipart/mixed; boundary=changesetresponse_00000001-0000-0000-0000-000000000000\r\n\r\n" +
                                                           "--changesetresponse_00000001-0000-0000-0000-000000000000\r\n" +
                                                           "--changesetresponse_00000001-0000-0000-0000-000000000000--\r\n" +
                                                           "--{0}--\r\n", testCase.PayloadBatchBoundary));
                        request.RequestUriString = "/$batch";
                        request.HttpMethod       = "POST";

                        Exception exception = TestUtil.RunCatching(request.SendRequest);

                        int actualStatusCode = 0;
                        if (exception != null)
                        {
                            actualStatusCode = request.ResponseStatusCode;
                        }
                        else
                        {
                            Assert.AreEqual(202, request.ResponseStatusCode, "Wrong response code for no-exception request.");
                            BatchWebRequest batchResponse = BatchWebRequest.FromResponse(InMemoryWebRequest.FromResponse(request));
                            if (batchResponse.Parts.Count > 0)
                            {
                                actualStatusCode = batchResponse.Parts[0].ResponseStatusCode;
                                if (actualStatusCode == 200)
                                {
                                    actualStatusCode = 0;
                                }
                            }
                        }

                        Assert.AreEqual(testCase.ExpectedErrorStatusCode, actualStatusCode, "Wrong status code.");
                    }

                    using (TestWebRequest request = clientService.CreateForInProcessWcf())
                    {
                        request.StartService();

                        clientService.ProcessRequestOverride = clientRequest =>
                        {
                            var clientResponse = new InMemoryWebRequest();
                            clientResponse.SetResponseStatusCode(202);
                            clientResponse.ResponseHeaders["Content-Type"] = testCase.ContentType;
                            clientResponse.SetResponseStreamAsText(string.Format(
                                                                       "--{0}\r\n" +
                                                                       "Content-Type: application/http\r\n" +
                                                                       "Content-Transfer-Encoding: binary\r\n" +
                                                                       "\r\n" +
                                                                       "200 OK\r\n" +
                                                                       "<feed xmlns='http://www.w3.org/2005/Atom'/>\r\n" +
                                                                       "--{0}--\r\n",
                                                                       testCase.PayloadBatchBoundary));
                            return(clientResponse);
                        };

                        DataServiceContext ctx = new DataServiceContext(request.ServiceRoot);
                        Exception exception    = TestUtil.RunCatching(() => ctx.ExecuteBatch(ctx.CreateQuery <Customer>("/Customers")));

                        if (exception != null)
                        {
                            exception = ((DataServiceRequestException)exception).InnerException;
                            Assert.AreEqual(testCase.ExpectedClientErrorMessage, exception.Message, "Unexpected error message.");
                        }
                        else
                        {
                            Assert.IsNull(testCase.ExpectedClientErrorMessage, "Expected exception, but none was thrown.");
                        }
                    }
                });
            }
Ejemplo n.º 6
0
        public void PrimitiveTypes_Generated_WithIncorrectNullability()
        {
            string orderPayloadWithNullableProperties = @"HTTP/1.1 200 OK
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Length: 2542
Via: 1.1 TK5-PRXY-08
Expires: Wed, 04 Jan 2012 23:39:02 GMT
Date: Wed, 04 Jan 2012 23:38:02 GMT
Content-Type: application/atom+xml;charset=utf-8
Server: Microsoft-IIS/7.5
Cache-Control: private
Vary: *
OData-Version: 4.0;
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<feed xml:base='http://services.odata.org/Northwind/Northwind.svc/' xmlns:d='http://docs.oasis-open.org/odata/ns/data' xmlns:m='http://docs.oasis-open.org/odata/ns/metadata' xmlns='http://www.w3.org/2005/Atom'>
  <title type='text'>OrderDetails</title>
  <id>http://services.odata.org/Northwind/Northwind.svc/OrderDetails</id>
  <updated>2012-01-04T23:32:26Z</updated>
  <link rel='self' title='OrderDetails' href='OrderDetails' />
  <entry>
    <id>http://services.odata.org/Northwind/Northwind.svc/OrderDetails(10248)</id>
    <title type='text'></title>
    <updated>2012-01-04T23:32:26Z</updated>
    <author>
      <name />
    </author>
    <link rel='edit' title='OrderDetail' href='OrderDetails(10248)' />
    <link rel='http://docs.oasis-open.org/odata/ns/related/Customer' type='application/atom+xml;type=entry' title='Customer' href='OrderDetails(10248)/Customer' />
    <link rel='http://docs.oasis-open.org/odata/ns/related/Employee' type='application/atom+xml;type=entry' title='Employee' href='OrderDetails(10248)/Employee' />
    <link rel='http://docs.oasis-open.org/odata/ns/related/OrderDetail_Details' type='application/atom+xml;type=feed' title='OrderDetail_Details' href='OrderDetails(10248)/OrderDetail_Details' />
    <link rel='http://docs.oasis-open.org/odata/ns/related/Shipper' type='application/atom+xml;type=entry' title='Shipper' href='OrderDetails(10248)/Shipper' />
    <category term='NorthwindModel.OrderDetail' scheme='http://docs.oasis-open.org/odata/ns/scheme' />
    <content type='application/xml'>
      <m:properties>
        <d:OrderID m:type='Edm.Int32'>10248</d:OrderID>
        <d:ProductID m:type='Edm.Int32'>10248</d:ProductID>
        <d:UnitPrice m:null='true'/>
        <d:Quantity m:null='true'/> 
      </m:properties>
    </content>
  </entry>
</feed>";


            PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition();

            using (TestWebRequest request = playbackService.CreateForInProcessWcf())
            {
                request.ServiceType        = typeof(PlaybackService);
                request.ForceVerboseErrors = true;
                request.StartService();
                DataServiceContext ctx = new DataServiceContext(request.ServiceRoot);
                ctx.EnableAtom = true;
                playbackService.OverridingPlayback = orderPayloadWithNullableProperties;
                InvalidOperationException nullAssignmentError = null;
                try
                {
                    foreach (var orderDetail in ctx.CreateQuery <OrderDetail>("OrderDetails"))
                    {
                    }
                }
                catch (InvalidOperationException ioException)
                {
                    nullAssignmentError = ioException;
                }
                finally
                {
                    request.StopService();
                }

                Assert.IsNotNull(nullAssignmentError, "Client library should not allow assignment of null values to non-nullable properties");
                Assert.AreEqual(ODataLibResourceUtil.GetString("ReaderValidationUtils_NullNamedValueForNonNullableType", "UnitPrice", "Edm.Double"), nullAssignmentError.Message);
            }
        }
Ejemplo n.º 7
0
        public void AtomParserAttributeNamespaceTest()
        {
            const string atomPayloadTemplate =
                @"HTTP/1.1 200 OK
Content-Type: application/atom+xml

<feed xmlns:d='http://docs.oasis-open.org/odata/ns/data' 
      xmlns:m='http://docs.oasis-open.org/odata/ns/metadata' 
      xmlns:atom='http://www.w3.org/2005/Atom' 
      xmlns:invalid='http://odata.org/invalid/namespace' 
      xmlns:gml='http://www.opengis.net/gml'
      xmlns='http://www.w3.org/2005/Atom'>
  <title type='text'>ASet</title>
  <id>http://host/ASet</id>
  <updated>2010-09-01T23:36:00Z</updated>
  <link rel='self' title='ASet' href='ASet' />
  <entry>
    <id>http://host/ASet(1)</id>
    <title type='text'></title>
    <updated>2010-09-01T23:36:00Z</updated>
    <author>
      <name />
    </author>
    <link rel='self' title='EntityA' href='ASet(1)' />
    <link rel='edit' title='EntityA' href='ASet(1)' />
    <link rel='edit-media' title='EntityA' href='ASet(1)/$value' />
    <category {0}='NamespaceName.EntityA' {1}='http://docs.oasis-open.org/odata/ns/scheme' />
    <content {3}='http://odata.org/readstream1' />
    <m:properties>
      <d:ID {4}='Edm.Int32'>1</d:ID>
    </m:properties>
  </entry>
</feed>";

            const string defaultCategoryTerm   = "term";
            const string defaultCategoryScheme = "scheme";
            const string defaultContentType    = "type";
            const string defaultContentSource  = "src";
            const string defaultPropertyType   = "m:type";

            AtomParserAttributeNamespaceTestCase[] testCases = new AtomParserAttributeNamespaceTestCase[]
            {
                // Error cases
                new AtomParserAttributeNamespaceTestCase {
                    CategoryScheme = "invalid:scheme"
                },
                new AtomParserAttributeNamespaceTestCase {
                    CategoryTerm = "invalid:term"
                },
                new AtomParserAttributeNamespaceTestCase {
                    ContentType = "invalid:type"
                },
                new AtomParserAttributeNamespaceTestCase {
                    ContentSrc = "invalid:src"
                },
                new AtomParserAttributeNamespaceTestCase {
                    PropertyType = "invalid:type"
                },
            };

            PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition();

            using (TestWebRequest request = playbackService.CreateForInProcessWcf())
            {
                request.ServiceType        = typeof(PlaybackService);
                request.ForceVerboseErrors = true;
                request.StartService();

                TestUtil.RunCombinations(
                    testCases,
                    testCase =>
                {
                    DataServiceContext ctx = new DataServiceContext(request.ServiceRoot, ODataProtocolVersion.V4);
                    ctx.EnableAtom         = true;

                    // {0} - atom:category/@term
                    // {1} - atom:category/@scheme
                    // {2} - atom:content/@type
                    // {3} - atom:content/@src
                    // {4} - d:property/@m:type
                    string atomPayload = string.Format(
                        atomPayloadTemplate,
                        testCase.CategoryTerm ?? defaultCategoryTerm,
                        testCase.CategoryScheme ?? defaultCategoryScheme,
                        testCase.ContentType ?? defaultContentType,
                        testCase.ContentSrc ?? defaultContentSource,
                        testCase.PropertyType ?? defaultPropertyType);
                    playbackService.OverridingPlayback = atomPayload;


                    EntityA[] items;
                    try
                    {
                        items = ctx.CreateQuery <EntityA>("ASet").ToArray();
                    }
                    catch (InvalidOperationException ex)
                    {
                        if (testCase.ContentSrc != null && testCase.ContentSrc.StartsWith("invalid"))
                        {
                            Assert.AreEqual(ODataLibResourceUtil.GetString("ODataAtomReader_MediaLinkEntryMismatch"),
                                            ex.Message);
                            return;
                        }
                        else if (testCase.PropertyType != null && testCase.PropertyType.StartsWith("invalid"))
                        {
                            Assert.AreEqual(DataServicesClientResourceUtil.GetString("Deserialize_ExpectingSimpleValue"), ex.Message);
                            return;
                        }

                        Assert.Fail("Exception received but did not expect any failure.\n" + ex.Message);
                        return;
                    }

                    Assert.AreEqual(1, items.Length, "Expected 1 item in the payload.");
                    var firstItem = items[0];

                    var entities = ctx.Entities;
                    Assert.AreEqual(1, entities.Count, "Expected 1 entity in the context.");
                    var firstEntity = entities[0];

                    // Verify category/@term and category/@scheme by testing the type name
                    string expectedTypeName =
                        testCase.CategoryScheme != null && testCase.CategoryScheme.StartsWith("invalid") ||
                        testCase.CategoryTerm != null && testCase.CategoryTerm.StartsWith("invalid")
                            ? null
                            : "NamespaceName.EntityA";
                    Assert.AreEqual(expectedTypeName, firstEntity.ServerTypeName, "Type names don't match.");

                    // Verify content/@src by checking that the read stream of the entry is correct
                    Assert.AreEqual("http://odata.org/readstream1", firstEntity.ReadStreamUri.OriginalString, "ReadStreams don't match.");

                    // Verify content/@type by making sure the properties were read
                    Assert.AreEqual(1, firstItem.ID, "IDs don't match.");
                });
            }
        }
Ejemplo n.º 8
0
        public void AtomParserNavigationLinkTypeTest()
        {
            const string atomPayloadTemplate =
                @"HTTP/1.1 200 OK
Content-Type: application/atom+xml

<feed xmlns:d='http://docs.oasis-open.org/odata/ns/data' xmlns:m='http://docs.oasis-open.org/odata/ns/metadata' xmlns='http://www.w3.org/2005/Atom'>
  <title type='text'>ASet</title>
  <id>http://host/ASet</id>
  <updated>2010-09-01T23:36:00Z</updated>
  <link rel='self' title='ASet' href='ASet' />
  <entry>
    <id>http://host/ASet(1)</id>
    <title type='text'></title>
    <updated>2010-09-01T23:36:00Z</updated>
    <author>
      <name />
    </author>
    <link rel='http://docs.oasis-open.org/odata/ns/related/{0}' title='{0}' href='ASet' type='{1}'>{2}</link>
    <category term='NamespaceName.EntityA' scheme='http://docs.oasis-open.org/odata/ns/scheme' />
    <content type='CustomType/CustomSubType' src='ASet(1)/$value' />
    <m:properties>
      <d:ID m:type='Edm.Int32'>1</d:ID>
    </m:properties>
  </entry>
</feed>";

            var linkTypes = new[]
            {
                new
                {
                    LinkType             = "application/atom+xml;type=entry",
                    ExpectedErrorMessage = new Func <bool, bool?, string>((collection, expandedFeed) =>
                    {
                        // If the link type is entry:
                        // - For collection property, it must fail since the type must match the model
                        // - If the link is expanded the payload must match the model
                        if (collection && expandedFeed == false)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedEntryInFeedNavigationLink"));
                        }
                        if (collection && expandedFeed == true)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedFeedInEntryNavigationLink"));
                        }
                        if (collection)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_DeferredEntryInFeedNavigationLink"));
                        }
                        if (!collection && expandedFeed == true)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedFeedInEntryNavigationLink"));
                        }
                        return(null);
                    }),
                    RecognizedLinkType = true,
                },
                new
                {
                    LinkType             = "application/atom+xml;type=feed",
                    ExpectedErrorMessage = new Func <bool, bool?, string>((collection, expandedFeed) =>
                    {
                        // If the link type is feed:
                        // - If the link is not expanded the type must match the model
                        // - If the link is expanded the expanded payload must match the model as well.
                        if (!collection)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_FeedNavigationLinkForResourceReferenceProperty", "Singleton"));
                        }
                        if (collection && expandedFeed == false)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedEntryInFeedNavigationLink"));
                        }
                        return(null);
                    }),
                    RecognizedLinkType = true,
                },
                new
                {
                    LinkType = "application/atom+xml",
                    // Breaking change: we are not recognizing links even when type is missing
                    // If the link has no type, client doesn't recognize this as a nav. prop.
                    ExpectedErrorMessage = new Func <bool, bool?, string>((collection, expandedFeed) =>
                    {
                        if (!collection && expandedFeed == true)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedFeedInEntryNavigationLink"));
                        }
                        if (collection && expandedFeed == false)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedEntryInFeedNavigationLink"));
                        }
                        return(null);
                    }),
                    RecognizedLinkType = true,
                },
                new
                {
                    LinkType = "application/atom+xml;type=test",
                    // Breaking change: we are not recognizing links even when type is wrong
                    // If the link has wrong type, client doesn't recognize this as a nav. prop.
                    ExpectedErrorMessage = new Func <bool, bool?, string>((collection, expandedFeed) =>
                    {
                        if (!collection && expandedFeed == true)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedFeedInEntryNavigationLink"));
                        }
                        if (collection && expandedFeed == false)
                        {
                            return(ODataLibResourceUtil.GetString("ODataAtomReader_ExpandedEntryInFeedNavigationLink"));
                        }
                        return(null);
                    }),
                    RecognizedLinkType = true,
                },
            };

            string deferredLinkBody  = string.Empty;
            string expandedFeedBody  = "<m:inline><feed/></m:inline>";
            string expandedEntryBody = "<m:inline><entry><id>http://host/ASet</id></entry></m:inline>";

            PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition();

            using (TestWebRequest request = playbackService.CreateForInProcessWcf())
            {
                request.ServiceType        = typeof(PlaybackService);
                request.ForceVerboseErrors = true;
                request.StartService();

                TestUtil.RunCombinations(
                    new string[] { "Singleton", "Collection" },
                    new bool?[] { null, false, true },
                    linkTypes,
                    (propertyName, expandedFeed, linkType) =>
                {
                    DataServiceContext ctx = new DataServiceContext(request.ServiceRoot);
                    ctx.EnableAtom         = true;

                    string linkBody    = expandedFeed.HasValue ? (expandedFeed == true ? expandedFeedBody : expandedEntryBody) : deferredLinkBody;
                    string atomPayload = string.Format(atomPayloadTemplate, propertyName, linkType.LinkType, linkBody);
                    playbackService.OverridingPlayback = atomPayload;

                    Exception exception = TestUtil.RunCatching(() => ctx.CreateQuery <EntityWithNavigationProperties>("ASet").AsEnumerable().Count());

                    string expectedExceptionMessage = linkType.ExpectedErrorMessage(propertyName == "Collection", expandedFeed);

                    if (exception == null)
                    {
                        Assert.IsNull(expectedExceptionMessage, "The test case was expected to fail.");
                        EntityDescriptor entity = ctx.Entities.Single(e => e.Identity == new Uri("http://host/ASet(1)"));
                        if (linkType.RecognizedLinkType)
                        {
                            Assert.AreEqual(ctx.BaseUri + "/ASet", entity.LinkInfos.Single(li => li.Name == propertyName).NavigationLink.OriginalString);
                        }
                        else
                        {
                            Assert.AreEqual(0, entity.LinkInfos.Count, "The link should not have been recognized as a navigation link.");
                        }
                    }
                    else
                    {
                        Assert.IsNotNull(expectedExceptionMessage, "The test case was expected to succeed.");
                        Assert.AreEqual(expectedExceptionMessage, exception.Message, "Unexpected error.");
                    }
                });
            }
        }