From 0335b7bddaa457e7d71b9449c2fe4f31c6ecfc91 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 29 Jun 2022 12:35:13 -0500 Subject: [PATCH 01/12] begin writing tests for controllers --- .editorconfig | 6 +++ criminalmindsapi.sln | 6 +++ server/Controllers/v1/CharactersController.cs | 4 +- server/Controllers/v1/EpisodesController.cs | 4 +- server/Controllers/v1/QuotesController.cs | 4 +- server/Controllers/v1/SeasonsController.cs | 4 +- server/server.csproj | 1 + tests/Usings.cs | 1 + .../controllers/CharactersControllerTests.cs | 40 +++++++++++++++++++ tests/tests.csproj | 28 +++++++++++++ 10 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 tests/Usings.cs create mode 100644 tests/controllers/CharactersControllerTests.cs create mode 100644 tests/tests.csproj diff --git a/.editorconfig b/.editorconfig index 801f4f7..732666a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,3 +8,9 @@ dotnet_diagnostic.CS8604.severity = none # CS1591: Missing XML comment for publicly visible type or member dotnet_diagnostic.CS1591.severity = none + +# CS8629: Nullable value type may be null. +dotnet_diagnostic.CS8629.severity = silent + +# CS8602: Dereference of a possibly null reference. +dotnet_diagnostic.CS8602.severity = silent diff --git a/criminalmindsapi.sln b/criminalmindsapi.sln index 71e421e..e3a131e 100644 --- a/criminalmindsapi.sln +++ b/criminalmindsapi.sln @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{B78134BA-1C1D-43CC-9B41-78C67B057AAC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,10 @@ Global {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.Build.0 = Release|Any CPU + {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server/Controllers/v1/CharactersController.cs b/server/Controllers/v1/CharactersController.cs index 9e1c3e6..ddc3fdf 100644 --- a/server/Controllers/v1/CharactersController.cs +++ b/server/Controllers/v1/CharactersController.cs @@ -31,7 +31,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task>> GetCharactersAsync([FromQuery] CharacterFilter? filter) + public async Task GetCharactersAsync([FromQuery] CharacterFilter? filter) { try { @@ -60,7 +60,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task> GetCharacterByIdAsync(string id) + public async Task GetCharacterByIdAsync(string id) { if (!ObjectId.TryParse(id, out _)) diff --git a/server/Controllers/v1/EpisodesController.cs b/server/Controllers/v1/EpisodesController.cs index dbb131c..7ee0864 100644 --- a/server/Controllers/v1/EpisodesController.cs +++ b/server/Controllers/v1/EpisodesController.cs @@ -30,7 +30,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task>> GetEpisodesAsync([FromQuery] EpisodeFilter? filter) + public async Task GetEpisodesAsync([FromQuery] EpisodeFilter? filter) { try { @@ -59,7 +59,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task> GetEpisodeByNumberAsync(int number) + public async Task GetEpisodeByNumberAsync(int number) { try { diff --git a/server/Controllers/v1/QuotesController.cs b/server/Controllers/v1/QuotesController.cs index e8f838c..a5a18d6 100644 --- a/server/Controllers/v1/QuotesController.cs +++ b/server/Controllers/v1/QuotesController.cs @@ -32,7 +32,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task>> GetQuotesAsync([FromQuery] QuoteFilter? filter) + public async Task GetQuotesAsync([FromQuery] QuoteFilter? filter) { try { @@ -61,7 +61,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task> GetQuoteByIdAsync(string id) + public async Task GetQuoteByIdAsync(string id) { if (!ObjectId.TryParse(id, out _)) diff --git a/server/Controllers/v1/SeasonsController.cs b/server/Controllers/v1/SeasonsController.cs index fd84db7..5dfdd41 100644 --- a/server/Controllers/v1/SeasonsController.cs +++ b/server/Controllers/v1/SeasonsController.cs @@ -27,7 +27,7 @@ namespace server.Controllers.v1 [HttpGet] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task>> GetSeasonsAsync() + public async Task GetSeasonsAsync() { try { @@ -53,7 +53,7 @@ namespace server.Controllers.v1 [ProducesResponseType(typeof(Season), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] - public async Task> GetSeasonByNumberAsync(int number) + public async Task GetSeasonByNumberAsync(int number) { try { diff --git a/server/server.csproj b/server/server.csproj index e3944a6..c124798 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -16,6 +16,7 @@ + diff --git a/tests/Usings.cs b/tests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/tests/controllers/CharactersControllerTests.cs b/tests/controllers/CharactersControllerTests.cs new file mode 100644 index 0000000..a753c04 --- /dev/null +++ b/tests/controllers/CharactersControllerTests.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Mvc; +using Moq; +using server.Controllers.v1; +using server.Models; +using server.Persistence.Repositories; +using System.Net; + +namespace tests.controllers +{ + public class CharactersControllerTests + { + private readonly Mock _mockRepo; + private readonly CharactersController _controller; + + public CharactersControllerTests() + { + _mockRepo = new Mock(); + _controller = new CharactersController(_mockRepo.Object); + } + + [Fact] + public async Task GetCharactersAsync_NoFilterNoCharacters_Returns200StatusWithEmptyCollection() + { + var filter = new CharacterFilter(); + + _mockRepo + .Setup(repo => repo.GetCharactersAsync(filter)) + .ReturnsAsync( new List()); + + var result = await _controller.GetCharactersAsync(filter) as ObjectResult; + var data = result?.Value as List; + + _mockRepo.Verify(c => c.GetCharactersAsync(filter), Times.Once()); + Assert.IsType(result); + Assert.IsType>(data); + Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); + Assert.Empty(data); + } + } +} diff --git a/tests/tests.csproj b/tests/tests.csproj new file mode 100644 index 0000000..bc23e61 --- /dev/null +++ b/tests/tests.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + From 948d33292e7ef2b6673cf72eff226ac05e9ccf4c Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 29 Jun 2022 12:53:34 -0500 Subject: [PATCH 02/12] updated server tests project and folder name --- criminalmindsapi.sln | 10 +++++----- {tests => server.tests}/Usings.cs | 0 .../controllers/CharactersControllerTests.cs | 0 tests/tests.csproj => server.tests/server.tests.csproj | 0 4 files changed, 5 insertions(+), 5 deletions(-) rename {tests => server.tests}/Usings.cs (100%) rename {tests => server.tests}/controllers/CharactersControllerTests.cs (100%) rename tests/tests.csproj => server.tests/server.tests.csproj (100%) diff --git a/criminalmindsapi.sln b/criminalmindsapi.sln index e3a131e..f52c00b 100644 --- a/criminalmindsapi.sln +++ b/criminalmindsapi.sln @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{B78134BA-1C1D-43CC-9B41-78C67B057AAC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server.tests", "server.tests\server.tests.csproj", "{F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,10 +33,10 @@ Global {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.Build.0 = Release|Any CPU - {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B78134BA-1C1D-43CC-9B41-78C67B057AAC}.Release|Any CPU.Build.0 = Release|Any CPU + {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/Usings.cs b/server.tests/Usings.cs similarity index 100% rename from tests/Usings.cs rename to server.tests/Usings.cs diff --git a/tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs similarity index 100% rename from tests/controllers/CharactersControllerTests.cs rename to server.tests/controllers/CharactersControllerTests.cs diff --git a/tests/tests.csproj b/server.tests/server.tests.csproj similarity index 100% rename from tests/tests.csproj rename to server.tests/server.tests.csproj From 5329f6eaf9907c5c7e328d65abcda951f65d8f71 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 29 Jun 2022 13:04:33 -0500 Subject: [PATCH 03/12] updated verify method --- server.tests/controllers/CharactersControllerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs index a753c04..9de3356 100644 --- a/server.tests/controllers/CharactersControllerTests.cs +++ b/server.tests/controllers/CharactersControllerTests.cs @@ -19,7 +19,7 @@ namespace tests.controllers } [Fact] - public async Task GetCharactersAsync_NoFilterNoCharacters_Returns200StatusWithEmptyCollection() + public async Task GetCharactersAsync_NoFilterParamsNoCharacters_Returns200StatusWithEmptyCollection() { var filter = new CharacterFilter(); @@ -30,7 +30,7 @@ namespace tests.controllers var result = await _controller.GetCharactersAsync(filter) as ObjectResult; var data = result?.Value as List; - _mockRepo.Verify(c => c.GetCharactersAsync(filter), Times.Once()); + _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); Assert.IsType(result); Assert.IsType>(data); Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); From 207032df05b42cb96fb90262d2a190a404af9e26 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 29 Jun 2022 20:20:52 -0500 Subject: [PATCH 04/12] added setting error message back to null when handling try again --- client/src/pages/home.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/pages/home.js b/client/src/pages/home.js index 3cd59be..3605abc 100644 --- a/client/src/pages/home.js +++ b/client/src/pages/home.js @@ -55,6 +55,8 @@ export default function Home() { const handleTryAgain = async () => { + setErrorMessage(null); + if (errorMessage === 'Failed to load characters.') return await getCharacters(); } From 4c647a5c9ad7b4a870e42049d1a135f5f7e12ef9 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 29 Jun 2022 23:02:35 -0500 Subject: [PATCH 05/12] continue writing tests for controllers --- .../controllers/CharactersControllerTests.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs index 9de3356..a2b4a44 100644 --- a/server.tests/controllers/CharactersControllerTests.cs +++ b/server.tests/controllers/CharactersControllerTests.cs @@ -36,5 +36,43 @@ namespace tests.controllers Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); Assert.Empty(data); } + + [Fact] + public async Task GetCharactersAsync_NoFilterParamsWithCharacters_Returns200StatusWithCharactersCollection() + { + var filter = new CharacterFilter(); + + var character = new Character + { + Id = "", + FirstName = "Jason", + LastName = "Gideon", + ActorFirstName = "Mandy", + ActorLastName = "Patinkin", + Seasons = new int[] {1,2,3,10,15}, + FirstEpisode = "Extreme Aggressor", + LastEpisode = "In Name and Blood", + Image = "https://criminalmindsapi.stevanfreeborn.com/characters/jason-gideon.png", + Bio = "Jason Gideon was a criminal profiler, formerly the Senior Supervisory Special Agent of the FBI's Behavioral Analysis Unit. At the beginning of Season Three, Gideon abruptly retired from the BAU due to emotional issues brought on by the murder of his girlfriend. His position is now held by his former partner and best friend David Rossi, who has held it to this day. In the Season Ten episode \"Nelson's Sparrow\", he was murdered by Donnie Mallick.", + }; + + var characters = new List + { + character + }; + + _mockRepo + .Setup(repo => repo.GetCharactersAsync(filter)) + .ReturnsAsync(characters); + + var result = await _controller.GetCharactersAsync(filter) as ObjectResult; + var data = result?.Value as List; + + _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); + Assert.IsType(result); + Assert.IsType>(data); + Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); + Assert.Single(data); + } } } From a3af130c92fde9c822c48529d20ad66727360f7f Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Thu, 30 Jun 2022 22:05:20 -0500 Subject: [PATCH 06/12] add unit test for repo throwing exception to characters controller tests --- .../controllers/CharactersControllerTests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs index a2b4a44..cf3780c 100644 --- a/server.tests/controllers/CharactersControllerTests.cs +++ b/server.tests/controllers/CharactersControllerTests.cs @@ -3,6 +3,7 @@ using Moq; using server.Controllers.v1; using server.Models; using server.Persistence.Repositories; +using System.ComponentModel.DataAnnotations; using System.Net; namespace tests.controllers @@ -74,5 +75,23 @@ namespace tests.controllers Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); Assert.Single(data); } + + [Fact] + public async Task GetCharactersAsync_RepoThrowsException_Returns500StatusWithProblemDetail() + { + var filter = new CharacterFilter(); + + _mockRepo + .Setup(repo => repo.GetCharactersAsync(filter)) + .Throws(new Exception()); + + var result = await _controller.GetCharactersAsync(filter) as ObjectResult; + var data = result.Value; + + _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); + Assert.IsType(result); + Assert.IsType(data); + Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)result.StatusCode); + } } } From 2bc2381567f79c8476f7bc6701f513d967d76a43 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Tue, 5 Jul 2022 08:55:36 -0500 Subject: [PATCH 07/12] finished unit tests for characters controller --- .../controllers/CharactersControllerTests.cs | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs index cf3780c..5eed596 100644 --- a/server.tests/controllers/CharactersControllerTests.cs +++ b/server.tests/controllers/CharactersControllerTests.cs @@ -19,25 +19,6 @@ namespace tests.controllers _controller = new CharactersController(_mockRepo.Object); } - [Fact] - public async Task GetCharactersAsync_NoFilterParamsNoCharacters_Returns200StatusWithEmptyCollection() - { - var filter = new CharacterFilter(); - - _mockRepo - .Setup(repo => repo.GetCharactersAsync(filter)) - .ReturnsAsync( new List()); - - var result = await _controller.GetCharactersAsync(filter) as ObjectResult; - var data = result?.Value as List; - - _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); - Assert.IsType(result); - Assert.IsType>(data); - Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); - Assert.Empty(data); - } - [Fact] public async Task GetCharactersAsync_NoFilterParamsWithCharacters_Returns200StatusWithCharactersCollection() { @@ -45,7 +26,7 @@ namespace tests.controllers var character = new Character { - Id = "", + Id = "62b7d5506c1b407771829926", FirstName = "Jason", LastName = "Gideon", ActorFirstName = "Mandy", From 64662126e1cb44833e00cf53d40739672d2b0ba6 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Tue, 5 Jul 2022 14:02:22 -0500 Subject: [PATCH 08/12] add integration tests --- client/client.esproj | 16 +++--- criminalmindsapi.sln | 10 ++-- .../CharactersEndpointsTests.cs | 38 ++++++++++++++ server.integrationTests/Usings.cs | 1 + .../server.integrationTests.csproj | 23 ++++++++ .../controllers/CharactersControllerTests.cs | 52 +++++++++++++++++-- server/server.csproj | 38 ++++++++------ 7 files changed, 145 insertions(+), 33 deletions(-) create mode 100644 server.integrationTests/CharactersEndpointsTests.cs create mode 100644 server.integrationTests/Usings.cs create mode 100644 server.integrationTests/server.integrationTests.csproj diff --git a/client/client.esproj b/client/client.esproj index 2fe46fe..5d60ada 100644 --- a/client/client.esproj +++ b/client/client.esproj @@ -1,10 +1,10 @@ - - 8fc072ca-af79-4930-b9b1-139230dc3426 - - - set BROWSER=none&&npm start - src\ - Jest - + + 8fc072ca-af79-4930-b9b1-139230dc3426 + + + set BROWSER=none&&npm start + src\ + Jest + \ No newline at end of file diff --git a/criminalmindsapi.sln b/criminalmindsapi.sln index f52c00b..382dc72 100644 --- a/criminalmindsapi.sln +++ b/criminalmindsapi.sln @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server.tests", "server.tests\server.tests.csproj", "{F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server.integrationTests", "server.integrationTests\server.integrationTests.csproj", "{88C22EC9-3069-41AA-99EC-5503385A3633}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,10 +33,10 @@ Global {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.Build.0 = Release|Any CPU - {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F47F6C5F-DB28-44D7-B9CF-6B927755B2D8}.Release|Any CPU.Build.0 = Release|Any CPU + {88C22EC9-3069-41AA-99EC-5503385A3633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88C22EC9-3069-41AA-99EC-5503385A3633}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88C22EC9-3069-41AA-99EC-5503385A3633}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88C22EC9-3069-41AA-99EC-5503385A3633}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server.integrationTests/CharactersEndpointsTests.cs b/server.integrationTests/CharactersEndpointsTests.cs new file mode 100644 index 0000000..4624a4d --- /dev/null +++ b/server.integrationTests/CharactersEndpointsTests.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc.Testing; +using server.Models; +using System.Net; +using System.Text.Json; + +namespace server.integrationTests +{ + [TestClass] + public class CharacterEndpointsTests + { + private readonly HttpClient _httpClient; + private readonly JsonSerializerOptions _serializerOptions; + public TestContext? TestContext { get; set; } + + public CharacterEndpointsTests() + { + var webAppFactory = new WebApplicationFactory(); + + _httpClient = webAppFactory.CreateDefaultClient(); + + _serializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + } + + [TestMethod] + public async Task GetCharacters_AllCharacters_Returns200StatusWithCharacters() + { + var response = await _httpClient.GetAsync("/api/characters"); + var data = await response.Content.ReadAsStringAsync(); + var characters = JsonSerializer.Deserialize>(data, _serializerOptions); + + Assert.IsNotNull(characters); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + } +} \ No newline at end of file diff --git a/server.integrationTests/Usings.cs b/server.integrationTests/Usings.cs new file mode 100644 index 0000000..ab67c7e --- /dev/null +++ b/server.integrationTests/Usings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/server.integrationTests/server.integrationTests.csproj b/server.integrationTests/server.integrationTests.csproj new file mode 100644 index 0000000..19c5be3 --- /dev/null +++ b/server.integrationTests/server.integrationTests.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + + diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs index 5eed596..d801e76 100644 --- a/server.tests/controllers/CharactersControllerTests.cs +++ b/server.tests/controllers/CharactersControllerTests.cs @@ -3,8 +3,10 @@ using Moq; using server.Controllers.v1; using server.Models; using server.Persistence.Repositories; -using System.ComponentModel.DataAnnotations; +using server.tests.Mocks; using System.Net; +using System.Text.Json; +using Xunit.Abstractions; namespace tests.controllers { @@ -12,11 +14,13 @@ namespace tests.controllers { private readonly Mock _mockRepo; private readonly CharactersController _controller; + private readonly ITestOutputHelper _output; - public CharactersControllerTests() + public CharactersControllerTests(ITestOutputHelper output) { _mockRepo = new Mock(); _controller = new CharactersController(_mockRepo.Object); + _output = output; } [Fact] @@ -67,12 +71,54 @@ namespace tests.controllers .Throws(new Exception()); var result = await _controller.GetCharactersAsync(filter) as ObjectResult; - var data = result.Value; + var data = result?.Value; _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); Assert.IsType(result); Assert.IsType(data); Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)result.StatusCode); } + + [Fact] + public async Task GetCharacterByIdAsync_ValidCharacterId_Returns200StatusWithCharacter() + { + throw new NotImplementedException(); + } + + [Fact] + public async Task GetCharacterByIdAsync_InvalidCharacterId_Returns400StatusWithValidationProblemDetail() + { + var characterId = "1"; + + _controller.ProblemDetailsFactory = new MockProblemDetailsFactory(); + + var result = await _controller.GetCharacterByIdAsync(characterId) as ObjectResult; + var data = result?.Value; + + string json = JsonSerializer.Serialize(result); + + _output.WriteLine(json); + + // Assert.IsType(result); + // Assert.IsType(); + } + + [Fact] + public async Task GetCharacterByIdAsync_RepoThrowsException_Returns500StatusWithProblemDetail() + { + var characterId = "62b7d5506c1b407771829938"; + + _mockRepo + .Setup(repo => repo.GetCharacterByIdAsync(characterId)) + .Throws(new Exception()); + + var result = await _controller.GetCharacterByIdAsync(characterId) as ObjectResult; + var data = result?.Value; + + _mockRepo.Verify(repo => repo.GetCharacterByIdAsync(It.IsAny()), Times.Once()); + Assert.IsType(result); + Assert.IsType(data); + Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)result.StatusCode); + } } } diff --git a/server/server.csproj b/server/server.csproj index c124798..309d659 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -1,23 +1,27 @@  - - net6.0 - enable - enable - True - + + net6.0 + enable + enable + True + - - - + + + - - - - - - - - + + + + + + + + + + + + From 6d94582686253571ca455d4bccb336f6b143a181 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Tue, 5 Jul 2022 14:38:00 -0500 Subject: [PATCH 09/12] added test project and put integration and unit tests under single test project --- criminalmindsapi.sln | 10 +- .../CharactersEndpointsTests.cs | 38 ------ server.integrationTests/Usings.cs | 1 - .../server.integrationTests.csproj | 23 ---- .../controllers/CharactersControllerTests.cs | 124 ------------------ .../CharactersControllerIntegrationTests.cs | 24 ++++ server.tests/server.tests.csproj | 6 +- .../unitTests/CharacterControllerUnitTests.cs | 18 +++ server/server.csproj | 2 +- 9 files changed, 52 insertions(+), 194 deletions(-) delete mode 100644 server.integrationTests/CharactersEndpointsTests.cs delete mode 100644 server.integrationTests/Usings.cs delete mode 100644 server.integrationTests/server.integrationTests.csproj delete mode 100644 server.tests/controllers/CharactersControllerTests.cs create mode 100644 server.tests/integrationTests/CharactersControllerIntegrationTests.cs create mode 100644 server.tests/unitTests/CharacterControllerUnitTests.cs diff --git a/criminalmindsapi.sln b/criminalmindsapi.sln index 382dc72..c1fb9bb 100644 --- a/criminalmindsapi.sln +++ b/criminalmindsapi.sln @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server.integrationTests", "server.integrationTests\server.integrationTests.csproj", "{88C22EC9-3069-41AA-99EC-5503385A3633}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server.tests", "server.tests\server.tests.csproj", "{8B86105B-F6F4-49A9-B384-C2823F693F38}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,10 +33,10 @@ Global {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EF6EB3C-FCA0-4FF6-8A99-373AFAC6E4EC}.Release|Any CPU.Build.0 = Release|Any CPU - {88C22EC9-3069-41AA-99EC-5503385A3633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88C22EC9-3069-41AA-99EC-5503385A3633}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88C22EC9-3069-41AA-99EC-5503385A3633}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88C22EC9-3069-41AA-99EC-5503385A3633}.Release|Any CPU.Build.0 = Release|Any CPU + {8B86105B-F6F4-49A9-B384-C2823F693F38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B86105B-F6F4-49A9-B384-C2823F693F38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B86105B-F6F4-49A9-B384-C2823F693F38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B86105B-F6F4-49A9-B384-C2823F693F38}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server.integrationTests/CharactersEndpointsTests.cs b/server.integrationTests/CharactersEndpointsTests.cs deleted file mode 100644 index 4624a4d..0000000 --- a/server.integrationTests/CharactersEndpointsTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using server.Models; -using System.Net; -using System.Text.Json; - -namespace server.integrationTests -{ - [TestClass] - public class CharacterEndpointsTests - { - private readonly HttpClient _httpClient; - private readonly JsonSerializerOptions _serializerOptions; - public TestContext? TestContext { get; set; } - - public CharacterEndpointsTests() - { - var webAppFactory = new WebApplicationFactory(); - - _httpClient = webAppFactory.CreateDefaultClient(); - - _serializerOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; - } - - [TestMethod] - public async Task GetCharacters_AllCharacters_Returns200StatusWithCharacters() - { - var response = await _httpClient.GetAsync("/api/characters"); - var data = await response.Content.ReadAsStringAsync(); - var characters = JsonSerializer.Deserialize>(data, _serializerOptions); - - Assert.IsNotNull(characters); - Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); - } - } -} \ No newline at end of file diff --git a/server.integrationTests/Usings.cs b/server.integrationTests/Usings.cs deleted file mode 100644 index ab67c7e..0000000 --- a/server.integrationTests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/server.integrationTests/server.integrationTests.csproj b/server.integrationTests/server.integrationTests.csproj deleted file mode 100644 index 19c5be3..0000000 --- a/server.integrationTests/server.integrationTests.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net6.0 - enable - enable - - false - - - - - - - - - - - - - - - diff --git a/server.tests/controllers/CharactersControllerTests.cs b/server.tests/controllers/CharactersControllerTests.cs deleted file mode 100644 index d801e76..0000000 --- a/server.tests/controllers/CharactersControllerTests.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Moq; -using server.Controllers.v1; -using server.Models; -using server.Persistence.Repositories; -using server.tests.Mocks; -using System.Net; -using System.Text.Json; -using Xunit.Abstractions; - -namespace tests.controllers -{ - public class CharactersControllerTests - { - private readonly Mock _mockRepo; - private readonly CharactersController _controller; - private readonly ITestOutputHelper _output; - - public CharactersControllerTests(ITestOutputHelper output) - { - _mockRepo = new Mock(); - _controller = new CharactersController(_mockRepo.Object); - _output = output; - } - - [Fact] - public async Task GetCharactersAsync_NoFilterParamsWithCharacters_Returns200StatusWithCharactersCollection() - { - var filter = new CharacterFilter(); - - var character = new Character - { - Id = "62b7d5506c1b407771829926", - FirstName = "Jason", - LastName = "Gideon", - ActorFirstName = "Mandy", - ActorLastName = "Patinkin", - Seasons = new int[] {1,2,3,10,15}, - FirstEpisode = "Extreme Aggressor", - LastEpisode = "In Name and Blood", - Image = "https://criminalmindsapi.stevanfreeborn.com/characters/jason-gideon.png", - Bio = "Jason Gideon was a criminal profiler, formerly the Senior Supervisory Special Agent of the FBI's Behavioral Analysis Unit. At the beginning of Season Three, Gideon abruptly retired from the BAU due to emotional issues brought on by the murder of his girlfriend. His position is now held by his former partner and best friend David Rossi, who has held it to this day. In the Season Ten episode \"Nelson's Sparrow\", he was murdered by Donnie Mallick.", - }; - - var characters = new List - { - character - }; - - _mockRepo - .Setup(repo => repo.GetCharactersAsync(filter)) - .ReturnsAsync(characters); - - var result = await _controller.GetCharactersAsync(filter) as ObjectResult; - var data = result?.Value as List; - - _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); - Assert.IsType(result); - Assert.IsType>(data); - Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)result.StatusCode); - Assert.Single(data); - } - - [Fact] - public async Task GetCharactersAsync_RepoThrowsException_Returns500StatusWithProblemDetail() - { - var filter = new CharacterFilter(); - - _mockRepo - .Setup(repo => repo.GetCharactersAsync(filter)) - .Throws(new Exception()); - - var result = await _controller.GetCharactersAsync(filter) as ObjectResult; - var data = result?.Value; - - _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); - Assert.IsType(result); - Assert.IsType(data); - Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)result.StatusCode); - } - - [Fact] - public async Task GetCharacterByIdAsync_ValidCharacterId_Returns200StatusWithCharacter() - { - throw new NotImplementedException(); - } - - [Fact] - public async Task GetCharacterByIdAsync_InvalidCharacterId_Returns400StatusWithValidationProblemDetail() - { - var characterId = "1"; - - _controller.ProblemDetailsFactory = new MockProblemDetailsFactory(); - - var result = await _controller.GetCharacterByIdAsync(characterId) as ObjectResult; - var data = result?.Value; - - string json = JsonSerializer.Serialize(result); - - _output.WriteLine(json); - - // Assert.IsType(result); - // Assert.IsType(); - } - - [Fact] - public async Task GetCharacterByIdAsync_RepoThrowsException_Returns500StatusWithProblemDetail() - { - var characterId = "62b7d5506c1b407771829938"; - - _mockRepo - .Setup(repo => repo.GetCharacterByIdAsync(characterId)) - .Throws(new Exception()); - - var result = await _controller.GetCharacterByIdAsync(characterId) as ObjectResult; - var data = result?.Value; - - _mockRepo.Verify(repo => repo.GetCharacterByIdAsync(It.IsAny()), Times.Once()); - Assert.IsType(result); - Assert.IsType(data); - Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)result.StatusCode); - } - } -} diff --git a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs new file mode 100644 index 0000000..455a8d2 --- /dev/null +++ b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs @@ -0,0 +1,24 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Mvc.Testing; +using System.Net; + +namespace server.tests.integrationTests +{ + public class CharactersControllerIntegrationTests + { + private readonly HttpClient _client; + + public CharactersControllerIntegrationTests() + { + var webAppFactory = new WebApplicationFactory(); + _client = webAppFactory.CreateDefaultClient(); + } + + [Fact] + public async Task GetCharacters_RetrievesAllCharacters_Returns200StatusCodeWithCharacters() + { + var response = await _client.GetAsync("/api/characters"); + response.StatusCode.Should().Be(HttpStatusCode.OK); + } + } +} diff --git a/server.tests/server.tests.csproj b/server.tests/server.tests.csproj index bc23e61..a00c304 100644 --- a/server.tests/server.tests.csproj +++ b/server.tests/server.tests.csproj @@ -9,9 +9,11 @@ - + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/server.tests/unitTests/CharacterControllerUnitTests.cs b/server.tests/unitTests/CharacterControllerUnitTests.cs new file mode 100644 index 0000000..9337729 --- /dev/null +++ b/server.tests/unitTests/CharacterControllerUnitTests.cs @@ -0,0 +1,18 @@ +using Moq; +using server.Controllers.v1; +using server.Persistence.Repositories; + +namespace server.tests.unitTests +{ + public class CharacterControllerUnitTests + { + private readonly Mock _mockRepo; + private readonly CharactersController _controller; + + public CharacterControllerUnitTests() + { + _mockRepo = new Mock(); + _controller = new CharactersController(_mockRepo.Object); + } + } +} diff --git a/server/server.csproj b/server/server.csproj index 309d659..d48351d 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -21,7 +21,7 @@ - + From f1cff5c3e5f4463f9cc0378a548317cb3a2e2af3 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Tue, 5 Jul 2022 14:55:50 -0500 Subject: [PATCH 10/12] testing --- .../integrationTests/CharactersControllerIntegrationTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs index 455a8d2..fe5c6ae 100644 --- a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs +++ b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs @@ -12,12 +12,14 @@ namespace server.tests.integrationTests { var webAppFactory = new WebApplicationFactory(); _client = webAppFactory.CreateDefaultClient(); + _client.DefaultRequestHeaders.Add("x-api-version", "1"); } [Fact] public async Task GetCharacters_RetrievesAllCharacters_Returns200StatusCodeWithCharacters() { var response = await _client.GetAsync("/api/characters"); + response.StatusCode.Should().Be(HttpStatusCode.OK); } } From a9e4c628c93e46f0c2efa7bb14e3fa769c838b6f Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Tue, 5 Jul 2022 15:29:48 -0500 Subject: [PATCH 11/12] still working on tests --- .../CharactersControllerIntegrationTests.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs index fe5c6ae..11ffbc5 100644 --- a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs +++ b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs @@ -1,26 +1,51 @@ using FluentAssertions; using Microsoft.AspNetCore.Mvc.Testing; +using server.Models; using System.Net; +using System.Text.Json; namespace server.tests.integrationTests { public class CharactersControllerIntegrationTests { private readonly HttpClient _client; + private readonly JsonSerializerOptions _serializerOptions; public CharactersControllerIntegrationTests() { var webAppFactory = new WebApplicationFactory(); + _client = webAppFactory.CreateDefaultClient(); + _client.DefaultRequestHeaders.Add("x-api-version", "1"); + + _serializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; } [Fact] - public async Task GetCharacters_RetrievesAllCharacters_Returns200StatusCodeWithCharacters() + public async Task GetCharacters_RetrieveAllCharacters_Returns200StatusCodeWithCharacters() { var response = await _client.GetAsync("/api/characters"); + var data = await response.Content.ReadAsStreamAsync(); + + var characters = JsonSerializer.Deserialize>(data); + response.StatusCode.Should().Be(HttpStatusCode.OK); + characters.Should().NotBeNull(); + characters.Should().HaveCountGreaterThan(0); + response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); + response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); + response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + } + + [Fact] + public async Task GetCharacters_RetrieveSeasonOneCharacters_Returns200StatusCodeWithCharacters() + { + return; } } } From 7ac3b16cb88920824f0c86b084b026d430327528 Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Wed, 6 Jul 2022 09:41:28 -0500 Subject: [PATCH 12/12] finished tests for getcharactersasync --- .../CharactersControllerIntegrationTests.cs | 124 ++++++++++++++++-- .../unitTests/CharacterControllerUnitTests.cs | 54 +++++++- 2 files changed, 169 insertions(+), 9 deletions(-) diff --git a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs index 11ffbc5..9afc85a 100644 --- a/server.tests/integrationTests/CharactersControllerIntegrationTests.cs +++ b/server.tests/integrationTests/CharactersControllerIntegrationTests.cs @@ -1,15 +1,19 @@ using FluentAssertions; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Testing; using server.Models; +using System.Collections; +using System.Collections.Generic; using System.Net; using System.Text.Json; namespace server.tests.integrationTests { - public class CharactersControllerIntegrationTests + public class CharactersControllerIntegrationTests { private readonly HttpClient _client; private readonly JsonSerializerOptions _serializerOptions; + private readonly string _endpoint; public CharactersControllerIntegrationTests() { @@ -23,29 +27,133 @@ namespace server.tests.integrationTests { PropertyNameCaseInsensitive = true }; + + _endpoint = "/api/characters"; } [Fact] - public async Task GetCharacters_RetrieveAllCharacters_Returns200StatusCodeWithCharacters() + public async Task GetCharactersAsync_AllCharacters_Returns200StatusCodeWithCharacters() { - var response = await _client.GetAsync("/api/characters"); + var response = await _client.GetAsync(_endpoint); var data = await response.Content.ReadAsStreamAsync(); var characters = JsonSerializer.Deserialize>(data); response.StatusCode.Should().Be(HttpStatusCode.OK); - characters.Should().NotBeNull(); - characters.Should().HaveCountGreaterThan(0); response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + + characters.Should().NotBeNull(); + characters.Should().BeOfType>(); + characters.Should().HaveCountGreaterThan(0); } - + [Fact] - public async Task GetCharacters_RetrieveSeasonOneCharacters_Returns200StatusCodeWithCharacters() + public async Task GetCharactersAsync_SeasonOneCharacters_Returns200StatusCodeWithCharacters() { - return; + var seasonValue = 1; + + var url = $"{_endpoint}?season={seasonValue}"; + + var response = await _client.GetAsync(url); + + var data = await response.Content.ReadAsStreamAsync(); + + var characters = JsonSerializer.Deserialize>(data, _serializerOptions); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); + response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); + response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + + characters.Should().NotBeNull(); + characters.Should().BeOfType>(); + characters.Should().HaveCountGreaterThan(0); + + foreach (var character in characters) + { + character.Seasons.Should().Contain(seasonValue); + } + } + + [Fact] + public async Task GetCharactersAsync_InvalidSeasonQueryParameter_Returns400StatusCodeWithValidationProblemDetails() + { + var seasonValue = "test"; + + var url = $"{_endpoint}?season={seasonValue}"; + + var response = await _client.GetAsync(url); + + var data = await response.Content.ReadAsStreamAsync(); + + var details = JsonSerializer.Deserialize(data, _serializerOptions); + + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); + response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); + response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + + details.Should().NotBeNull(); + details.Should().BeOfType(); + } + + [Fact] + public async Task GetCharactersAsync_NameContainsJason_Returns200StatusCodeWithCharacters() + { + var nameValue = "jason"; + + var url = $"{_endpoint}?name={nameValue}"; + + var response = await _client.GetAsync(url); + + var data = await response.Content.ReadAsStreamAsync(); + + var characters = JsonSerializer.Deserialize>(data, _serializerOptions); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); + response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); + response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + + characters.Should().NotBeNull(); + characters.Should().BeOfType>(); + characters.Should().HaveCountGreaterThan(0); + + foreach (var character in characters) + { + character.FullName.ToLower().Should().Contain(nameValue); + } + } + + [Fact] + public async Task GetCharactersAsync_ActorNameContainsMandy_Returns200StatusCodeWithCharacters() + { + var actorNameValue = "mandy"; + + var url = $"{_endpoint}?actorname={actorNameValue}"; + + var response = await _client.GetAsync(url); + + var data = await response.Content.ReadAsStreamAsync(); + + var characters = JsonSerializer.Deserialize>(data, _serializerOptions); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Should().ContainKey("X-Rate-Limit-Limit"); + response.Headers.Should().ContainKey("X-Rate-Limit-Remaining"); + response.Headers.Should().ContainKey("X-Rate-Limit-Reset"); + + characters.Should().NotBeNull(); + characters.Should().BeOfType>(); + characters.Should().HaveCountGreaterThan(0); + + foreach (var character in characters) + { + character.ActorFullName.ToLower().Should().Contain(actorNameValue); + } } } } diff --git a/server.tests/unitTests/CharacterControllerUnitTests.cs b/server.tests/unitTests/CharacterControllerUnitTests.cs index 9337729..929857c 100644 --- a/server.tests/unitTests/CharacterControllerUnitTests.cs +++ b/server.tests/unitTests/CharacterControllerUnitTests.cs @@ -1,6 +1,10 @@ -using Moq; +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Moq; using server.Controllers.v1; +using server.Models; using server.Persistence.Repositories; +using System.Net; namespace server.tests.unitTests { @@ -14,5 +18,53 @@ namespace server.tests.unitTests _mockRepo = new Mock(); _controller = new CharactersController(_mockRepo.Object); } + + [Fact] + public async Task GetCharactersAsync_AllCharacters_Returns200StatusCodeWithCharactersCollection() + { + var filter = new CharacterFilter(); + + var characters = new List {new Character(), new Character()}; + + _mockRepo + .Setup(repo => repo.GetCharactersAsync(filter)) + .ReturnsAsync(characters); + + var response = await _controller.GetCharactersAsync(filter) as ObjectResult; + var data = response.Value as List; + + _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); + + response.Should().NotBeNull(); + response.Should().BeOfType(); + response.StatusCode.Should().Be((int)HttpStatusCode.OK); + + data.Should().NotBeNull(); + data.Should().BeOfType>(); + data.Should().HaveCount(2); + } + + [Fact] + public async Task GetCharactersAsync_RepoThrowsException_Returns500StatusCodeWithProblemDetails() + { + var filter = new CharacterFilter(); + + _mockRepo + .Setup(repo => repo.GetCharactersAsync(filter)) + .Throws(new Exception()); + + var response = await _controller.GetCharactersAsync(filter) as ObjectResult; + + var details = response.Value; + + _mockRepo.Verify(repo => repo.GetCharactersAsync(It.IsAny()), Times.Once()); + + response.Should().NotBeNull(); + response.Should().BeOfType(); + response.StatusCode.Should().Be((int)HttpStatusCode.InternalServerError); + + details.Should().NotBeNull(); + details.Should().BeOfType(); + } } }