PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/test/System.Web.Http.OData.Test/OData/Formatter/ODataFormatterTests.cs

http://aspnetwebstack.codeplex.com
C# | 536 lines | 419 code | 65 blank | 52 comment | 4 complexity | f0df41b373fe5c9a2d6e66d9e0de5ef8 MD5 | raw file
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Http;
  6. using System.Net.Http.Headers;
  7. using System.Web.Http.OData.Builder;
  8. using System.Web.Http.Tracing;
  9. using System.Xml.Linq;
  10. using Microsoft.Data.Edm;
  11. using Microsoft.TestCommon;
  12. using Moq;
  13. using Newtonsoft.Json.Linq;
  14. namespace System.Web.Http.OData.Formatter
  15. {
  16. public class ODataFormatterTests
  17. {
  18. private const string baseAddress = "http://localhost:8081/";
  19. [Theory]
  20. [InlineData(true)]
  21. [InlineData(false)]
  22. public void GetEntryInODataAtomFormat(bool tracingEnabled)
  23. {
  24. // Arrange
  25. using (HttpConfiguration configuration = CreateConfiguration(tracingEnabled))
  26. using (HttpServer host = new HttpServer(configuration))
  27. using (HttpClient client = new HttpClient(host))
  28. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("People(10)",
  29. ODataTestUtil.ApplicationAtomMediaTypeWithQuality))
  30. // Act
  31. using (HttpResponseMessage response = client.SendAsync(request).Result)
  32. {
  33. // Assert
  34. AssertODataVersion3AtomResponse(Resources.PersonEntryInAtom, response);
  35. }
  36. }
  37. [Theory]
  38. [InlineData(true)]
  39. [InlineData(false)]
  40. public void PostEntryInODataAtomFormat(bool tracingEnabled)
  41. {
  42. // Arrange
  43. using (HttpConfiguration configuration = CreateConfiguration(tracingEnabled))
  44. using (HttpServer host = new HttpServer(configuration))
  45. using (HttpClient client = new HttpClient(host))
  46. using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, baseAddress + "People"))
  47. {
  48. request.Content = new StringContent(Resources.PersonEntryInAtom);
  49. request.Content.Headers.ContentType = ODataTestUtil.ApplicationAtomMediaTypeWithQuality;
  50. // Act
  51. using (HttpResponseMessage response = client.SendAsync(request).Result)
  52. {
  53. // Assert
  54. AssertODataVersion3AtomResponse(Resources.PersonEntryInAtom, response, HttpStatusCode.Created);
  55. }
  56. }
  57. }
  58. [Fact]
  59. public void GetEntryInODataJsonVerboseFormat()
  60. {
  61. // Arrange
  62. using (HttpConfiguration configuration = CreateConfiguration())
  63. using (HttpServer host = new HttpServer(configuration))
  64. using (HttpClient client = new HttpClient(host))
  65. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("People(10)",
  66. MediaTypeWithQualityHeaderValue.Parse("application/json;odata=verbose")))
  67. // Act
  68. using (HttpResponseMessage response = client.SendAsync(request).Result)
  69. {
  70. // Assert
  71. AssertODataVersion3JsonResponse(Resources.PersonEntryInJsonVerbose, response);
  72. }
  73. }
  74. [Fact]
  75. public void GetEntryInODataJsonFullMetadataFormat()
  76. {
  77. // Arrange
  78. using (HttpConfiguration configuration = CreateConfiguration())
  79. using (HttpServer host = new HttpServer(configuration))
  80. using (HttpClient client = new HttpClient(host))
  81. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("People(10)",
  82. MediaTypeWithQualityHeaderValue.Parse("application/json;odata=fullmetadata")))
  83. // Act
  84. using (HttpResponseMessage response = client.SendAsync(request).Result)
  85. {
  86. // Assert
  87. AssertODataVersion3JsonResponse(Resources.PersonEntryInJsonFullMetadata, response);
  88. }
  89. }
  90. [Fact]
  91. public void GetEntry_UsesRouteModel_ForMultipleModels()
  92. {
  93. // Model 1 only has Name, Model 2 only has Age
  94. ODataModelBuilder builder1 = new ODataModelBuilder();
  95. var personType1 = builder1.Entity<FormatterPerson>().Property(p => p.Name);
  96. builder1.EntitySet<FormatterPerson>("People").HasIdLink(p => "link", false);
  97. var model1 = builder1.GetEdmModel();
  98. ODataModelBuilder builder2 = new ODataModelBuilder();
  99. builder2.Entity<FormatterPerson>().Property(p => p.Age);
  100. builder2.EntitySet<FormatterPerson>("People").HasIdLink(p => "link", false);
  101. var model2 = builder2.GetEdmModel();
  102. var config = new HttpConfiguration();
  103. config.Routes.MapODataRoute("OData1", "v1", model1);
  104. config.Routes.MapODataRoute("OData2", "v2", model2);
  105. using (HttpServer host = new HttpServer(config))
  106. using (HttpClient client = new HttpClient(host))
  107. {
  108. using (HttpResponseMessage response = client.GetAsync("http://localhost/v1/People(10)").Result)
  109. {
  110. Assert.True(response.IsSuccessStatusCode);
  111. JToken json = JToken.Parse(response.Content.ReadAsStringAsync().Result);
  112. // Model 1 has the Name property but not the Age property
  113. Assert.NotNull(json["Name"]);
  114. Assert.Null(json["Age"]);
  115. }
  116. using (HttpResponseMessage response = client.GetAsync("http://localhost/v2/People(10)").Result)
  117. {
  118. Assert.True(response.IsSuccessStatusCode);
  119. JToken json = JToken.Parse(response.Content.ReadAsStringAsync().Result);
  120. // Model 2 has the Age property but not the Name property
  121. Assert.Null(json["Name"]);
  122. Assert.NotNull(json["Age"]);
  123. }
  124. }
  125. }
  126. [Fact]
  127. public void GetFeedInODataJsonFullMetadataFormat()
  128. {
  129. // Arrange
  130. IEdmModel model = CreateModelForFullMetadata(sameLinksForIdAndEdit: false, sameLinksForEditAndRead: false);
  131. using (HttpConfiguration configuration = CreateConfiguration(model))
  132. using (HttpServer host = new HttpServer(configuration))
  133. using (HttpClient client = new HttpClient(host))
  134. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("MainEntity",
  135. MediaTypeWithQualityHeaderValue.Parse("application/json;odata=fullmetadata")))
  136. // Act
  137. using (HttpResponseMessage response = client.SendAsync(request).Result)
  138. {
  139. // Assert
  140. AssertODataVersion3JsonResponse(
  141. Resources.MainEntryFeedInJsonFullMetadata, response);
  142. }
  143. }
  144. [Fact]
  145. public void GetFeedInODataJsonNoMetadataFormat()
  146. {
  147. // Arrange
  148. IEdmModel model = CreateModelForFullMetadata(sameLinksForIdAndEdit: false, sameLinksForEditAndRead: false);
  149. using (HttpConfiguration configuration = CreateConfiguration(model))
  150. using (HttpServer host = new HttpServer(configuration))
  151. using (HttpClient client = new HttpClient(host))
  152. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("MainEntity",
  153. MediaTypeWithQualityHeaderValue.Parse("application/json;odata=nometadata")))
  154. // Act
  155. using (HttpResponseMessage response = client.SendAsync(request).Result)
  156. {
  157. // Assert
  158. AssertODataVersion3JsonResponse(Resources.MainEntryFeedInJsonNoMetadata, response);
  159. }
  160. }
  161. [Fact]
  162. public void SupportOnlyODataAtomFormat()
  163. {
  164. // Arrange #1 and #2
  165. using (HttpConfiguration configuration = CreateConfiguration())
  166. {
  167. foreach (ODataMediaTypeFormatter odataFormatter in
  168. configuration.Formatters.OfType<ODataMediaTypeFormatter>())
  169. {
  170. odataFormatter.SupportedMediaTypes.Remove(ODataMediaTypes.ApplicationJsonODataVerbose);
  171. odataFormatter.SupportedMediaTypes.Remove(ODataMediaTypes.ApplicationJson);
  172. }
  173. using (HttpServer host = new HttpServer(configuration))
  174. using (HttpClient client = new HttpClient(host))
  175. {
  176. // Arrange #1
  177. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("People(10)",
  178. ODataTestUtil.ApplicationAtomMediaTypeWithQuality))
  179. // Act #1
  180. using (HttpResponseMessage response = client.SendAsync(request).Result)
  181. {
  182. // Assert #1
  183. AssertODataVersion3AtomResponse(Resources.PersonEntryInAtom, response);
  184. }
  185. // Arrange #2
  186. using (HttpRequestMessage request = CreateRequestWithDataServiceVersionHeaders("People(10)",
  187. ODataTestUtil.ApplicationJsonMediaTypeWithQuality))
  188. // Act #2
  189. using (HttpResponseMessage response = client.SendAsync(request).Result)
  190. {
  191. // Assert #2
  192. Assert.NotNull(response);
  193. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  194. Assert.Equal(ODataTestUtil.ApplicationJsonMediaTypeWithQuality.MediaType,
  195. response.Content.Headers.ContentType.MediaType);
  196. ODataTestUtil.VerifyJsonResponse(response.Content, Resources.PersonEntryInPlainOldJson);
  197. }
  198. }
  199. }
  200. }
  201. [Fact]
  202. public void ConditionallySupportODataIfQueryStringPresent()
  203. {
  204. // Arrange #1, #2 and #3
  205. using (HttpConfiguration configuration = CreateConfiguration())
  206. {
  207. foreach (ODataMediaTypeFormatter odataFormatter in
  208. configuration.Formatters.OfType<ODataMediaTypeFormatter>())
  209. {
  210. odataFormatter.SupportedMediaTypes.Clear();
  211. odataFormatter.MediaTypeMappings.Add(new ODataMediaTypeMapping(ODataTestUtil.ApplicationAtomMediaTypeWithQuality));
  212. odataFormatter.MediaTypeMappings.Add(new ODataMediaTypeMapping(ODataTestUtil.ApplicationJsonMediaTypeWithQuality));
  213. }
  214. using (HttpServer host = new HttpServer(configuration))
  215. using (HttpClient client = new HttpClient(host))
  216. {
  217. // Arrange #1 this request should return response in OData atom format
  218. using (HttpRequestMessage request = ODataTestUtil.GenerateRequestMessage(
  219. CreateAbsoluteUri("People(10)?format=odata"), isAtom: true))
  220. // Act #1
  221. using (HttpResponseMessage response = client.SendAsync(request).Result)
  222. {
  223. // Assert #1
  224. AssertODataVersion3AtomResponse(Resources.PersonEntryInAtom, response);
  225. }
  226. // Arrange #2: this request should return response in OData json format
  227. using (HttpRequestMessage requestWithJsonHeader = ODataTestUtil.GenerateRequestMessage(
  228. CreateAbsoluteUri("People(10)?format=odata"), isAtom: false))
  229. // Act #2
  230. using (HttpResponseMessage response = client.SendAsync(requestWithJsonHeader).Result)
  231. {
  232. // Assert #2
  233. AssertODataVersion3JsonResponse(Resources.PersonEntryInJsonVerbose, response);
  234. }
  235. // Arrange #3: when the query string is not present, request should be handled by the regular Json
  236. // Formatter
  237. using (HttpRequestMessage requestWithNonODataJsonHeader = ODataTestUtil.GenerateRequestMessage(
  238. CreateAbsoluteUri("People(10)"), isAtom: false))
  239. // Act #3
  240. using (HttpResponseMessage response = client.SendAsync(requestWithNonODataJsonHeader).Result)
  241. {
  242. // Assert #3
  243. Assert.NotNull(response);
  244. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  245. Assert.Equal(ODataTestUtil.ApplicationJsonMediaTypeWithQuality.MediaType,
  246. response.Content.Headers.ContentType.MediaType);
  247. Assert.Null(ODataTestUtil.GetDataServiceVersion(response.Content.Headers));
  248. ODataTestUtil.VerifyJsonResponse(response.Content, Resources.PersonEntryInPlainOldJson);
  249. }
  250. }
  251. }
  252. }
  253. [Fact]
  254. public void GetFeedInODataAtomFormat_HasSelfLink()
  255. {
  256. // Arrange
  257. using (HttpConfiguration configuration = CreateConfiguration())
  258. using (HttpServer host = new HttpServer(configuration))
  259. using (HttpClient client = new HttpClient(host))
  260. using (HttpRequestMessage request = CreateRequest("People",
  261. ODataTestUtil.ApplicationAtomMediaTypeWithQuality))
  262. // Act
  263. using (HttpResponseMessage response = client.SendAsync(request).Result)
  264. {
  265. // Assert
  266. Assert.NotNull(response);
  267. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  268. XElement xml = XElement.Load(response.Content.ReadAsStreamAsync().Result);
  269. XElement[] links = xml.Elements(XName.Get("link", "http://www.w3.org/2005/Atom")).ToArray();
  270. Assert.Equal("self", links.First().Attribute("rel").Value);
  271. Assert.Equal(baseAddress + "People", links.First().Attribute("href").Value);
  272. }
  273. }
  274. [Fact]
  275. public void GetFeedInODataAtomFormat_LimitsResults()
  276. {
  277. // Arrange
  278. using (HttpConfiguration configuration = CreateConfiguration())
  279. using (HttpServer host = new HttpServer(configuration))
  280. using (HttpClient client = new HttpClient(host))
  281. using (HttpRequestMessage request = CreateRequest("People?$orderby=Name&$inlinecount=allpages",
  282. ODataTestUtil.ApplicationAtomMediaTypeWithQuality))
  283. // Act
  284. using (HttpResponseMessage response = client.SendAsync(request).Result)
  285. {
  286. // Assert
  287. Assert.NotNull(response);
  288. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  289. XElement xml = XElement.Load(response.Content.ReadAsStreamAsync().Result);
  290. XElement[] entries = xml.Elements(XName.Get("entry", "http://www.w3.org/2005/Atom")).ToArray();
  291. XElement nextPageLink = xml.Elements(XName.Get("link", "http://www.w3.org/2005/Atom"))
  292. .Where(link => link.Attribute(XName.Get("rel")).Value == "next")
  293. .SingleOrDefault();
  294. XElement count = xml.Element(XName.Get("count", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"));
  295. // Assert the PageSize correctly limits three results to two
  296. Assert.Equal(2, entries.Length);
  297. // Assert there is a next page link
  298. Assert.NotNull(nextPageLink);
  299. // Assert the count is included with the number of entities (3)
  300. Assert.Equal("3", count.Value);
  301. }
  302. }
  303. [Fact]
  304. public void HttpErrorInODataFormat_GetsSerializedCorrectly()
  305. {
  306. // Arrange
  307. using (HttpConfiguration configuration = CreateConfiguration())
  308. {
  309. configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
  310. using (HttpServer host = new HttpServer(configuration))
  311. using (HttpClient client = new HttpClient(host))
  312. using (HttpRequestMessage request = CreateRequest("People?$filter=abc+eq+null",
  313. MediaTypeWithQualityHeaderValue.Parse("application/xml")))
  314. // Act
  315. using (HttpResponseMessage response = client.SendAsync(request).Result)
  316. {
  317. // Assert
  318. Assert.NotNull(response);
  319. Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
  320. XElement xml = XElement.Load(response.Content.ReadAsStreamAsync().Result);
  321. Assert.Equal("error", xml.Name.LocalName);
  322. Assert.Equal("The query specified in the URI is not valid.", xml.Element(XName.Get("{http://schemas.microsoft.com/ado/2007/08/dataservices/metadata}message")).Value);
  323. XElement innerErrorXml = xml.Element(XName.Get("{http://schemas.microsoft.com/ado/2007/08/dataservices/metadata}innererror"));
  324. Assert.NotNull(innerErrorXml);
  325. Assert.Equal("Type 'System.Web.Http.OData.Formatter.FormatterPerson' does not have a property 'abc'.", innerErrorXml.Element(XName.Get("{http://schemas.microsoft.com/ado/2007/08/dataservices/metadata}message")).Value);
  326. Assert.Equal("Microsoft.Data.OData.ODataException", innerErrorXml.Element(XName.Get("{http://schemas.microsoft.com/ado/2007/08/dataservices/metadata}type")).Value);
  327. }
  328. }
  329. }
  330. private static void AddDataServiceVersionHeaders(HttpRequestMessage request)
  331. {
  332. request.Headers.Add("DataServiceVersion", "2.0");
  333. request.Headers.Add("MaxDataServiceVersion", "3.0");
  334. }
  335. private static void AssertODataVersion3AtomResponse(string expectedContent, HttpResponseMessage actual)
  336. {
  337. AssertODataVersion3AtomResponse(expectedContent, actual, HttpStatusCode.OK);
  338. }
  339. private static void AssertODataVersion3AtomResponse(string expectedContent, HttpResponseMessage actual, HttpStatusCode statusCode)
  340. {
  341. Assert.NotNull(actual);
  342. Assert.Equal(statusCode, actual.StatusCode);
  343. Assert.Equal(ODataTestUtil.ApplicationAtomMediaTypeWithQuality.MediaType,
  344. actual.Content.Headers.ContentType.MediaType);
  345. Assert.Equal(ODataTestUtil.GetDataServiceVersion(actual.Content.Headers),
  346. ODataTestUtil.Version3NumberString);
  347. ODataTestUtil.VerifyResponse(actual.Content, expectedContent);
  348. }
  349. private static void AssertODataVersion3JsonResponse(string expectedContent, HttpResponseMessage actual)
  350. {
  351. Assert.NotNull(actual);
  352. Assert.Equal(HttpStatusCode.OK, actual.StatusCode);
  353. Assert.Equal(ODataTestUtil.ApplicationJsonMediaTypeWithQuality.MediaType,
  354. actual.Content.Headers.ContentType.MediaType);
  355. Assert.Equal(ODataTestUtil.Version3NumberString,
  356. ODataTestUtil.GetDataServiceVersion(actual.Content.Headers));
  357. ODataTestUtil.VerifyJsonResponse(actual.Content, expectedContent);
  358. }
  359. private static string CreateAbsoluteLink(string relativeUri)
  360. {
  361. return CreateAbsoluteUri(relativeUri).AbsoluteUri;
  362. }
  363. private static Uri CreateAbsoluteUri(string relativeUri)
  364. {
  365. return new Uri(new Uri(baseAddress), relativeUri);
  366. }
  367. private static HttpConfiguration CreateConfiguration(bool tracingEnabled = false)
  368. {
  369. IEdmModel model = ODataTestUtil.GetEdmModel();
  370. HttpConfiguration configuration = CreateConfiguration(model);
  371. if (tracingEnabled)
  372. {
  373. configuration.Services.Replace(typeof(ITraceWriter), new Mock<ITraceWriter>().Object);
  374. }
  375. return configuration;
  376. }
  377. private static HttpConfiguration CreateConfiguration(IEdmModel model)
  378. {
  379. HttpConfiguration configuration = new HttpConfiguration();
  380. configuration.Routes.MapODataRoute(model);
  381. configuration.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create());
  382. return configuration;
  383. }
  384. private static IEdmModel CreateModelForFullMetadata(bool sameLinksForIdAndEdit, bool sameLinksForEditAndRead)
  385. {
  386. ODataModelBuilder builder = new ODataModelBuilder();
  387. EntitySetConfiguration<MainEntity> mainSet = builder.EntitySet<MainEntity>("MainEntity");
  388. Func<EntityInstanceContext<MainEntity>, string> idLinkFactory = (e) =>
  389. CreateAbsoluteLink("/MainEntity/id/" + e.EntityInstance.Id.ToString());
  390. mainSet.HasIdLink(idLinkFactory, followsConventions: true);
  391. Func<EntityInstanceContext<MainEntity>, string> editLinkFactory;
  392. if (!sameLinksForIdAndEdit)
  393. {
  394. editLinkFactory = (e) => CreateAbsoluteLink("/MainEntity/edit/" + e.EntityInstance.Id.ToString());
  395. mainSet.HasEditLink(editLinkFactory, followsConventions: false);
  396. }
  397. Func<EntityInstanceContext<MainEntity>, string> readLinkFactory;
  398. if (!sameLinksForEditAndRead)
  399. {
  400. readLinkFactory = (e) => CreateAbsoluteLink("/MainEntity/read/" + e.EntityInstance.Id.ToString());
  401. mainSet.HasReadLink(readLinkFactory, followsConventions: false);
  402. }
  403. EntityTypeConfiguration<MainEntity> main = mainSet.EntityType;
  404. main.HasKey<int>((e) => e.Id);
  405. main.Property<short>((e) => e.Int16);
  406. NavigationPropertyConfiguration mainToRelated = mainSet.EntityType.HasRequired((e) => e.Related);
  407. main.Action("DoAlways").ReturnsCollectionFromEntitySet<MainEntity>("MainEntity").HasActionLink((c) =>
  408. CreateAbsoluteUri("/MainEntity/DoAlways/" + ((MainEntity)(c.EntityInstance)).Id),
  409. followsConventions: true);
  410. main.TransientAction("DoSometimes").ReturnsCollectionFromEntitySet<MainEntity>(
  411. "MainEntity").HasActionLink((c) =>
  412. CreateAbsoluteUri("/MainEntity/DoSometimes/" + ((MainEntity)(c.EntityInstance)).Id),
  413. followsConventions: false);
  414. mainSet.HasNavigationPropertyLink(mainToRelated, (c, p) => new Uri("/MainEntity/RelatedEntity/" +
  415. c.EntityInstance.Id, UriKind.Relative), followsConventions: true);
  416. EntitySetConfiguration<RelatedEntity> related = builder.EntitySet<RelatedEntity>("RelatedEntity");
  417. return builder.GetEdmModel();
  418. }
  419. private static HttpRequestMessage CreateRequest(string pathAndQuery, MediaTypeWithQualityHeaderValue accept)
  420. {
  421. HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, CreateAbsoluteUri(pathAndQuery));
  422. request.Headers.Accept.Add(accept);
  423. return request;
  424. }
  425. private static HttpRequestMessage CreateRequestWithDataServiceVersionHeaders(string pathAndQuery,
  426. MediaTypeWithQualityHeaderValue accept)
  427. {
  428. HttpRequestMessage request = CreateRequest(pathAndQuery, accept);
  429. AddDataServiceVersionHeaders(request);
  430. return request;
  431. }
  432. }
  433. public class MainEntity
  434. {
  435. public int Id { get; set; }
  436. public short Int16 { get; set; }
  437. public RelatedEntity Related { get; set; }
  438. }
  439. public class RelatedEntity
  440. {
  441. public int Id { get; set; }
  442. }
  443. public class MainEntityController : ODataController
  444. {
  445. public IEnumerable<MainEntity> Get()
  446. {
  447. MainEntity[] entities = new MainEntity[]
  448. {
  449. new MainEntity
  450. {
  451. Id = 1,
  452. Int16 = -1,
  453. Related = new RelatedEntity
  454. {
  455. Id = 101
  456. }
  457. },
  458. new MainEntity
  459. {
  460. Id = 2,
  461. Int16 = -2,
  462. Related = new RelatedEntity
  463. {
  464. Id = 102
  465. }
  466. }
  467. };
  468. return new PageResult<MainEntity>(entities, new Uri("aa:b"), 3);
  469. }
  470. }
  471. }