From 197043c0a730b660817f931b3300dc5b51208421 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 2 Apr 2020 12:12:47 -0700 Subject: [PATCH] Add initial CSV tests --- WebApiContrib.Core.sln | 13 +- tests/WebApiContrib.Core.Csv.Tests/Book.cs | 15 ++ .../BooksController.cs | 31 ++++ .../WebApiContrib.Core.Csv.Tests/CsvTests.cs | 141 ++++++++++++++++++ tests/WebApiContrib.Core.Csv.Tests/Startup.cs | 35 +++++ .../WebApiContrib.Core.Csv.Tests.csproj | 29 ++++ 6 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 tests/WebApiContrib.Core.Csv.Tests/Book.cs create mode 100644 tests/WebApiContrib.Core.Csv.Tests/BooksController.cs create mode 100644 tests/WebApiContrib.Core.Csv.Tests/CsvTests.cs create mode 100644 tests/WebApiContrib.Core.Csv.Tests/Startup.cs create mode 100644 tests/WebApiContrib.Core.Csv.Tests/WebApiContrib.Core.Csv.Tests.csproj diff --git a/WebApiContrib.Core.sln b/WebApiContrib.Core.sln index bc3f645..34a350b 100644 --- a/WebApiContrib.Core.sln +++ b/WebApiContrib.Core.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2003 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29926.136 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. Core", "01. Core", "{5898776F-DBF8-4C30-85A3-66401B12016F}" EndProject @@ -44,7 +44,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Formatte EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Yaml.Tests", "tests\WebApiContrib.Core.Yaml.Tests\WebApiContrib.Core.Yaml.Tests.csproj", "{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiContrib.Core.Tests", "tests\WebApiContrib.Core.Tests\WebApiContrib.Core.Tests.csproj", "{6466DD2D-6358-4D7F-8562-2517F7F53E3F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Tests", "tests\WebApiContrib.Core.Tests\WebApiContrib.Core.Tests.csproj", "{6466DD2D-6358-4D7F-8562-2517F7F53E3F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Csv.Tests", "tests\WebApiContrib.Core.Csv.Tests\WebApiContrib.Core.Csv.Tests.csproj", "{202BC540-B2CA-4CCA-8E9C-EE18B0B59A20}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -124,6 +126,10 @@ Global {6466DD2D-6358-4D7F-8562-2517F7F53E3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {6466DD2D-6358-4D7F-8562-2517F7F53E3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {6466DD2D-6358-4D7F-8562-2517F7F53E3F}.Release|Any CPU.Build.0 = Release|Any CPU + {202BC540-B2CA-4CCA-8E9C-EE18B0B59A20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {202BC540-B2CA-4CCA-8E9C-EE18B0B59A20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {202BC540-B2CA-4CCA-8E9C-EE18B0B59A20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {202BC540-B2CA-4CCA-8E9C-EE18B0B59A20}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -148,6 +154,7 @@ Global {D542971E-D9D3-4E8F-A4BF-D1638D7C2613} = {5898776F-DBF8-4C30-85A3-66401B12016F} {3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB} = {A7A8D928-30D6-4497-8B7D-F233B79A2917} {6466DD2D-6358-4D7F-8562-2517F7F53E3F} = {A7A8D928-30D6-4497-8B7D-F233B79A2917} + {202BC540-B2CA-4CCA-8E9C-EE18B0B59A20} = {A7A8D928-30D6-4497-8B7D-F233B79A2917} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C1758983-E7A8-4669-9176-F46E7A08E588} diff --git a/tests/WebApiContrib.Core.Csv.Tests/Book.cs b/tests/WebApiContrib.Core.Csv.Tests/Book.cs new file mode 100644 index 0000000..83caefb --- /dev/null +++ b/tests/WebApiContrib.Core.Csv.Tests/Book.cs @@ -0,0 +1,15 @@ +namespace WebApiContrib.Core.Csv.Tests +{ + public class Book + { + public static Book[] Data = new[] + { + new Book { Title = "Our Mathematical Universe: My Quest for the Ultimate Nature of Reality", Author = "Max Tegmark"}, + new Book { Title = "Hockey Towns", Author = "Ron MacLean"}, + }; + + public string Title { get; set; } + + public string Author { get; set; } + } +} diff --git a/tests/WebApiContrib.Core.Csv.Tests/BooksController.cs b/tests/WebApiContrib.Core.Csv.Tests/BooksController.cs new file mode 100644 index 0000000..b640a49 --- /dev/null +++ b/tests/WebApiContrib.Core.Csv.Tests/BooksController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApiContrib.Core.Csv.Tests +{ + [Route("api/books")] + public class BooksController : ControllerBase + { + [HttpGet] + public IActionResult Get() + { + return Ok(Book.Data); + } + + [HttpGet("{id}")] + public IActionResult Get(int id) + { + if (Book.Data.Length >= id && id > 0) + { + return Ok(Book.Data[id-1]); + } + + return Ok(null); + } + + [HttpPost] + public IActionResult Post([FromBody]Book book) + { + return Ok(book); + } + } +} diff --git a/tests/WebApiContrib.Core.Csv.Tests/CsvTests.cs b/tests/WebApiContrib.Core.Csv.Tests/CsvTests.cs new file mode 100644 index 0000000..a158b66 --- /dev/null +++ b/tests/WebApiContrib.Core.Csv.Tests/CsvTests.cs @@ -0,0 +1,141 @@ +using CsvHelper; +using CsvHelper.Configuration; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Newtonsoft.Json; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Xunit; + +namespace WebApiContrib.Core.Csv.Tests +{ + // note: the JSON tests are here to verify that the two formatters do not conflict with each other + public class CsvTests + { + private TestServer _server; + public CsvTests() + { + _server = new TestServer(new WebHostBuilder() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseStartup()); + } + + [Fact] + public async Task GetCollection_Text_Csv_Header() + { + var client = _server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, "/api/books"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/Csv")); + var result = await client.SendAsync(request); + var books = Deserialize(new StreamReader(await result.Content.ReadAsStreamAsync())); + + Assert.Equal(2, books.Length); + Assert.Equal(Book.Data[0].Author, books[0].Author); + Assert.Equal(Book.Data[0].Title, books[0].Title); + Assert.Equal(Book.Data[1].Author, books[1].Author); + Assert.Equal(Book.Data[1].Title, books[1].Title); + } + + [Fact(Skip = "Returns json instead of csv")] + public async Task GetById_Text_Csv_Header() + { + var client = _server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, "/api/books/1"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/Csv")); + var result = await client.SendAsync(request); + var book = Deserialize(new StreamReader(await result.Content.ReadAsStreamAsync())).Single(); + + Assert.NotNull(book); + Assert.Equal(Book.Data[0].Author, book.Author); + Assert.Equal(Book.Data[0].Title, book.Title); + } + + [Fact(Skip = "Returns json instead of csv")] + public async Task Post_Text_Csv_Header() + { + var client = _server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Post, "/api/books"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/Csv")); + + var book = new Book + { + Author = "Tim Parks", + Title = "Italian Ways: On and off the Rails from Milan to Palermo" + }; + + MemoryStream stream = new MemoryStream(); + StreamWriter writer = new StreamWriter(stream); + Serialize(writer,new[] { book }); + writer.Flush(); + + HttpContent data = new StreamContent(stream); + data.Headers.ContentType = new MediaTypeHeaderValue("text/Csv"); + + request.Content = data; + var result = await client.SendAsync(request); + + var echo = Deserialize(new StreamReader(await result.Content.ReadAsStreamAsync())).Single(); + + Assert.NotNull(book); + Assert.Equal(book.Author, echo.Author); + Assert.Equal(book.Title, echo.Title); + } + + [Fact] + public async Task GetCollection_JSON_Header() + { + var client = _server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, "/api/books"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var result = await client.SendAsync(request); + var books = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); + + Assert.Equal(2, books.Length); + Assert.Equal(Book.Data[0].Author, books[0].Author); + Assert.Equal(Book.Data[0].Title, books[0].Title); + Assert.Equal(Book.Data[1].Author, books[1].Author); + Assert.Equal(Book.Data[1].Title, books[1].Title); + } + + private static T[] Deserialize(StreamReader reader) + { + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + csv.Configuration.Delimiter = ";"; + csv.Configuration.RegisterClassMap(); + return csv.GetRecords().ToArray(); + } + } + + private static void Serialize(StreamWriter writer, IEnumerable obj) + { + using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture, leaveOpen: true)) + { + csv.Configuration.RegisterClassMap(); + + csv.WriteRecords(obj); + } + } + + private class BookMap : ClassMap + { + public BookMap() + { + Map(m => m.Title) + .Index(0); + Map(m => m.Author) + .Index(1); + } + } + } +} diff --git a/tests/WebApiContrib.Core.Csv.Tests/Startup.cs b/tests/WebApiContrib.Core.Csv.Tests/Startup.cs new file mode 100644 index 0000000..ecfeb5f --- /dev/null +++ b/tests/WebApiContrib.Core.Csv.Tests/Startup.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using WebApiContrib.Core.Formatter.Csv; +using WebApiContrib.Core.Formatter.Csv; + +namespace WebApiContrib.Core.Csv.Tests +{ + public class Startup + { + public IConfigurationRoot Configuration { get; } + + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddMvcCore() + .AddJsonFormatters() + .AddCsvSerializerFormatters(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseMvc(); + } + } +} diff --git a/tests/WebApiContrib.Core.Csv.Tests/WebApiContrib.Core.Csv.Tests.csproj b/tests/WebApiContrib.Core.Csv.Tests/WebApiContrib.Core.Csv.Tests.csproj new file mode 100644 index 0000000..9487b98 --- /dev/null +++ b/tests/WebApiContrib.Core.Csv.Tests/WebApiContrib.Core.Csv.Tests.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp2.1 + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + \ No newline at end of file