private static void TestSupportedLinqQueries(Func <DataServiceContext, LinqTestCase[]> getTests) { PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition(); using (TestWebRequest request = playbackService.CreateForInProcessWcf()) { request.StartService(); DataServiceContext context = new DataServiceContext(request.ServiceRoot, ODataProtocolVersion.V4); //context.EnableAtom = true; context.MergeOption = MergeOption.NoTracking; var tests = getTests(context); for (int i = 0; i < tests.Length; i++) { playbackService.OverridingPlayback = tests[i].ServerPayload; VerifyURI(context, tests[i].Query, tests[i].ExpectedUri, tests[i].ExpectedResults, tests[i].ExpectKeyInUri); } } }
[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."); } } }); }
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); } }
public void OperationDescriptorsShouldBeUpdatedForEachRequest() { #region Response payload var response = @"HTTP/1.1 200 OK Connection: Keep-Alive Content-Length: 2011 Date: Thu, 06 Jun 2013 22:07:07 GMT Content-Type: application/atom+xml;type=entry;charset=utf-8 Server: Microsoft-IIS/7.5 Cache-Control: no-cache X-Content-Type-Options: nosniff OData-Version: 4.0; X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET <?xml version='1.0' encoding='utf-8'?> <entry xml:base='http://services.odata.org/V3/(S(imdxx1ujw4ludze1t3k2wmgs))/OData/OData.svc/' xmlns='http://www.w3.org/2005/Atom' xmlns:d='http://docs.oasis-open.org/odata/ns/data' xmlns:m='http://docs.oasis-open.org/odata/ns/metadata' xmlns:georss='http://www.georss.org/georss' xmlns:gml='http://www.opengis.net/gml'> <id>http://services.odata.org/V3/(S(imdxx1ujw4ludze1t3k2wmgs))/OData/OData.svc/Products(0)</id> <category term='ODataDemo.Product' scheme='http://docs.oasis-open.org/odata/ns/scheme' /> <link rel='edit' title='Product' href='Products(0)' /> <link rel='http://docs.oasis-open.org/odata/ns/related/Category' type='application/atom+xml;type=entry' title='Category' href='Products(0)/Category' /> <link rel='http://docs.oasis-open.org/odata/ns/related/Supplier' type='application/atom+xml;type=entry' title='Supplier' href='Products(0)/Supplier' /> <title type='text'>Bread</title> <summary type='text'>Whole grain bread</summary> <updated>2013-06-06T22:07:08Z</updated> <author> <name /> </author> <link rel='http://docs.oasis-open.org/odata/ns/relatedlinks/OrderDetails' type='application/xml' title='OrderDetails' href='Products(0)/OrderDetails/$ref' /> <m:action metadata='http://services.odata.org/V3/(S(imdxx1ujw4ludze1t3k2wmgs))/OData/OData.svc/$metadata#DemoService.{0}' title='{0}' target='http://services.odata.org/V3/(S(imdxx1ujw4ludze1t3k2wmgs))/OData/OData.svc/Products(0)/{0}' /> <content type='application/xml'> <m:properties> <d:ID m:type='Edm.Int32'>0</d:ID> <d:ProductName m:type='Edm.String'>Demo Product</d:ProductName> <d:Discontinued m:type='Edm.Boolean'>false</d:Discontinued> </m:properties> </content> </entry> "; #endregion // Consider two actions that an entity Product supports, and they are mutually exclusive, i.e. only one action // is returned in the response string action1 = "Discount"; string action2 = "Discount1234"; // Create two responses with each of above actions var response1 = string.Format(response, action1); var response2 = string.Format(response, action2); PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition(); using (TestWebRequest request = playbackService.CreateForInProcessWcf()) { request.ServiceType = typeof(PlaybackService); request.ForceVerboseErrors = true; request.StartService(); DataServiceContext ctx = new DataServiceContext(request.ServiceRoot, ODataProtocolVersion.V4); ctx.EnableAtom = true; try { for (int i = 0; i < 2; i++) { playbackService.OverridingPlayback = (i % 2 == 0) ? response1 : response2; Product product = ctx.Execute <Product>(new Uri("Products(0)", UriKind.Relative)).First(); EntityDescriptor descriptor = ctx.GetEntityDescriptor(product); descriptor.OperationDescriptors.Count.Should().Be(1); var actionName = descriptor.OperationDescriptors.First().Title; if (i % 2 == 0) { actionName.Should().Be(action1); } else { actionName.Should().Be(action2); } } } finally { request.StopService(); } } }
public void AtomParserAttributeNamespacePrecedenceTest() { const string atomPayload = @"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 term='NamespaceName.Invalid' scheme='http://docs.oasis-open.org/odata/ns/data/unsupported' atom:term='NamespaceName.EntityA' atom:scheme='http://docs.oasis-open.org/odata/ns/scheme'/> <content type='application/invalid' atom:type='application/xml' src='http://odata.org/invalid' atom:src='http://odata.org/readstream1'/> <m:properties> <d:ID type='Edm.Invalid' m:type='Edm:Int32'>1</d:ID> </m:properties> </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, ODataProtocolVersion.V4); ctx.EnableAtom = true; playbackService.OverridingPlayback = atomPayload; EntityA[] items; try { items = ctx.CreateQuery <EntityA>("ASet").ToArray(); } catch (InvalidOperationException ex) { 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 that the atom:scheme and atom:term attribute are ignored // Verify that since the scheme attribute value is wrong, the term attribute value is not read Assert.AreEqual(null, 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/invalid", 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."); } }
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."); }); } }
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."); } }); } }
public void AtomParserLinkRelTest() { 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='{0}' title='EntityA' href='ASet(1)' /> <link rel='{1}' title='EntityA' href='ASet(1)' /> <link rel='{2}' title='EntityA' href='ASet(1)/$value' /> <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>"; string[] selfLinkRefs = { "self", "http://www.iana.org/assignments/relation/self" }; string[] editLinkRefs = { "edit", "http://www.iana.org/assignments/relation/edit" }; string[] editMediaLinkRefs = { "edit-media", "http://www.iana.org/assignments/relation/edit-media" }; PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition(); using (TestWebRequest request = playbackService.CreateForInProcessWcf()) { request.ServiceType = typeof(PlaybackService); request.ForceVerboseErrors = true; request.StartService(); TestUtil.RunCombinations(selfLinkRefs, editLinkRefs, editMediaLinkRefs, (self, edit, editMedia) => { DataServiceContext ctx = new DataServiceContext(request.ServiceRoot); ctx.EnableAtom = true; string atomPayload = string.Format(atomPayloadTemplate, self, edit, editMedia); playbackService.OverridingPlayback = atomPayload; foreach (var item in ctx.CreateQuery <EntityA>("ASet")) { Assert.IsNotNull(item, "item"); } EntityDescriptor entity = ctx.Entities.Single(); Assert.AreEqual(ctx.BaseUri + "/ASet(1)", entity.SelfLink.OriginalString); Assert.AreEqual(ctx.BaseUri + "/ASet(1)", entity.EditLink.OriginalString); Assert.AreEqual(ctx.BaseUri + "/ASet(1)/$value", entity.ReadStreamUri.OriginalString); Assert.AreEqual(ctx.BaseUri + "/ASet(1)/$value", entity.EditStreamUri.OriginalString); }); } }
public void AtomParserDuplicateTest_AfterODataLibIntegration() { // BUG: // The following breaking changes are taken in WCF DS client. Once "ODataLib - WCF DS client" integration is complete, enable the asserts in this test. // Current ODataLib behavior for WCF DS client is the following. // (a) Read the first entry/category element with a valid scheme even if it does not have a term attribute. // (b) Throw if there are duplicate entry/link/m:inline elements // (c) For m:properties: // For entry/content/m:properties, the first m:properties in each of the entry/content elements is taken and merged. // For entry/m:properties, all duplicates are read and merged. // (d) Relationship links are V3 feature. So, we will change WCF DS client do disallow duplicates. // (e) Named streams are V3 feature. If we see two different mime types when processing two named streams with the same name but are read and edit links respectively, we will throw. // (f) Named streams are V3 feature. If we see named stream duplicates, we will throw. 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'> <m:count>3</m:count> <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(2)</id> <id>http://host/ASet(1)</id> <title type='text'></title> <updated>2010-09-01T23:36:00Z</updated> <author> <name /> </author> {0} </entry> </feed>"; var tests = new[] { new { Type = "AssociationLinks", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/relatedlinks/NavProp' href='http://odata.org/link1' type='application/xml'/> <link rel='http://docs.oasis-open.org/odata/ns/relatedlinks/NavProp' href='http://odata.org/link2' type='application/xml'/>" }, new { Type = "NavigationLinks", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/related/RelProp' href='http://odata.org/link3' type='application/atom+xml;type=entry'/> <link rel='http://docs.oasis-open.org/odata/ns/related/RelProp' href='http://odata.org/link4' type='application/atom+xml;type=entry'/>" }, new { // mime types differ for the same stream. This is not allowed. Type = "NamedStreams", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/mediaresource/NamedStream' href='http://odata.org/readlink' type='mime/type1'/> <link rel='http://docs.oasis-open.org/odata/ns/edit-media/NamedStream' href='http://odata.org/editlink' type='mime/type2'/>" }, new { Type = "MediaResourceStreams", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/mediaresource/MediaResourceStreamDup' href='http://odata.org/readlink1' type='mime/type3'/> <link rel='http://docs.oasis-open.org/odata/ns/mediaresource/MediaResourceStreamDup' href='http://odata.org/readlink2' type='mime/type4'/>" }, new { Type = "EditMedialinks", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/edit-media/EditMediaStreamDup' href='http://odata.org/editlink1' type='mime/type5'/> <link rel='http://docs.oasis-open.org/odata/ns/edit-media/EditMediaStreamDup' href='http://odata.org/editlink2' type='mime/type6'/>" }, new { Type = "Content", Payload = @" <content type='application/xml' > <m:properties> <d:ID m:type='Edm.Int32'>1</d:ID> </m:properties> </content> <content src='http://odata.org'/>" }, new { Type = "Properties", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/related/B' type='application/atom+xml;type=entry' title='B' href='Customer(1)/B'> <m:inline> <entry> <id>http://host/BSet(1)</id> <content type='application/xml' > <m:properties> <d:ID m:type='Edm.Int32'>1</d:ID> <d:Prop1 m:type='Edm.Int32'>1</d:Prop1> </m:properties> <m:properties> <d:Prop1 m:type='Edm.Int32'>2</d:Prop1> </m:properties> </content> <content type='application/xml' > <m:properties> <d:ID m:type='Edm.Int32'>2</d:ID> <d:Prop1 m:type='Edm.Int32'>3</d:Prop1> </m:properties> <m:properties> <d:Prop1 m:type='Edm.Int32'>4</d:Prop1> </m:properties> </content> </entry> </m:inline> </link> <m:properties> <d:ID m:type='Edm.Int32'>1</d:ID> <d:Prop1 m:type='Edm.Int32'>1</d:Prop1> </m:properties> <m:properties> <d:Prop1 m:type='Edm.Int32'>2</d:Prop1> </m:properties>" }, new { Type = "TypeScheme", Payload = @" <category scheme='http://docs.oasis-open.org/odata/ns/scheme' /> <category scheme='http://docs.oasis-open.org/odata/ns/scheme' term='NamespaceName.EntityB' />" }, new { Type = "InlineElements", Payload = @" <link rel='http://docs.oasis-open.org/odata/ns/related/B' type='application/atom+xml;type=entry' title='B' href='Customer(1)/B'> <m:inline> <entry> <id>http://host/BSet(2)</id> <content type='application/xml' > <m:properties> <d:ID m:type='Edm.Int32'>2</d:ID> <d:Prop1 m:type='Edm.Int32'>5</d:Prop1> </m:properties> <m:properties> <d:Prop1 m:type='Edm.Int32'>6</d:Prop1> </m:properties> </content> </entry> </m:inline> <m:inline/> </link>" }, }; string[] shouldFailTests = new string[] { "AssociationLinks", "NamedStreams", "Content", "MediaResourceStreams", "EditMedialinks", "InlineElements", "Properties" }; string[] shouldPassTests = new string[] { "NavigationLinks", "TypeScheme" }; PlaybackServiceDefinition playbackService = new PlaybackServiceDefinition(); using (TestWebRequest request = playbackService.CreateForInProcessWcf()) { request.ServiceType = typeof(PlaybackService); request.ForceVerboseErrors = true; request.StartService(); TestUtil.RunCombinations(tests, (test) => { DataServiceContext ctx = new DataServiceContext(request.ServiceRoot, ODataProtocolVersion.V4); ctx.EnableAtom = true; playbackService.OverridingPlayback = string.Format(atomPayloadTemplate, test.Payload); EntityA entity = null; Exception ex = TestUtil.RunCatching(() => entity = ctx.CreateQuery <EntityA>("ASet").FirstOrDefault()); if (shouldFailTests.Contains(test.Type)) { Assert.IsNotNull(ex, test.Type + " expects an exception but got none."); } else { Assert.IsNull(ex, test.Type + " expects to pass but got an exception: " + ex); } // Duplicate navigation links are allowed. if (test.Type == "NavigationLinks") { EntityDescriptor descriptor = ctx.Entities.First(); Assert.AreEqual(1, descriptor.LinkInfos.Count()); Assert.AreEqual("http://odata.org/link4", descriptor.LinkInfos[0].NavigationLink.AbsoluteUri); } // Read the first entry/category element with a valid scheme even if the term attribute is not present. if (test.Type == "TypeScheme") { EntityDescriptor descriptor = ctx.Entities.First(); Assert.AreEqual(null, descriptor.ServerTypeName); } }); } }