diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9dd01c8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/GitHubActionsDemo.Api/bin/Debug/net7.0/GitHubActionsDemo.Api.dll", + "args": [], + "cwd": "${workspaceFolder}/src/GitHubActionsDemo.Api", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..879a25d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/GitHubActionsDemo.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/GitHubActionsDemo.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/GitHubActionsDemo.sln" + ], + "problemMatcher": "$msCompile" + }, + { + "label": ".NET Core Test with debugger", + "type": "process", + "isBackground": true, + "command": "dotnet", + "args": ["test"], + "options": { + "cwd": "${workspaceFolder}/tests", + "env": { + "VSTEST_HOST_DEBUG": "1" + } + }, + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": [] + } + ] +} diff --git a/GitHubActionsDemo.sln b/GitHubActionsDemo.sln index c245ed4..215bceb 100644 --- a/GitHubActionsDemo.sln +++ b/GitHubActionsDemo.sln @@ -11,6 +11,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubActionsDemo.Service", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubActionsDemo.Persistance", "src\GitHubActionsDemo.Persistance\GitHubActionsDemo.Persistance.csproj", "{339301B3-E9BD-45D1-B6A9-94F41367DF16}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{015E0E24-CD73-48C7-8565-5A566F5DAB87}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubActionsDemo.Api.Tests", "test\GitHubActionsDemo.Api.Tests\GitHubActionsDemo.Api.Tests.csproj", "{AA6299AC-C10C-49CD-89A3-33122123AE7A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,10 +36,15 @@ Global {339301B3-E9BD-45D1-B6A9-94F41367DF16}.Debug|Any CPU.Build.0 = Debug|Any CPU {339301B3-E9BD-45D1-B6A9-94F41367DF16}.Release|Any CPU.ActiveCfg = Release|Any CPU {339301B3-E9BD-45D1-B6A9-94F41367DF16}.Release|Any CPU.Build.0 = Release|Any CPU + {AA6299AC-C10C-49CD-89A3-33122123AE7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA6299AC-C10C-49CD-89A3-33122123AE7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA6299AC-C10C-49CD-89A3-33122123AE7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA6299AC-C10C-49CD-89A3-33122123AE7A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {E3C34F79-B74E-44A4-99F0-B0B3C2D7CCB2} = {53A44CCA-C5C6-4D98-91B8-D331D68BF7B0} {F6807FFB-3C0D-4F84-8803-8ECAFD72EF76} = {53A44CCA-C5C6-4D98-91B8-D331D68BF7B0} {339301B3-E9BD-45D1-B6A9-94F41367DF16} = {53A44CCA-C5C6-4D98-91B8-D331D68BF7B0} + {AA6299AC-C10C-49CD-89A3-33122123AE7A} = {015E0E24-CD73-48C7-8565-5A566F5DAB87} EndGlobalSection EndGlobal diff --git a/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs b/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs index 91f639a..0703906 100644 --- a/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs +++ b/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs @@ -1,4 +1,3 @@ -using System.Runtime.Intrinsics.X86; using GitHubActionsDemo.Api.Models; using GitHubActionsDemo.Api.Mappers; using GitHubActionsDemo.Service; diff --git a/test/GitHubActionsDemo.Api.Tests/AuthorsControllerTests.cs b/test/GitHubActionsDemo.Api.Tests/AuthorsControllerTests.cs new file mode 100644 index 0000000..81b956c --- /dev/null +++ b/test/GitHubActionsDemo.Api.Tests/AuthorsControllerTests.cs @@ -0,0 +1,80 @@ +using System.Net; +using FluentValidation; +using FluentValidation.Results; +using GitHubActionsDemo.Api.Controllers; +using GitHubActionsDemo.Api.Models; +using GitHubActionsDemo.Service; +using GitHubActionsDemo.Service.Models; +using Microsoft.AspNetCore.Http.HttpResults; +using Moq; +using Shouldly; + +namespace GitHubActionsDemo.Api.Tests; + +public class AuthorsControllerTests +{ + private readonly Mock _libraryService; + private readonly Mock> _authorValidator; + private readonly Mock> _pageValidator; + + private readonly AuthorsController _sut; + + public AuthorsControllerTests() + { + _libraryService = new Mock(); + _authorValidator = new Mock>(); + _pageValidator = new Mock>(); + _sut = new AuthorsController(_libraryService.Object, _authorValidator.Object, _pageValidator.Object); + } + + [Fact] + public async Task Given_invalid_request_should_return_validation_problem() + { + var request = new AuthorRequest + { + FirstName = "", + LastName = "" + }; + + var failures = new List + { + new ValidationFailure("FirstName", "Missing"), + new ValidationFailure("LastName", "Missing") + }; + + _authorValidator.Setup(x => x.ValidateAsync(request, It.IsAny())) + .ReturnsAsync(new ValidationResult(failures)); + + var result = await _sut.AddAuthorAsync(request); + var problem = result as ProblemHttpResult; + problem.ShouldNotBeNull(); + } + + [Fact] + public async Task Given_valid_request_should_return_author() + { + var request = new AuthorRequest + { + FirstName = "Joe", + LastName = "Bloggs" + }; + + var mockAuthor = new Author(1, request.FirstName, request.LastName, DateTime.UtcNow, DateTime.UtcNow); + + _authorValidator.Setup(x => x.ValidateAsync(request, It.IsAny())) + .ReturnsAsync(new ValidationResult()); + + _libraryService.Setup( + x => x.AddAuthorAsync(It.Is(a => + a.FirstName == request.FirstName && + a.LastName == request.LastName))) + .ReturnsAsync(new OneOf.Types.Success(mockAuthor)); + + var result = await _sut.AddAuthorAsync(request); + var successValue = result as Ok; + successValue.ShouldNotBeNull(); + successValue.StatusCode.ShouldBe((int)HttpStatusCode.OK); + successValue.Value.FirstName.ShouldBe(request.FirstName); + successValue.Value.LastName.ShouldBe(request.LastName); + } +} \ No newline at end of file diff --git a/test/GitHubActionsDemo.Api.Tests/GitHubActionsDemo.Api.Tests.csproj b/test/GitHubActionsDemo.Api.Tests/GitHubActionsDemo.Api.Tests.csproj new file mode 100644 index 0000000..7c04f8c --- /dev/null +++ b/test/GitHubActionsDemo.Api.Tests/GitHubActionsDemo.Api.Tests.csproj @@ -0,0 +1,31 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/GitHubActionsDemo.Api.Tests/Usings.cs b/test/GitHubActionsDemo.Api.Tests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/test/GitHubActionsDemo.Api.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file