Add unit tests
This commit is contained in:
179
tests/WebApi.Tests.Unit/Authentication/UserAccessorTests.cs
Normal file
179
tests/WebApi.Tests.Unit/Authentication/UserAccessorTests.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using System.Security.Claims;
|
||||
using Vegasco.WebApi.Authentication;
|
||||
|
||||
namespace WebApi.Tests.Unit.Authentication;
|
||||
public sealed class UserAccessorTests
|
||||
{
|
||||
private readonly UserAccessor _sut;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
private static readonly string _nameClaimType = "name";
|
||||
private readonly JwtOptions _jwtOptions = new()
|
||||
{
|
||||
NameClaimType = _nameClaimType
|
||||
};
|
||||
|
||||
private readonly IOptions<JwtOptions> _options = Substitute.For<IOptions<JwtOptions>>();
|
||||
|
||||
private static readonly string _defaultUsername = "username";
|
||||
private static readonly string _defaultId = "id";
|
||||
private readonly ClaimsPrincipal _defaultUser = new(new ClaimsIdentity(
|
||||
[
|
||||
new Claim(_nameClaimType, _defaultUsername),
|
||||
new Claim(ClaimTypes.NameIdentifier, _defaultId)
|
||||
]));
|
||||
|
||||
public UserAccessorTests()
|
||||
{
|
||||
_httpContextAccessor = new HttpContextAccessor
|
||||
{
|
||||
HttpContext = new DefaultHttpContext()
|
||||
{
|
||||
User = _defaultUser
|
||||
}
|
||||
};
|
||||
|
||||
_options.Value.Returns(_jwtOptions);
|
||||
|
||||
_sut = new UserAccessor(_httpContextAccessor, _options);
|
||||
}
|
||||
|
||||
#region GetUsername
|
||||
|
||||
[Fact]
|
||||
public void GetUsername_ShouldReturnUsername_WhenOptionsNameClaimTypeMatches()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = _sut.GetUsername();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(_defaultUsername);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUsername_ShouldReturnUsername_WhenNameClaimTypeIsNotSetAndUsernameIsInUriNameClaimType()
|
||||
{
|
||||
// Arrange
|
||||
_jwtOptions.NameClaimType = null;
|
||||
_httpContextAccessor.HttpContext!.User = new ClaimsPrincipal(new ClaimsIdentity(
|
||||
[
|
||||
new Claim(ClaimTypes.Name, _defaultUsername)
|
||||
]));
|
||||
|
||||
// Act
|
||||
var result = _sut.GetUsername();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(_defaultUsername);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUsername_ShouldCacheUsername_WhenFirstCalled()
|
||||
{
|
||||
// Arrange
|
||||
_ = _sut.GetUsername();
|
||||
_options.ClearReceivedCalls();
|
||||
|
||||
// Act
|
||||
var result = _sut.GetUsername();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(_defaultUsername);
|
||||
_ = _options.Received(0).Value;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUsername_ShouldThrowInvalidOperationException_WhenHttpContextIsNull()
|
||||
{
|
||||
// Arrange
|
||||
_httpContextAccessor.HttpContext = null;
|
||||
|
||||
// Act
|
||||
var action = () => _sut.GetUsername();
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<InvalidOperationException>()
|
||||
.Which.Message.Should().Be("No HttpContext available.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUsername_ShouldThrowInvalidOperationException_WhenNameClaimIsNotFound()
|
||||
{
|
||||
// Arrange
|
||||
_httpContextAccessor.HttpContext!.User = new ClaimsPrincipal();
|
||||
|
||||
// Act
|
||||
var action = () => _sut.GetUsername();
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<InvalidOperationException>()
|
||||
.Which.Message.Should().Be($"No claim of type '{_nameClaimType}' found on the current user.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetUserId
|
||||
|
||||
[Fact]
|
||||
public void GetUserId_ShouldReturnUserId_WhenUserIdClaimExists()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = _sut.GetUserId();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(_defaultId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUserId_ShouldCacheUserId_WhenFirstCalled()
|
||||
{
|
||||
// Arrange
|
||||
_ = _sut.GetUserId();
|
||||
_options.ClearReceivedCalls();
|
||||
|
||||
// Act
|
||||
var result = _sut.GetUserId();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(_defaultId);
|
||||
_ = _options.Received(0).Value;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUserId_ShouldThrowInvalidOperationException_WhenHttpContextIsNull()
|
||||
{
|
||||
// Arrange
|
||||
_httpContextAccessor.HttpContext = null;
|
||||
|
||||
// Act
|
||||
var action = () => _sut.GetUserId();
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<InvalidOperationException>()
|
||||
.Which.Message.Should().Be("No HttpContext available.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetUserId_ShouldThrowInvalidOperationException_WhenIdClaimIsNotFound()
|
||||
{
|
||||
// Arrange
|
||||
_httpContextAccessor.HttpContext!.User = new ClaimsPrincipal();
|
||||
|
||||
// Act
|
||||
var action = () => _sut.GetUserId();
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<InvalidOperationException>()
|
||||
.Which.Message.Should().Be($"No claim of type '{ClaimTypes.NameIdentifier}' found on the current user.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using FluentAssertions;
|
||||
using Vegasco.WebApi.Cars;
|
||||
|
||||
namespace WebApi.Tests.Unit.Cars;
|
||||
|
||||
public sealed class CreateCarRequestValidatorTests
|
||||
{
|
||||
private readonly CreateCar.Validator _sut = new();
|
||||
|
||||
private readonly CreateCar.Request _validRequest = new("Ford Focus");
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(_validRequest);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(50)]
|
||||
public async Task ValidateAsync_ShouldBeValid_WhenNameIsJustWithinTheLimits(int nameLength)
|
||||
{
|
||||
// Arrange
|
||||
var request = _validRequest with { Name = new string('s', nameLength) };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var request = _validRequest with { Name = "" };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().ContainSingle()
|
||||
.Which
|
||||
.PropertyName.Should().Be(nameof(CreateCar.Request.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsTooLong()
|
||||
{
|
||||
// Arrange
|
||||
const int nameMaxLength = 50;
|
||||
var request = _validRequest with { Name = new string('s', nameMaxLength + 1) };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().ContainSingle()
|
||||
.Which
|
||||
.PropertyName.Should().Be(nameof(CreateCar.Request.Name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using FluentAssertions;
|
||||
using Vegasco.WebApi.Cars;
|
||||
|
||||
namespace WebApi.Tests.Unit.Cars;
|
||||
|
||||
public sealed class UpdateCarRequestValidatorTests
|
||||
{
|
||||
private readonly UpdateCar.Validator _sut = new();
|
||||
|
||||
private readonly UpdateCar.Request _validRequest = new("Ford Focus");
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(_validRequest);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(50)]
|
||||
public async Task ValidateAsync_ShouldBeValid_WhenNameIsJustWithinTheLimits(int nameLength)
|
||||
{
|
||||
// Arrange
|
||||
var request = _validRequest with { Name = new string('s', nameLength) };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var request = _validRequest with { Name = "" };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().ContainSingle()
|
||||
.Which
|
||||
.PropertyName.Should().Be(nameof(UpdateCar.Request.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsTooLong()
|
||||
{
|
||||
// Arrange
|
||||
const int nameMaxLength = 50;
|
||||
var request = _validRequest with { Name = new string('s', nameMaxLength + 1) };
|
||||
|
||||
// Act
|
||||
var result = await _sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().ContainSingle()
|
||||
.Which
|
||||
.PropertyName.Should().Be(nameof(UpdateCar.Request.Name));
|
||||
}
|
||||
}
|
||||
35
tests/WebApi.Tests.Unit/WebApi.Tests.Unit.csproj
Normal file
35
tests/WebApi.Tests.Unit/WebApi.Tests.Unit.csproj
Normal file
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WebApi\WebApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user