Skip to content

Commit

Permalink
Add API Tests (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
vanderlan authored Nov 15, 2023
1 parent 549be9e commit 85b4c40
Show file tree
Hide file tree
Showing 51 changed files with 722 additions and 349 deletions.
41 changes: 19 additions & 22 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ on:
- master
pull_request:
types: [opened, synchronize, reopened]
env:
ASPNETCORE_ENVIRONMENT: Test
jobs:
build:
name: Build
runs-on: windows-latest
runs-on: ubuntu-latest
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2019-latest
ports:
- 1433:1433
env:
ACCEPT_EULA: Y
SA_PASSWORD: SqlServer2019!
steps:
- name: Set up JDK 17
uses: actions/setup-java@v3
Expand All @@ -18,33 +28,20 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Cache SonarCloud packages
uses: actions/cache@v1
with:
path: ~\sonar\cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache SonarCloud scanner
id: cache-sonar-scanner
uses: actions/cache@v1
with:
path: .\.sonar\scanner
key: ${{ runner.os }}-sonar-scanner
restore-keys: ${{ runner.os }}-sonar-scanner
- name: Install SonarCloud scanner
if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
shell: powershell
run: |
New-Item -Path .\.sonar\scanner -ItemType Directory
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
dotnet tool install --global dotnet-sonarscanner --version 5.14.0
- name: Install EF Core Tools
run: |
dotnet tool install --global dotnet-ef --version 7.0.14
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
shell: powershell
run: |
.\.sonar\scanner\dotnet-sonarscanner begin /k:"vanderlan_Orion-Api" /o:"vanderlan-gomes" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="opencover.xml"
dotnet ef database update -p src/Orion.Infra.Data -s src/Orion.Api --verbose
dotnet sonarscanner begin /k:"vanderlan_Orion-Api" /o:"vanderlan-gomes" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="opencover.xml"
dotnet build Orion.sln
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=coverage /p:Exclude=[xunit.*]* Orion.sln
cp tests\Orion.Test\coverage.opencover.xml opencover.xml
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
cp tests/Orion.Test/coverage.opencover.xml opencover.xml
dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,4 @@ __pycache__/
/VBaseProject.Test/coverage.json
/.editorconfig
/.sonarqube
/tests/Orion.Test/coverage
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# **Orion Api**

[![Build](https://github.com/vanderlan/Orion-Api/actions/workflows/sonar.yml/badge.svg)](https://github.com/vanderlan/Orion-Api/actions/workflows/sonar.yml)
[![Coverage Status](https://coveralls.io/repos/github/vanderlan/Orion-Api/badge.svg)](https://coveralls.io/github/vanderlan/Orion-Api) <a href="https://codeclimate.com/github/vanderlan/Orion-Api/maintainability"><img src="https://api.codeclimate.com/v1/badges/76a30970ddd45c75129b/maintainability" /></a>
[![GitHub release](https://img.shields.io/github/release/vanderlan/Orion-Api.svg)](https://GitHub.com/vanderlan/Orion-Api/)
[![GitHub repo size](https://img.shields.io/github/repo-size/vanderlan/Orion-Api)](https://github.com/vanderlan/Orion-Api)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=vanderlan_Orion-Api&metric=coverage)](https://sonarcloud.io/summary/new_code?id=vanderlan_Orion-Api)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vanderlan_Orion-Api&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vanderlan_Orion-Api)
[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=vanderlan_Orion-Api&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=vanderlan_Orion-Api)
[![Manteinability](https://api.codeclimate.com/v1/badges/76a30970ddd45c75129b/maintainability)](https://codeclimate.com/github/vanderlan/Orion-Api/maintainability)
[![GitHub release](https://img.shields.io/github/release/vanderlan/Orion-Api.svg)](https://github.com/vanderlan/Orion-Api/releases)

**About this Project**

Expand Down Expand Up @@ -50,7 +52,7 @@ The main objective is to start projects with a clean and simple architecture, wi

**CI & CD**

1. Automated tests;
1. Unit, Integration and Api Tests;
2. Continuous Integration (GitHub CI);
3. Continuous Delivery (GitHub CI);
4. DockerHub Integration;
Expand All @@ -72,4 +74,4 @@ The main objective is to start projects with a clean and simple architecture, wi
dotnet ef migrations add MigrationName -p Orion.Infra.Data -s Orion.Api
dotnet ef database update -p Orion.Infra.Data -s Orion.Api --verbose

Author: https://github.com/vanderlan
*author:* https://github.com/vanderlan
14 changes: 14 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ services:
ports:
- "5010:80"
container_name: orion-api
depends_on:
- sqlserver
networks:
- backend-network

sqlserver:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
SA_PASSWORD: "SqlServer2019!"
ACCEPT_EULA: "Y"
MSSQL_PID: "Developer"
ports:
- "1434:1433"
container_name: sqlserver
networks:
- backend-network

Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Api/Configuration/HealthCheckConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static void ConfigureHealthCheck(this IApplicationBuilder app)

public static void AddApplicationHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks().AddSqlServer(configuration["DatabaseOptions:ConnectionString"],
services.AddHealthChecks().AddSqlServer(configuration["ConnectionStrings:OrionDatabase"],
tags: new[]
{
"database"
Expand Down
13 changes: 13 additions & 0 deletions src/Orion.Api/Controllers/V1/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Mvc;
using Orion.Api.Attributes;
using Orion.Api.Controllers.Base;
using Orion.Application.Core.Commands.UserChangePassword;
using Orion.Application.Core.Commands.UserCreate;
using Orion.Application.Core.Commands.UserDelete;
using Orion.Application.Core.Commands.UserUpdate;
Expand Down Expand Up @@ -83,4 +84,16 @@ public async Task<IActionResult> Delete(string id)

return NoContent();
}

[HttpPatch("Me/PasswordChange")]
[SwaggerResponse((int)HttpStatusCode.BadRequest, "A error response with the error description", typeof(ExceptionResponse))]
[SwaggerResponse((int)HttpStatusCode.Accepted)]
[SwaggerResponse((int)HttpStatusCode.NotFound)]
[SwaggerResponse((int)HttpStatusCode.Unauthorized)]
public async Task<IActionResult> PatchChangePassword([FromBody] UserChangePasswordRequest userChangePasswordRequest)
{
await Mediator.Send(userChangePasswordRequest);

return Accepted();
}
}
7 changes: 0 additions & 7 deletions src/Orion.Api/Models/RefreshTokenModel.cs

This file was deleted.

13 changes: 0 additions & 13 deletions src/Orion.Api/Models/UserLoginModel.cs

This file was deleted.

6 changes: 1 addition & 5 deletions src/Orion.Api/Orion.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="7.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0" />
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.12">
Expand All @@ -26,7 +22,7 @@
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.3.0" />
Expand Down
6 changes: 2 additions & 4 deletions src/Orion.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
"Microsoft": "Information"
}
},

"DatabaseOptions": {
"ConnectionString": "Data Source=10.0.0.90,1433;Initial Catalog=Orion;User ID=sa;Password=123;TrustServerCertificate=True"
"ConnectionStrings": {
"OrionDatabase": "Data Source=localhost,1433;Initial Catalog=Orion;User ID=Orion;Password=123;TrustServerCertificate=True"
},

"JwtOptions": {
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
"Issuer": "Orion API",
Expand Down
6 changes: 2 additions & 4 deletions src/Orion.Api/appsettings.Production.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
"Microsoft": "Information"
}
},

"DatabaseOptions": {
"ConnectionString": "Data Source=10.0.0.90,1433;Initial Catalog=Orion;User ID=sa;Password=123;TrustServerCertificate=True"
"ConnectionStrings": {
"OrionDatabase": "Data Source=localhost,1433;Initial Catalog=Orion;User ID=Orion;Password=123;TrustServerCertificate=True"
},

"JwtOptions": {
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
"Issuer": "Orion API",
Expand Down
8 changes: 3 additions & 5 deletions src/Orion.Api/appsettings.Test.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
"Microsoft": "Information"
}
},

"DatabaseOptions": {
"ConnectionString": "Data Source=localhost,1433;Initial Catalog=Orion;User ID=sa;Password=123;TrustServerCertificate=True"
"ConnectionStrings": {
"OrionDatabase": "Data Source=localhost,1433;Initial Catalog=Orion;User ID=sa;Password=SqlServer2019!;TrustServerCertificate=True"
},

"JwtOptions": {
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
"Issuer": "Orion API",
Expand All @@ -20,4 +18,4 @@
"ElasticConfiguration": {
"Uri": "http://localhost:9200"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using MediatR;

namespace Orion.Application.Core.Commands.UserChangePassword
{
public class UserChangePasswordRequest : IRequest<Unit>
{
public string CurrentPassword { get; set; }
public string NewPassword { get; set; }
public string NewPasswordConfirm { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using MediatR;
using Orion.Application.Core.Notifications.UserPasswordChanged;
using Orion.Domain.Core.Authentication;
using Orion.Domain.Core.Services.Interfaces;

namespace Orion.Application.Core.Commands.UserChangePassword
{
public class UserUpdatePasswordRequestHandler : IRequestHandler<UserChangePasswordRequest, Unit>
{
private readonly IUserService _userService;
private readonly ICurrentUser _currentUser;
private readonly IMediator _mediator;

public UserUpdatePasswordRequestHandler(IUserService userService, ICurrentUser currentUser, IMediator mediator)
{
_userService = userService;
_currentUser = currentUser;
_mediator = mediator;
}

public async Task<Unit> Handle(UserChangePasswordRequest request, CancellationToken cancellationToken)
{
await _userService.ChangePasswordAsync(_currentUser.Id, request.CurrentPassword, request.NewPassword);

await _mediator.Publish(new UserPasswordChangedNotification(), cancellationToken);

return Unit.Value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using FluentValidation;
using Microsoft.Extensions.Localization;
using Orion.Croscutting.Resources;
using static Orion.Croscutting.Resources.Messages.MessagesKeys;

namespace Orion.Application.Core.Commands.UserChangePassword;

public class UserChangePasswordRequestValidator : AbstractValidator<UserChangePasswordRequest>
{
public UserChangePasswordRequestValidator(IStringLocalizer<OrionResources> stringLocalizer)
{

RuleFor(c => c.CurrentPassword)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyPasword]);

RuleFor(c => c.NewPassword)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyNewPasword]);

RuleFor(c => c.NewPasswordConfirm)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyNewPaswordConfirmation]);

RuleFor(x => x.NewPassword).Equal(x => x.NewPasswordConfirm)
.WithMessage(stringLocalizer[UserMessages.PaswordAndConfirmationDifferent]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ public UserCreateRequestValidator(IStringLocalizer<OrionResources> stringLocaliz
.WithMessage(stringLocalizer[UserMessages.NullEntity]);

RuleFor(c => c.Name)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyName])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyName]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyName]);

RuleFor(c => c.Email)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyEmail])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyEmail]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyEmail]);

RuleFor(c => c.Password)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyPasword])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyPasword]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyPasword]);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ public UserUpdateRequestValidator(IStringLocalizer<OrionResources> stringLocaliz
.WithMessage(stringLocalizer[UserMessages.NullEntity]);

RuleFor(c => c.Name)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyName])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyName]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyName]);

RuleFor(c => c.Email)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyEmail])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyEmail]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyEmail]);

RuleFor(c => c.PublicId)
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyId])
.NotNull().WithMessage(stringLocalizer[UserMessages.EmptyId]);
.NotEmpty().WithMessage(stringLocalizer[UserMessages.EmptyId]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using MediatR;

namespace Orion.Application.Core.Notifications.UserPasswordChanged
{
public class UserPasswordChangedNotification : INotification
{

}
}
Loading

0 comments on commit 85b4c40

Please sign in to comment.