From 68866f87160e0d10aa3b9fd9468bf7e3b59a1e5f Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Thu, 30 Mar 2017 10:28:04 -0700 Subject: [PATCH] Added functional tests for JsonPatch --- .../JsonPatchInputFormatterTest.cs | 150 ++++++++++++++++++ .../Controllers/JsonPatchController.cs | 51 ++++++ .../FormatterWebSite/Models/Product.cs | 14 ++ .../FormatterWebSite/Models/Review.cs | 12 ++ 4 files changed, 227 insertions(+) create mode 100644 test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonPatchInputFormatterTest.cs create mode 100644 test/WebSites/FormatterWebSite/Controllers/JsonPatchController.cs create mode 100644 test/WebSites/FormatterWebSite/Models/Product.cs create mode 100644 test/WebSites/FormatterWebSite/Models/Review.cs diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonPatchInputFormatterTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonPatchInputFormatterTest.cs new file mode 100644 index 0000000000..73d1ca3a21 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonPatchInputFormatterTest.cs @@ -0,0 +1,150 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using FormatterWebSite; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.FunctionalTests +{ + public class JsonPatchSampleTest : IClassFixture> + { + public JsonPatchSampleTest(MvcTestFixture fixture) + { + Client = fixture.Client; + } + + public HttpClient Client { get; } + + [Fact] + public async Task AddOperation_Works() + { + // Arrange + var input = "[{ 'op': 'add', 'path': 'Reviews/-', 'value': { 'Rating': 3.5 }}]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var product = JsonConvert.DeserializeObject(body); + Assert.NotNull(product); + Assert.NotNull(product.Reviews); + Assert.Equal(3, product.Reviews.Count); + Assert.Equal(3.5, product.Reviews[2].Rating); + } + + [Fact] + public async Task ReplaceOperation_Works() + { + // Arrange + var input = "[{ 'op': 'replace', 'path': 'Reviews/0/Rating', 'value': 5 }]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var product = JsonConvert.DeserializeObject(body); + Assert.NotNull(product); + Assert.NotNull(product.Reviews); + Assert.Equal(2, product.Reviews.Count); + Assert.Equal(5, product.Reviews[0].Rating); + } + + [Fact] + public async Task CopyOperation_Works() + { + // Arrange + var input = "[{ 'op': 'copy', 'path': 'Reviews/1/Rating', 'from': 'Reviews/0/Rating'}]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var product = JsonConvert.DeserializeObject(body); + Assert.NotNull(product); + Assert.NotNull(product.Reviews); + Assert.Equal(2, product.Reviews.Count); + Assert.Equal(4, product.Reviews[1].Rating); + Assert.Equal(4, product.Reviews[0].Rating); + } + + [Fact] + public async Task MoveOperation_Works() + { + // Arrange + var input = "[{ 'op': 'move', 'path': 'Reviews/1/Rating', 'from': 'Reviews/0/Rating'}]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var product = JsonConvert.DeserializeObject(body); + Assert.NotNull(product); + Assert.NotNull(product.Reviews); + Assert.Equal(2, product.Reviews.Count); + Assert.Equal(4, product.Reviews[1].Rating); + Assert.Equal(0, product.Reviews[0].Rating); + } + + [Fact] + public async Task RemoveOperation_Works() + { + // Arrange + var input = "[{ 'op': 'remove', 'path': 'Reviews/0/Rating'}]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + var product = JsonConvert.DeserializeObject(body); + Assert.NotNull(product); + Assert.NotNull(product.Reviews); + Assert.Equal(2, product.Reviews.Count); + Assert.Equal(0, product.Reviews[0].Rating); + } + + [Fact] + public async Task AddOperation_InvalidValueForProperty_AddsErrorToModelState() + { + // Arrange + var input = "[{ 'op': 'add', 'path': 'Reviews/-', 'value': { 'Rating': 'not-a-double' }}]".Replace("'", "\""); + var request = GetPatchRequest(input); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + private HttpRequestMessage GetPatchRequest(string body) + { + return new HttpRequestMessage + { + Content = new StringContent(body, Encoding.UTF8, "application/json-patch+json"), + Method = new HttpMethod("PATCH"), + RequestUri = new Uri("http://localhost/jsonpatch/PatchProduct") + }; + } + } +} \ No newline at end of file diff --git a/test/WebSites/FormatterWebSite/Controllers/JsonPatchController.cs b/test/WebSites/FormatterWebSite/Controllers/JsonPatchController.cs new file mode 100644 index 0000000000..634388bc25 --- /dev/null +++ b/test/WebSites/FormatterWebSite/Controllers/JsonPatchController.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.Mvc; + +namespace FormatterWebSite.Controllers +{ + [Route("jsonpatch/[action]")] + public class JsonPatchController : Controller + { + [HttpPatch] + public IActionResult PatchProduct([FromBody] JsonPatchDocument patchDoc) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var product = CreateProduct(); + patchDoc.ApplyTo(product, ModelState); + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + return Ok(product); + } + + private Product CreateProduct() + { + return new Product + { + Name = "Book1", + Reviews = new List() + { + new Review + { + Rating = 4 + }, + new Review + { + Rating = 3 + } + } + }; + } + } +} diff --git a/test/WebSites/FormatterWebSite/Models/Product.cs b/test/WebSites/FormatterWebSite/Models/Product.cs new file mode 100644 index 0000000000..ed14c8f0f8 --- /dev/null +++ b/test/WebSites/FormatterWebSite/Models/Product.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace FormatterWebSite +{ + public class Product + { + public string Name { get; set; } + + public List Reviews { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/test/WebSites/FormatterWebSite/Models/Review.cs b/test/WebSites/FormatterWebSite/Models/Review.cs new file mode 100644 index 0000000000..dc8ad30271 --- /dev/null +++ b/test/WebSites/FormatterWebSite/Models/Review.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace FormatterWebSite +{ + public class Review + { + public double Rating { get; set; } + + public string Description { get; set; } + } +}